Android自定義懸浮按鈕效果
本文實(shí)例為大家分享了Android自定義懸浮按鈕效果的具體代碼,供大家參考,具體內(nèi)容如下
以下:內(nèi)容沒(méi)有參考,寫的也是一個(gè)比較簡(jiǎn)單的例子,主要就是應(yīng)用切換前后臺(tái)時(shí)會(huì)顯示/隱藏懸浮窗。內(nèi)容僅用于自我記錄學(xué)習(xí)使用。
項(xiàng)目的開發(fā)時(shí)應(yīng)用在登陸后顯示一個(gè)懸浮窗,同時(shí)顯示在線人數(shù)等一些其他信息,點(diǎn)擊懸浮按鈕可以顯示全局彈窗名單。開發(fā)完成后,覺(jué)著需要記錄一下這種實(shí)現(xiàn)方式。所以寫一個(gè)簡(jiǎn)單的Demo。
Demo思路是通過(guò)啟動(dòng)Service來(lái)添加/移除 懸浮窗,因?yàn)槭且粋€(gè)全局懸浮窗,所以選擇依附于Service。
在MyAppliction中監(jiān)聽(tīng)?wèi)?yīng)用的前后臺(tái)切換來(lái)添加或者隱藏懸浮窗。
其自定義的懸浮窗View在項(xiàng)目中是通過(guò)Canvas手動(dòng)繪制的,這里圖個(gè)省事直接加載一個(gè)布局文件。主要是想理解這種添加全局懸浮窗的方式,所以Demo樣式和功能能簡(jiǎn)則簡(jiǎn)。
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(); } /** * 監(jiān)聽(tīng)程序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) { } }); } /** * 判斷應(yīng)用運(yùn)行在前后臺(tái) */ public boolean isRunningForeground() { ActivityManager activityManager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningAppProcessInfo> appProcessInfos = activityManager.getRunningAppProcesses(); // 枚舉進(jìn)程 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() { // 通過(guò)WindowManager來(lái)添加懸浮窗 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; // 左上為坐標(biāo)點(diǎn) // 設(shè)置View大小 params.height = 50; params.width = 50; // 設(shè)置View坐標(biāo)位置 params.x = 0; params.y = mScreenHeight / 2; this.mButton = FloatingActionButton.getInstance(this); this.mWindowManager.addView(mButton, params); } /** * 初始化事件 */ private void initEvent() { this.mButton.setOnClickListener(v -> { // 項(xiàng)目中是在這里進(jìn)行添加 名單彈窗 和移除名單彈窗的操作,但名單彈窗的初始化不在這里 Toast.makeText(this, "點(diǎn)擊了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 自定義懸浮按鈕 -- 這里隨便寫的一個(gè)布局文件 */ 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(){ // 隨便添加一個(gè)簡(jiǎn)單的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) { //獲取相對(duì)屏幕的坐標(biāo),即以屏幕左上角為原點(diǎn) float rawX = event.getRawX(); float rawY = event.getRawY() - mStatusHeight; //statusHeight是系統(tǒng)狀態(tài)欄的高度 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; // 這里的策略是默認(rèn)松手吸附到左側(cè) 如果需要吸附到右側(cè)則改為mFloatingLayoutParams.x = mScreenWidth; mScreenWidth 是屏幕寬度,不想吸附則注釋這一句即可 this.mWindowManager.updateViewLayout(this, mFloatingLayoutParams); break; } return isIntercept ? isIntercept : super.onTouchEvent(event); } /** * 狀態(tài)欄的高度 */ public static int getStatusHeight(Context context) { int statusHeight = -1; try { Class clazz = Class.forName("com.android.internal.R$dimen"); //使用反射獲取實(shí)例 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; } }
布局文件
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:background="@color/colorPrimary"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="12312313131313" android:textSize="20sp" /> </LinearLayout>
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Flutter仿微信通訊錄實(shí)現(xiàn)自定義導(dǎo)航條的示例代碼
某些頁(yè)面比如我們?cè)谶x擇聯(lián)系人或者某個(gè)城市的時(shí)候需要快速定位到我們需要的選項(xiàng),一般都會(huì)需要像微信通訊錄右邊有一個(gè)導(dǎo)航條一樣的功能,本文將利用Flutter實(shí)現(xiàn)這一效果,需要的可以參考一下2022-04-04android應(yīng)用開發(fā)之spinner控件的簡(jiǎn)單使用
Android的控件有很多種,其中就有一個(gè)Spinner的控件,這個(gè)控件其實(shí)就是一個(gè)下拉顯示列表。本文通過(guò)腳本之家平臺(tái)給大家介紹android應(yīng)用開發(fā)之spinner控件的簡(jiǎn)單使用,感興趣的朋友可以參考下2015-11-11android商品詳情頁(yè)面設(shè)計(jì)詳解
這篇文章主要為大家詳細(xì)介紹了android商品詳情頁(yè)面設(shè)計(jì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12Kotlin協(xié)程操作之創(chuàng)建啟動(dòng)掛起恢復(fù)詳解
本文的定位是協(xié)程的創(chuàng)建、啟動(dòng)、掛起、恢復(fù),也會(huì)示例一些簡(jiǎn)單的使用,這里不對(duì)suspend講解,,也不對(duì)協(xié)程的高級(jí)用法做闡述(熱數(shù)據(jù)通道Channel、冷數(shù)據(jù)流Flow...),本文主要講協(xié)程稍微深入的全面知識(shí)2022-08-08基于Android實(shí)現(xiàn)點(diǎn)擊某個(gè)按鈕讓菜單選項(xiàng)從按鈕周圍指定位置彈出
這篇文章主要介紹了基于Android實(shí)現(xiàn)點(diǎn)擊某個(gè)按鈕讓菜單選項(xiàng)從按鈕周圍指定位置彈出的相關(guān)資料,需要的朋友可以參考下2015-12-12