Android 可拖動可點擊懸浮窗

Android 懸浮窗在5.0以上,特別是小米手機,魅族手機,就算給到了
2
</uses-permission>這個權限也是不會顯示出來的,還需要在設置把懸浮權限開關給打開


<uses-permission android:name="android.permission.WRITE_SETTINGS"/>


最近公司項目要用到一個應用內的懸浮框,需要在當前的這個應用每個頁面都顯示出來,並且懸浮框可隨手指移動,但是停止時,要靠邊,可以點擊,其實這些問題都不是大問題,就是Android5.0,6.0這些機Android類型,又或者不同機型,權限問題的解決掉,啥也不說了,直接看代碼吧!



## 1.自定義懸浮View


    package com.amei.floatwindow.view;


    import android.content.Context;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.WindowManager;
    import android.widget.ImageView;
    import android.widget.Toast;


    import com.amei.floatwindow.application.MyApplication;


    /**
     * Created by kitchee on 2017/1/10.
     * Description:全局懸浮窗口
     */


    public class FloatScanView extends ImageView {




    private final int statusHeight;
    int sW;
    int sH;
    private float mTouchStartX;
    private float mTouchStartY;
    private float x;
    private float y;
    private boolean isMove=false;
    private Context context;


    private WindowManager wm = (WindowManager) getContext().getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
    //此wmParams變量爲獲取的全局變量,用以保存懸浮窗口的屬性
    private WindowManager.LayoutParams wmParams = ((MyApplication) getContext().getApplicationContext()).getMywmParams();
    private float mLastX;
    private float mLastY;
    private float mStartX;
    private float mStartY;
    private long mLastTime ;
    private long mCurrentTime;




    public FloatScanView(Context context) {
        this(context,null);
        this.context = context;
    }


    public FloatScanView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }


    public FloatScanView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        sW = wm.getDefaultDisplay().getWidth();
        sH = wm.getDefaultDisplay().getHeight();
        statusHeight = getStatusHeight(context);


    }


    /**
     * 獲得狀態欄的高度
     *
     * @param context
     * @return
     */
    public static int getStatusHeight(Context context) {


        int statusHeight = -1;
        try {
            Class clazz = Class.forName("com.android.internal.R$dimen");
            Object object = clazz.newInstance();
            int height = Integer.parseInt(clazz.getField("status_bar_height")
                    .get(object).toString());
            statusHeight = context.getResources().getDimensionPixelSize(height);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return statusHeight;
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //獲取相對屏幕的座標,即以屏幕左上角爲原點
        x = event.getRawX();
        y = event.getRawY() - statusHeight;   //statusHeight是系統狀態欄的高度
        Log.i("currP", "currX" + x + "====currY" + y);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:    //捕獲手指觸摸按下動作
                //獲取相對View的座標,即以此View左上角爲原點
                mTouchStartX = event.getX();
                mTouchStartY = event.getY();
                mStartX = event.getRawX();
                mStartY = event.getRawY();
                mLastTime = System.currentTimeMillis();
                Log.i("startP", "startX" + mTouchStartX + "====startY" + mTouchStartY);
                isMove = false;
                break;


            case MotionEvent.ACTION_MOVE:   //捕獲手指觸摸移動動作
                updateViewPosition();
                isMove = true;
                break;


            case MotionEvent.ACTION_UP:    //捕獲手指觸摸離開動作
                mLastX = event.getRawX();
                mLastY = event.getRawY();


                // 擡起手指時讓floatView緊貼屏幕左右邊緣
                wmParams.x = wmParams.x <= (sW / 2) ? 0 : sW;
                wmParams.y = (int) (y - mTouchStartY);
                wm.updateViewLayout(this, wmParams);




                mCurrentTime = System.currentTimeMillis();
                if(mCurrentTime - mLastTime < 800){
                    if(Math.abs(mStartX- mLastX )< 10.0 && Math.abs(mStartY - mLastY) < 10.0){
                        //處理點擊的事件
                        Toast.makeText(context,"可以處理點擊事件",Toast.LENGTH_SHORT).show();
                    }
                }


                break;
        }
        return true;
    }




    private void updateViewPosition() {
        //更新浮動窗口位置參數
        wmParams.x = (int) (x - mTouchStartX);
        wmParams.y = (int) (y - mTouchStartY);
        wm.updateViewLayout(this, wmParams);  //刷新顯示


    }


    }
    
## 2.定義一個MyApplication


    在裏面進行WindowManager的初始化




    public class MyApplication extends Application {


    private WindowManager.LayoutParams wmParams=new WindowManager.LayoutParams();


    public WindowManager.LayoutParams getMywmParams(){
        return wmParams;
    }


    @Override
    public void onCreate() {
        super.onCreate();


    }
    }


