Android自定义悬浮按钮效果

本文实例为大家分享了Android自定义悬浮按钮效果的具体代码,供大家参考,具体内容如下
以下:内容没有参考,写的也是一个比较简单的例子,主要就是应用切换前后台时会显示/隐藏悬浮窗。内容仅用于自我记录学习使用。
项目的开发时应用在登陆后显示一个悬浮窗,同时显示在线人数等一些其他信息,点击悬浮按钮可以显示全局弹窗名单。开发完成后,觉着需要记录一下这种实现方式。所以写一个简单的Demo。
Demo思路是通过启动Service来添加/移除 悬浮窗,因为是一个全局悬浮窗,所以选择依附于Service。
在MyAppliction中监听应用的前后台切换来添加或者隐藏悬浮窗。
其自定义的悬浮窗View在项目中是通过Canvas手动绘制的,这里图个省事直接加载一个布局文件。主要是想理解这种添加全局悬浮窗的方式,所以Demo样式和功能能简则简。
MyAppliction

package com.example.qxb_810.floatbuttondemo.application; import android.app.Activity; import android.app.ActivityManager; import android.app.Application; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.WindowManager; import com.example.qxb_810.floatbuttondemo.service.FloatingActionService; import java.util.List; import static android.content.ContentValues.TAG; /** * create 2018/12/1 13:31 * desc 自定义Application */public class MyApplication extends Application {private Intent mIntent; private WindowManager.LayoutParams mFloatingLayoutParams = new WindowManager.LayoutParams(); public WindowManager.LayoutParams getmFloatingLayoutParams() {return mFloatingLayoutParams; }@Overridepublic void onCreate() {super.onCreate(); this.monitorActivityLifecycle(); }/*** 监听程序Activity声明周期*/private void monitorActivityLifecycle() {this.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {@Overridepublic void onActivityCreated(Activity activity, Bundle savedInstanceState) {}@Overridepublic void onActivityStarted(Activity activity) {if (isRunningForeground()){mIntent = new Intent(MyApplication.this, FloatingActionService.class); startService(mIntent); }}@Overridepublic void onActivityResumed(Activity activity) {}@Overridepublic void onActivityPaused(Activity activity) {}@Overridepublic void onActivityStopped(Activity activity) {if (!isRunningForeground()){stopService(mIntent); }}@Overridepublic void onActivitySaveInstanceState(Activity activity, Bundle outState) {}@Overridepublic void onActivityDestroyed(Activity activity) {}}); }/*** 判断应用运行在前后台*/public boolean isRunningForeground() {ActivityManager activityManager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE); List appProcessInfos = activityManager.getRunningAppProcesses(); // 枚举进程for (ActivityManager.RunningAppProcessInfo appProcessInfo : appProcessInfos) {if (appProcessInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {if (appProcessInfo.processName.equals(this.getApplicationInfo().processName)) {Log.d(TAG, "EntryActivity isRunningForeGround"); return true; }}}Log.d(TAG, "EntryActivity isRunningBackGround"); return false; }}

