Android 懸浮窗、懸浮球開發
阿新 • • 發佈:2018-10-26
focus show 權限 代碼 .com creates his activit exce 原文:Android 懸浮窗、懸浮球開發
1、權限管理
直接看我另外一篇博客吧,傳送門:
https://my.oschina.net/u/1462828/blog/1933162
2、Base類BaseSuspend
import android.content.Context; import android.graphics.PixelFormat; import android.os.Build; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; import com.imxiaoyu.common.utils.entity.SizeEntity; public abstract class BaseSuspend { private Context context; private View view; private boolean isShowing = false; /** * UI */ private WindowManager.LayoutParams wmParams;//懸浮窗的布局 /** * 變量 */ private WindowManager mWindowManager;//創建浮動窗口設置布局參數的對象 /** * 接口 */ private OnSuspendDismissListener onSuspendDismissListener; public BaseSuspend(Context context) { this.context = context; view = LayoutInflater.from(context).inflate(getLayoutId(), null); init(); initView(); onCreateSuspension(); } public void init() { if (mWindowManager == null) { mWindowManager = (WindowManager) context.getApplicationContext().getSystemService(Context.WINDOW_SERVICE); } wmParams = getParams();//設置好懸浮窗的參數 // 懸浮窗默認顯示以左上角為起始坐標 wmParams.gravity = Gravity.LEFT | Gravity.TOP; } /** * 布局文件id,這裏是用不到的,但還是建議填寫,方便跳轉到布局管理 * * @return */ protected abstract int getLayoutId(); /** * 註冊需要使用的控件 */ protected abstract void initView(); protected abstract void onCreateSuspension(); /** * 根據id快速找到控件 * * @param id * @param <E> * @return */ public final <E extends View> E findView(int id) { try { return (E) view.findViewById(id); } catch (ClassCastException ex) { throw ex; } } /** * 根據id快速找到控件 * * @param id * @param onClickListener * @param <E> * @return */ public final <E extends View> E findView(int id, View.OnClickListener onClickListener) { E e = findView(id); e.setOnClickListener(onClickListener); return e; } /** * 對windowManager進行設置 * * @return */ public WindowManager.LayoutParams getParams() { wmParams = new WindowManager.LayoutParams(); //設置window type 下面變量2002是在屏幕區域顯示,2003則可以顯示在狀態欄之上 //wmParams.type = LayoutParams.TYPE_PHONE; if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { wmParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; } else { wmParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } // wmParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; //設置圖片格式,效果為背景透明 wmParams.format = PixelFormat.RGBA_8888; //設置浮動窗口不可聚焦(實現操作除浮動窗口外的其他可見窗口的操作) //wmParams.flags = LayoutParams.FLAG_NOT_FOCUSABLE; //設置可以顯示在狀態欄上 wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; return wmParams; } /** * 全屏顯示懸浮視圖 */ public void showSuspend() { showSuspend(0, 0, true); } /** * 顯示懸浮視圖 * * @param sizeEntity * @param isMatchParent 是否全屏顯示 */ public void showSuspend(SizeEntity sizeEntity, boolean isMatchParent) { if (sizeEntity != null) { showSuspend(sizeEntity.getWidth(), sizeEntity.getHeight(), isMatchParent); } } /** * 顯示懸浮視圖 * * @param width * @param height */ public void showSuspend(int width, int height, boolean isMatchParent) { //設置懸浮窗口長寬數據 if (isMatchParent) { wmParams.width = WindowManager.LayoutParams.MATCH_PARENT; wmParams.height = WindowManager.LayoutParams.MATCH_PARENT; } else { wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT; wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT; } //懸浮窗的開始位置,讀取緩存 wmParams.x = width; wmParams.y = height; if (isShowing) { removeView(); } mWindowManager.addView(view, wmParams); isShowing = true; } /** * 更新當前視圖的位置 * * @param x 更新後的X軸的增量 * @param y 更新後的Y軸的增量 */ public void updateSuspend(int x, int y) { if (view != null) { //必須是當前顯示的視圖才給更新 WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) view.getLayoutParams(); layoutParams.x += x; layoutParams.y += y; mWindowManager.updateViewLayout(view, layoutParams); } } /** * 移除當前懸浮窗 */ public void dismissSuspend() { if (view != null) { mWindowManager.removeView(view); isShowing = false; if (onSuspendDismissListener != null) { onSuspendDismissListener.onDismiss(); } } } public Context getContext() { return context; } public View getView() { return view; } /** * 是否正在顯示 * * @return */ public boolean isShowing() { return isShowing; } /** * 移除彈窗的時候回調 * * @param onSuspendDismissListener */ public void setOnSuspendDismissListener(OnSuspendDismissListener onSuspendDismissListener) { this.onSuspendDismissListener = onSuspendDismissListener; } public interface OnSuspendDismissListener { public void onDismiss(); } }
還有裏面用到的一個size類:
/** * 寬高實體 * Created by 她叫我小渝 on 2016/11/4. */ public class SizeEntity { private int width; private int height; public SizeEntity(){} public SizeEntity(int width,int height){ setWidth(width); setHeight(height); } public int getWidth() { return width; } public void setWidth(int width) { this.width = width; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } }
3、定制視圖和使用
要實現的邏輯是,顯示一個懸浮球,然後可以拖動移動懸浮球的位置,效果圖:
? ? ? ? ??
然後新建一個類,LogoSuspend繼承BaseSuspend,裏面引用到了一些工具類就不貼出來了,用到的地方我會加上註釋
/** * 懸浮球 * Created by 她叫我小渝 on 2017/1/1. */ public class LogoSuspend extends BaseSuspend { /** * ui */ private ImageView ivLogo; /** * 變量 */ private int width, height; private float mStartX, mStartY, mStopX, mStopY, touchStartX, touchStartY; private long touchStartTime; /** * 接口 */ private View.OnClickListener onClickListener; public LogoSuspend(Context context) { super(context); } @Override protected int getLayoutId() { return R.layout.suspend_logo; } @Override protected void initView() { ivLogo = findView(R.id.iv_logo); ivLogo.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent event) { final int action = event.getAction(); mStopX = event.getRawX(); mStopY = event.getRawY(); switch (action) { case MotionEvent.ACTION_DOWN: // 以當前父視圖左上角為原點 mStartX = event.getRawX(); mStartY = event.getRawY(); touchStartX = event.getRawX(); touchStartY = event.getRawY(); touchStartTime = DateUtil.getTimeForLong();//獲取當前時間戳 break; case MotionEvent.ACTION_MOVE: width = (int) (mStopX - mStartX); height = (int) (mStopY - mStartY); mStartX = mStopX; mStartY = mStopY; updateSuspend(width, height); break; case MotionEvent.ACTION_UP: width = (int) (mStopX - mStartX); height = (int) (mStopY - mStartY); updateSuspend(width, height); WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) getView().getLayoutParams(); SuspensionCache.setSuspendSize(getContext(), new SizeEntity(layoutParams.x + width, layoutParams.y + height));//緩存一下當前位置 if ((mStopX - touchStartX) < 30 && (mStartY - touchStartY) < 30 && (DateUtil.getTimeForLong() - touchStartTime) < 300) { //左右上下移動距離不超過30的,並且按下和擡起時間少於300毫秒,算是單擊事件,進行回調 if (onClickListener != null) { onClickListener.onClick(view); } } break; } return true; } }); } @Override protected void onCreateSuspension() { } /** * 設置點擊監聽 * * @param onClickListener */ public void setOnClickListener(View.OnClickListener onClickListener) { this.onClickListener = onClickListener; } }
布局文件syspend_logo.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rly_bg"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_logo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_home_add_normal" />
</RelativeLayout>
因為Activity是有生命周期的,所以打開懸浮窗的Context上下文,不要用Activity的,而是用Service的
創建並註冊一個Service,然後在onCreate方法中執行調用代碼就好
@Override
public void onCreate() {
super.onCreate();
ALog.e("服務已創建");
if (logoSuspend == null) {
logoSuspend = new LogoSuspend(this);
}
logoSuspend.showSuspend(SuspensionCache.getSuspendSize(this), false);//從緩存中提取上一次顯示的位置
logoSuspend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//處理單擊事件
}
});
}
4、廢話
????上面的例子,其實還是比較簡單的,但一般開發對於懸浮球的需求並不算很大,Base類的話,目前只是最基礎的東西,在開發的過程中,需要用到什麽了再往裏面加就好,問題不大。
????目前代碼支持同時顯示多個懸浮窗、懸浮球,主要用於在於懸浮窗交互的時候,直接彈出其他的交互界面(也是以懸浮窗的狀態出現),但建議每一個頁面都有關閉按鈕或者做返回鍵關閉的相關操作,畢竟是顯示在最前端的,要是關不掉就點哪裏都沒用,只能是強制關機了…………()&@()……()@#*¥)()@*#…………@#)()*¥)()…………
????
Android 懸浮窗、懸浮球開發