## 3.最後看看主頁MainActivity
    
    public class MainActivity extends AppCompatActivity {


    private WindowManager wm = null;
    private WindowManager.LayoutParams wmParams = null;
    private FloatScanView fsv = null;


    private AlertDialog dialog;




    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        dialog = new AlertDialog.Builder(this)
                .setTitle("懸浮窗權限管理")
                .setMessage("是否去開啓懸浮窗權限?")
                .setPositiveButton("是", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        //打開權限設置
                        openSetting();
                    }
                })
                .setNegativeButton("否",null)
                .create();


        //創建懸浮框
        createFloatView();


        }


    private void createFloatView() {


        //開啓懸浮窗前先請求權限
        if ("Xiaomi".equals(Build.MANUFACTURER)) {//小米手機
    //            LogUtil.E("小米手機");
            requestPermission();
        } else if ("Meizu".equals(Build.MANUFACTURER)) {//魅族手機
    //            LogUtil.E("魅族手機");
            requestPermission();
        } else {//其他手機
    //            LogUtil.E("其他手機");
            if (Build.VERSION.SDK_INT >= 23) {
                if (!Settings.canDrawOverlays(this)) {
                    Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
                    startActivityForResult(intent, 12);
                } else {
    //
                }
            } else {


            }
        }


        wm=(WindowManager)getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
    //        wmParams = new WindowManager.LayoutParams();
        wmParams = ((MyApplication)getApplication()).getMywmParams();


    //        wmParams.type=2002;          //type是關鍵,這裏的2002表示系統級窗口
        wmParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
        wmParams.format= PixelFormat.RGBA_8888;//設置圖片格式,效果爲背景透明


        wmParams.flags= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE ;//
        wmParams.gravity = Gravity.LEFT|Gravity.TOP;//
        wmParams.x = 0;
        wmParams.y = 0;
        wmParams.width=100;
        wmParams.height=100;
        fsv = new FloatScanView(getApplicationContext());
        fsv.setImageResource(R.mipmap.add_coupon_by_scan);
        fsv.setBackgroundColor(getResources().getColor(R.color.piegps_bg_new));
        wm.addView(fsv, wmParams);
    }




    @Override
    protected void onRestart() {
        super.onRestart();
        wm.addView(fsv,wmParams);
    }




    @Override
    protected void onStop() {
        if(fsv != null){
            wm.removeView(fsv);
        }
        super.onStop();
    }
    //
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(fsv != null){
            wm.removeView(fsv);
        }
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == 11) {
            if (isFloatWindowOpAllowed(this)) {//已經開啓
            } else {
                Toast.makeText(this,"開啓懸浮窗失敗",Toast.LENGTH_SHORT).show();
            }
        } else if (requestCode == 12) {
            if (Build.VERSION.SDK_INT >= 23) {
                if (!Settings.canDrawOverlays(MainActivity.this)) {


                    Toast.makeText(this,"權限授予失敗,無法開啓懸浮窗",Toast.LENGTH_SHORT).show();
                } else {
                }
            }
        }


    }
    /**
     * 判斷懸浮窗權限
     *
     * @param context
     * @return
     */
    @TargetApi(Build.VERSION_CODES.KITKAT)
    public static boolean isFloatWindowOpAllowed(Context context) {
        final int version = Build.VERSION.SDK_INT;
        if (version >= 19) {
            return checkOp(context, 24);  // AppOpsManager.OP_SYSTEM_ALERT_WINDOW
        } else {
            if ((context.getApplicationInfo().flags & 1 << 27) == 1 << 27) {
                return true;
            } else {
                return false;
            }
        }
    }


    @TargetApi(Build.VERSION_CODES.KITKAT)
    public static boolean checkOp(Context context, int op) {
        final int version = Build.VERSION.SDK_INT;


        if (version >= 19) {
            AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
            try {
                Class<?> spClazz = Class.forName(manager.getClass().getName());
                Method method = manager.getClass().getDeclaredMethod("checkOp", int.class, int.class, String.class);
                int property = (Integer) method.invoke(manager, op,
                        Binder.getCallingUid(), context.getPackageName());
                Log.e("399", " property: " + property);


                if (AppOpsManager.MODE_ALLOWED == property) {
                    return true;
                } else {
                    return false;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            Log.e("399", "Below API 19 cannot invoke!");
        }
        return false;
    }




    /**
     * 請求用戶給予懸浮窗的權限
     */
    public void requestPermission() {
        if (isFloatWindowOpAllowed(this)) {//已經開啓


        } else {
            dialog.show();
        }
    }




    /**
     * 打開權限設置界面
     */
    public void openSetting() {
        try {
            Intent localIntent = new Intent(
                    "miui.intent.action.APP_PERM_EDITOR");
            localIntent.setClassName("com.miui.securitycenter",
                    "com.miui.permcenter.permissions.AppPermissionsEditorActivity");
            localIntent.putExtra("extra_pkgname", getPackageName());
            startActivityForResult(localIntent, 11);
    //            LogUtil.E("啓動小米懸浮窗設置界面");
        } catch (ActivityNotFoundException localActivityNotFoundException) {
            Intent intent1 = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
            Uri uri = Uri.fromParts("package", getPackageName(), null);
            intent1.setData(uri);
            startActivityForResult(intent1, 11);
    //            LogUtil.E("啓動懸浮窗界面");
        }




    }


    }
    

    其實這裏面還是有一個問題,就是對於華爲手機5.0以下的會是不是的掃描是否有懸浮框,如果有,它就是自動禁止掉,這個很無奈啊!

資源下載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章