本文实例为大家分享了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; } @Override public void onCreate() { super.onCreate(); this.monitorActivityLifecycle(); } /** * 监听程序Activity声明周期 */ private void monitorActivityLifecycle() { this.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { } @Override public void onActivityStarted(Activity activity) { if (isRunningForeground()){ mIntent = new Intent(MyApplication.this, FloatingActionService.class); startService(mIntent); } } @Override public void onActivityResumed(Activity activity) { } @Override public void onActivityPaused(Activity activity) { } @Override public void onActivityStopped(Activity activity) { if (!isRunningForeground()){ stopService(mIntent); } } @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) { } @Override public void onActivityDestroyed(Activity activity) { } }); } /** * 判断应用运行在前后台 */ public boolean isRunningForeground() { ActivityManager activityManager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE); ListappProcessInfos = 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; @Override public void onCreate() { super.onCreate(); this.initView(); this.initEvent(); } @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { return super.onStartCommand(intent, flags, startId); } @Override public 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(); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected 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); } @Override public 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; } }
布局文件
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。