android创建可拖动的悬浮窗,并实现点击回调
最近用到悬浮窗的功能,android6.0以下的系统可以进行正常的显示,而android6.0及以上的则创建不了,运行直接崩溃,报如下错误:
Caused by: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@96872e8 -- permission denied for this window type
但是我在清单文件明明加了
查看android的官方文档:Note:If the app targets API level 23 or higher, the app user must explicitly grant this permission to the app through a permission management screen. The app requests the user's approval by sending an intent with action ACTION_MANAGE_OVERLAY_PERMISSION . The app can check whether it has this authorization by calling Settings.canDrawOverlays() .
能够熟练阅读各类英文文档也是一门非常重要的技能。上面的描述大概意思就是说,如果我们的 targetSdkVersion指定成了23或者更高,在使用 SYSTEM_ALERT_WINDOW权限时,需要先调用 Settings.canDrawOverlays() 来判断一下是否允许创建悬浮窗,如果允许的话就可以创建了,不允许的话还要发送一个action值为 ACTION_MANAGE_OVERLAY_PERMISSION 的Intent来让用户同意创建悬浮窗。
先上效果图:
那下面就这样试着写一下,代码如下:
package com.cool.floatviewdemo;import android.annotation.TargetApi;import android.app.Activity;import android.content.Intent;import android.os.Build;import android.os.Bundle;import android.provider.Settings;import android.util.Log;import android.view.View;import android.widget.Toast;public class MainActivity extends Activity { private final static int REQUEST_CODE = 100; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void callPhone(View v){ startActivity(new Intent(this, CallPhoneActivity.class)); } /** *显示悬浮窗 * @param v */ public void showFloatView(View v) { if (Build.VERSION.SDK_INT >= 23) { if (Settings.canDrawOverlays(this)) { showFloatView(); } else { Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); startActivityForResult(intent, REQUEST_CODE); } } else { showFloatView(); } } @TargetApi(Build.VERSION_CODES.M) @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if(requestCode == REQUEST_CODE){ if (Settings.canDrawOverlays(this)) { showFloatView(); } else { Toast.makeText(this,"授权失败",Toast.LENGTH_SHORT).show(); } } } /** * 显示悬浮窗 */ private void showFloatView() { FloatView.showFloatView(getApplication(), R.layout.layout_float_window); FloatView.setOnClickListener(new FloatView.OnClickListener() { @Override public void onClick(View view) { Log.e("399", "点击了"); } }); } /** * 隐藏悬浮窗 * @param v */ public void hideFloatView(View v){ FloatView.hideFloatView(); }}
悬浮窗的View如下:
package com.cool.floatviewdemo;import android.content.Context;import android.graphics.PixelFormat;import android.view.Gravity;import android.view.LayoutInflater;import android.view.MotionEvent;import android.view.View;import android.view.WindowManager;/** * Created by cool on 2016/8/30. */public class FloatView { private static Context mContext; private static WindowManager mWindowManager; private static WindowManager.LayoutParams wmParams; private static View mView; private static boolean isShow = false;//悬浮框是否已经显示 private static OnClickListener mListener;//view的点击回调listener public static void setOnClickListener(OnClickListener listener){ mListener = listener; } interface OnClickListener{ void onClick(View view); } /** * 显示悬浮框 */ public static void showFloatView(Context context,int layoutId){ mContext = context; if(isShow){ return; } mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); wmParams = new WindowManager.LayoutParams(); wmParams.type = WindowManager.LayoutParams.TYPE_PHONE; wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; wmParams.gravity = Gravity.CENTER; wmParams.format = PixelFormat.RGBA_8888; wmParams.x = context.getResources().getDisplayMetrics().widthPixels; wmParams.y = 0; wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT; wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT; mView = LayoutInflater.from(context).inflate(layoutId, null); mWindowManager.addView(mView, wmParams); mView.setOnTouchListener(new View.OnTouchListener() { float downX = 0; float downY = 0; int oddOffsetX = 0; int oddOffsetY = 0; @Override public boolean onTouch(View v, MotionEvent event) { switch(event.getAction()){ case MotionEvent.ACTION_DOWN: downX = event.getX(); downY = event.getY(); oddOffsetX = wmParams.x; oddOffsetY = wmParams.y; break; case MotionEvent.ACTION_MOVE: float moveX = event.getX(); float moveY = event.getY(); //不除以3,拖动的view抖动的有点厉害 wmParams.x += (moveX - downX)/3; wmParams.y += (moveY - downY)/3; if(mView != null){ mWindowManager.updateViewLayout(mView,wmParams); } break; case MotionEvent.ACTION_UP: int newOffsetX = wmParams.x; int newOffsetY = wmParams.y; if(Math.abs(newOffsetX - oddOffsetX) <=20 && Math.abs(newOffsetY - oddOffsetY) <=20){ if(mListener != null){ mListener.onClick(mView); } } break; } return true; } }); isShow = true; } /** * 隐藏悬浮窗 */ public static void hideFloatView(){ if(mWindowManager != null && isShow){ mWindowManager.removeView(mView); isShow = false; } }}
更多相关文章
- Android版本更新(断点下载)
- android之监控各个程序的流量
- android superuser.apk 管理root权限原理分析
- android7.1 外置SD卡三方应用没有读写权限
- [置顶] Android学习系列-把文件保存到SD卡上面(6)
- 使用mount修改你的android中/system为只读权限
- android――SharedPreferences存放数据
- Android无需申请权限拨打电话的两种方式
- Android中XML文件解析