最近項(xiàng)目在搞音頻的小窗口播放(不是類似于音樂播放器的那種高端大氣上檔次的貨),經(jīng)過一系列的掙扎和努力算是搞出和設(shè)計(jì)師要求的效果了
效果如下(不要在意圖片的名字)
就是這么個(gè)小東西,在app內(nèi)外來回跑的那種(原諒我不能用gift,不知道公司讓不讓,相信這么淺白的東西,應(yīng)該都能懂吧...)
看也看了,那就開擼吧
1,寫一個(gè)小窗口的布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/ll_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<LinearLayout
android:id="@+id/ll_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/float_window_back"http://.9 圖片
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="10dp"
android:paddingRight="10dp">
<FrameLayout
android:layout_width="40dp"
android:layout_height="40dp"
>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:scaleType="centerCrop"
android:src="@drawable/float_window_img"/>
<ProgressBar
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_gravity="center"
android:indeterminateDrawable="@drawable/float_window_anim"/>//動(dòng)畫
</FrameLayout>
<TextView
android:id="@+id/text_room_id"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginLeft="8dp"
android:layout_weight="1"
android:ellipsize="end"
android:gravity="center"
android:singleLine="true"
android:textColor="#E6E6E6"
android:textSize="14sp"/>
<ImageButton
android:id="@+id/bt_close"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="@color/transparent"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:src="@drawable/float_window_close"
/>
</LinearLayout>
</LinearLayout>
float_window_anim
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false" >
<item android:drawable="@drawable/float_window_animate_01" android:duration="300"></item>
<item android:drawable="@drawable/float_window_animate_02" android:duration="300"></item>
<item android:drawable="@drawable/float_window_animate_03" android:duration="300"></item>
</animation-list>
2,擼一個(gè)小窗口的自定義View
public class FloatWindowView extends LinearLayout implements View.OnClickListener {
private final TextView text_room_id;
public final ImageButton bt_close;
private WindowManager mManager;
private Context mContext;
private WindowManager.LayoutParams mParams;
public FloatWindowView(Context context) {
super(context);
mContext = context;
mManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
LayoutInflater.from(context).inflate(R.layout.dialog_video_play, this);
text_room_id = (TextView) findViewById(R.id.text_room_id);
bt_close = (ImageButton) findViewById(R.id.bt_close);
bt_close.setOnClickListener(this);
}
//外部傳入LayoutParams
public void setParams(WindowManager.LayoutParams params) {
this.mParams = params;
}
int lastX, lastY;
int paramX, paramY;
boolean isMove;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = (int) event.getRawX();
lastY = (int) event.getRawY();
paramX = mParams.x;
paramY = mParams.y;
break;
case MotionEvent.ACTION_MOVE:
int dx = (int) event.getRawX() - lastX;
int dy = (int) event.getRawY() - lastY;
if (Math.abs(dx) >= 50 || Math.abs(dy) >= 50) {
isMove = true;
}
mParams.x = paramX + dx;
mParams.y = paramY + dy;
mManager.updateViewLayout(this, mParams);
break;
case MotionEvent.ACTION_UP:
if (!isMove) {
int x = (int) event.getRawX() - lastX;
int y = (int) event.getRawY() - lastY;
if (Math.abs(x) <= 50 || Math.abs(y) <= 50) {
//模擬點(diǎn)擊事件
doClick();
}
}
isMove = false;
break;
}
return true;
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt_close:
//關(guān)閉窗口,服務(wù)之類的東東
break;
}
}
}
這里有個(gè)插曲,我們不能給整個(gè)小窗口的根布局設(shè)置點(diǎn)擊事件,這樣的話 mManager.updateViewLayout(this, mParams);更新小窗口位置的事件就會(huì)被攔截,導(dǎo)致小窗口不能跟著手指滑動(dòng),因此用了個(gè)粗鄙的辦法模擬點(diǎn)擊事件了
```基于我們項(xiàng)目的實(shí)現(xiàn)方式,我就直接說我們的方式了
3,在Application中創(chuàng)建,顯示并記錄小窗口的顯示狀態(tài)
//顯示狀態(tài)表示
public boolean inWindowShowing = false;
private FloatWindowView mFloatWindowView;
public void createFloatView(Context context) {
if (null == mFloatWindowView) {
mFloatWindowView = new FloatWindowView(context);
//獲取窗體服務(wù)
WindowManager wm = (WindowManager) getSystemService(
Context.WINDOW_SERVICE);
//創(chuàng)建一個(gè)窗體布局參數(shù)
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
//保證背景無色
params.format = PixelFormat.TRANSPARENT;
//設(shè)置窗體初始化的位置(這個(gè)很糾結(jié),下文再講)
params.gravity = Gravity.CENTER_VERTICAL;
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
params.width =WindowManager.LayoutParams.WRAP_CONTENT;
params.height =WindowManager.LayoutParams.WRAP_CONTENT;
//x坐標(biāo)的位置(這個(gè)是在gravity的基礎(chǔ)上的)
params.x= wm.getDefaultDisplay().getWidth()/2;
//y坐標(biāo)位置
params.y=wm.getDefaultDisplay().getHeight()/2- UIUtils.dip2px(100);
//這個(gè)標(biāo)識(shí)的優(yōu)先級(jí)低于下拉通知欄
params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
//這個(gè)就高于了 不推薦使用
// params.type = WindowManager.LayoutParams.TYPE_PHONE;
/*
* 下面的flags屬性的效果形同“鎖定”约巷。 懸浮窗不可觸摸姓蜂,不接受任何事件,同時(shí)不影響后面的事件響應(yīng)。
* wmParams.flags=LayoutParams.FLAG_NOT_TOUCH_MODAL |
* LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCHABLE;
*/
//先給浮窗view設(shè)置參數(shù),方便滑動(dòng)時(shí)動(dòng)態(tài)改變小窗口位置
mFloatWindowView.setParams(params);
//添加到窗體上
wm.addView(mFloatWindowView, params);
//顯示標(biāo)識(shí)
inWindowShowing = true;
}
}
//這個(gè)很簡(jiǎn)單,從窗體上移除
public void reMoveFloatView() {
if (null != mFloatWindowView) {
WindowManager wm = (WindowManager) getSystemService(
Context.WINDOW_SERVICE);
wm.removeView(mFloatWindowView);
mFloatWindowView = null;
inWindowShowing = false;
}
}
#然而然而然而,這個(gè)小窗口的初始化位置可把我整的夠嗆,只要把gravity設(shè)置成TOP和LEFT,
#好的可以就是在屏幕的左上角了(當(dāng)然設(shè)置成CENTER就會(huì)居中),但是呢 我們的設(shè)計(jì)說要在界面的右下角
#并且距離底部導(dǎo)航欄高個(gè)20px,那么問題就來了 我把gravity設(shè)置成RIGHT|BOTTOM不就行了?NONONO,
#TOO羊TOO新坡,設(shè)置成Bottom 只能在底部移動(dòng)了就, 設(shè)置成Right和Bottom那么就直接不能動(dòng)了,
#所以才有了設(shè)置成CENTER_VERTICAL,這個(gè)位置的的X和Y都是相對(duì)于屏幕的中間的,
#所以讓浮窗顯示在右邊就把X設(shè)置成屏幕的一半,下邊就把Y設(shè)置成高度的一半加上導(dǎo)航欄的高度再加個(gè)20px,好吧 位置終于OK 了
這樣的話就差不多實(shí)現(xiàn)了小窗口了
關(guān)于那些個(gè)FLOG的什么意思的直接看這個(gè) 傳送門--TP
到此我們可以安安靜靜的寫寫文章泡泡X了,可是.....................
TMD的一些國(guó)產(chǎn)ROM默認(rèn)是把浮窗的權(quán)限是關(guān)著的...........我曹.....
不過不要慌 還好前輩們幫我們解決了這個(gè)問題(至少90%吧)
4,浮窗權(quán)限的適配
具體做法傳送門--TP
再次向前輩們致敬...........
這個(gè)實(shí)現(xiàn)方式目前是有缺陷的,最好能在service中創(chuàng)建,這樣不容易被系統(tǒng)回收,反正問題還是有得,歡迎大家在評(píng)論區(qū)點(diǎn)評(píng)!