FloatingActionService.java ---- 悬浮窗所依附的Service
package com.example.qxb_810.floatbuttondemo.service; import android.app.Service; import android.content.Intent; import android.graphics.PixelFormat; import android.os.IBinder; import android.support.annotation.Nullable; import android.util.DisplayMetrics; import android.view.Display; import android.view.Gravity; import android.view.WindowManager; import android.widget.LinearLayout; import android.widget.Toast; import com.example.qxb_810.floatbuttondemo.application.MyApplication; import com.example.qxb_810.floatbuttondemo.button.FloatingActionButton; /** * create 2018/12/1 13:34 * desc悬浮按钮Service */public class FloatingActionService extends Service {private WindowManager mWindowManager; private FloatingActionButton mButton; private int mScreenWidth; private int mScreenHeight; @Overridepublic void onCreate() {super.onCreate(); this.initView(); this.initEvent(); }@Nullable@Overridepublic IBinder onBind(Intent intent) {return null; }@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {return super.onStartCommand(intent, flags, startId); }@Overridepublic void onDestroy() {super.onDestroy(); this.mWindowManager.removeViewImmediate(this.mButton); }/*** 添加按钮*/private void initView() {// 通过WindowManager来添加悬浮窗this.mWindowManager = (WindowManager) this.getApplicationContext().getSystemService(WINDOW_SERVICE); Display display = this.mWindowManager.getDefaultDisplay(); DisplayMetrics metrics = new DisplayMetrics(); display.getMetrics(metrics); this.mScreenWidth = metrics.widthPixels; this.mScreenHeight = metrics.heightPixels; WindowManager.LayoutParams params = ((MyApplication) this.getApplication()).getmFloatingLayoutParams(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {mFloatingLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } else {mFloatingLayoutParams.type = WindowManager.LayoutParams.TYPE_TOAST; }params.format = PixelFormat.RGBA_8888; params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; params.gravity = Gravity.LEFT | Gravity.TOP; // 左上为坐标点// 设置View大小params.height = 50; params.width = 50; // 设置View坐标位置params.x = 0; params.y = mScreenHeight / 2; this.mButton = FloatingActionButton.getInstance(this); this.mWindowManager.addView(mButton, params); }/***初始化事件*/private void initEvent() {this.mButton.setOnClickListener(v -> {// 项目中是在这里进行添加 名单弹窗 和移除名单弹窗的操作,但名单弹窗的初始化不在这里Toast.makeText(this, "点击了Button", Toast.LENGTH_SHORT).show(); }); }}

FloatingActionButton – 弹窗按钮
package com.example.qxb_810.floatbuttondemo.button; import android.content.Context; import android.graphics.Canvas; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.LinearLayout; import com.example.qxb_810.floatbuttondemo.R; import com.example.qxb_810.floatbuttondemo.application.MyApplication; import static android.content.Context.WINDOW_SERVICE; /** * create 2018/12/1 13:35 * desc 自定义悬浮按钮--这里随便写的一个布局文件 */public class FloatingActionButton extends FrameLayout {private Context mContext; private float mStartPointX; private float mStartPointY; private WindowManager mWindowManager; private WindowManager.LayoutParams mFloatingLayoutParams; private boolean isIntercept = false; private int mStatusHeight; public static FloatingActionButton getInstance(Context context) {FloatingActionButton button = new FloatingActionButton(context); return button; }public FloatingActionButton(Context context) {this(context, null); }public FloatingActionButton(Context context, @Nullable AttributeSet attrs) {this(context, attrs, -1); }public FloatingActionButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr); mContext = context; initView(); }@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom); }@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec); }@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas); }/***初始化View*/private void initView(){// 随便添加一个简单的View布局作为悬浮窗View view = LayoutInflater.from(mContext).inflate(R.layout.layout_floating_button, null, false); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT); this.addView(view, params); this.mWindowManager = (WindowManager) getContext().getApplicationContext().getSystemService(WINDOW_SERVICE); this.mFloatingLayoutParams = ((MyApplication) getContext().getApplicationContext()).getmFloatingLayoutParams(); this.mStatusHeight = getStatusHeight(mContext); }@Overridepublic boolean onTouchEvent(MotionEvent event) {//获取相对屏幕的坐标,即以屏幕左上角为原点float rawX = event.getRawX(); float rawY = event.getRawY() - mStatusHeight; //statusHeight是系统状态栏的高度switch (event.getAction()){case MotionEvent.ACTION_DOWN:this.mStartPointX = event.getX(); this.mStartPointY = event.getY(); isIntercept = false; break; case MotionEvent.ACTION_MOVE:mFloatingLayoutParams.x = (int)(rawX - mStartPointX); mFloatingLayoutParams.y = (int)(rawY - mStartPointY); this.mWindowManager.updateViewLayout(this, mFloatingLayoutParams); isIntercept = true; break; case MotionEvent.ACTION_UP:mFloatingLayoutParams.x = 0; // 这里的策略是默认松手吸附到左侧 如果需要吸附到右侧则改为mFloatingLayoutParams.x = mScreenWidth; mScreenWidth 是屏幕宽度,不想吸附则注释这一句即可this.mWindowManager.updateViewLayout(this, mFloatingLayoutParams); break; }return isIntercept ? isIntercept : super.onTouchEvent(event); }/*** 状态栏的高度*/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; }}

布局文件

【Android自定义悬浮按钮效果】以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

    推荐阅读