日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

android仿苹果悬浮窗(自动停靠、随手指滑动、返回主屏幕)

發(fā)布時間:2024/3/26 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android仿苹果悬浮窗(自动停靠、随手指滑动、返回主屏幕) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

說明:本人寫博客一來是為了方便日后查看項目,二來是希望能夠和廣大的程序猿相互交流學(xué)習(xí),文章布局簡單,如有嫌棄,請繞行,如有錯誤,請指出,謝謝。

實驗環(huán)境:安卓6.0 魅族手機(jī)

懸浮窗主要有以下幾個功能:

1、跟隨手指的滑動而滑動(也可以用鼠標(biāo)滑動)
2、在手指彈起的時候,懸浮窗會自動??吭谧笥覂蓚?cè)
3、點擊懸浮窗按鈕可以返回到桌面

MainActivity中添加6.0訪問權(quán)限

6.0權(quán)限問題:Google在6.0時加入權(quán)限管理機(jī)制,6.0之后,android需要動態(tài)獲取權(quán)限,要使用權(quán)限,不僅要在manifest文件中定義,還要在代碼中動態(tài)獲取。點我了解權(quán)限問題

manifest中添加權(quán)限聲明

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

MainActivity中代碼如下:

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);if (Settings.canDrawOverlays(MainActivity.this)) {Intent intent = new Intent(MainActivity.this, FloatViewService.class);Toast.makeText(MainActivity.this, "已開啟懸浮窗", Toast.LENGTH_SHORT).show();startService(intent);finish();} else {//若沒有權(quán)限,提示獲取.Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);intent.setData(Uri.parse("package:" + getPackageName()));Toast.makeText(MainActivity.this, "需要取得權(quán)限以使用懸浮窗", Toast.LENGTH_SHORT).show();startActivity(intent);finish();}} }

代碼說明:如果手機(jī)已授予該app使用懸浮窗的功能,界面會自動開啟懸浮窗,MainActivity被finish,否則直接跳轉(zhuǎn)到本手機(jī)開啟懸浮窗權(quán)限的界面,親測有效,比如魅族手機(jī)開啟權(quán)限的界面如下圖所示:

問題:只有在第一次安裝app的時候才會跳轉(zhuǎn)到打開權(quán)限的界面,之后打開app則不會跳轉(zhuǎn),這部分不太理解,有知悉的評論區(qū)見。

懸浮窗界面的繪制

Android的窗口是基于WindowManager實現(xiàn)的,它面向的對象一端是屏幕,另一端就是View,比如我們之前使用的setContentView(R.layout.activity_main), 就是將view顯示在屏幕上,代碼的底層都是經(jīng)過WindowManager實現(xiàn)的,整個系統(tǒng)只有一個WindowManager。點我了解界面繪制詳解

Service實現(xiàn)后臺運行

當(dāng)app沒有被關(guān)閉時,懸浮窗同樣可以運行,這時候就需要Service來實現(xiàn)后臺運行。這里可自行百度Service具體實現(xiàn)的過程,本篇不做解釋。

跟隨手指的滑動而滑動

說明:需要監(jiān)聽手勢,所以設(shè)置了setOnTouchListener,識別按下、移動、彈起三個動作,移動的過程需要動態(tài)獲取觸摸的坐標(biāo),所以首先要在按下的過程中獲取按下的坐標(biāo),rawX = event.getRawX(); rawY = event.getRawY(),在移動的過程中進(jìn)行刷新, wmParams.x = wmParams.x - distanceX;wmParams.y = wmParams.y - distanceY。

// 設(shè)置監(jiān)聽浮動窗口的觸摸移動go_mainhome.setOnTouchListener(new View.OnTouchListener() {private float rawX;private float rawY;@SuppressLint("ClickableViewAccessibility")@Overridepublic boolean onTouch(View v, MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN: // Log.i("qqq", "onTouch------------------------------ACTION_DOWN: ");mFloatLayout.setAlpha(1.0f);//設(shè)置其透明度myCountDownTimer.cancel();//取消計時rawX = event.getRawX();rawY = event.getRawY();break;case MotionEvent.ACTION_MOVE: // Log.i("qqq", "onTouch------------------------------ACTION_MOVE: ");// getRawX是觸摸位置相對于屏幕的坐標(biāo),getX是相對于按鈕的坐標(biāo)int distanceX = (int) (event.getRawX() - rawX);int distanceY = (int) (event.getRawY() - rawY);//mFloatView.getMeasuredWidth()和mFloatView.getMeasuredHeight()都是100wmParams.x = wmParams.x - distanceX;wmParams.y = wmParams.y - distanceY;// 刷新mWindowManager.updateViewLayout(mFloatLayout, wmParams);rawX = event.getRawX();rawY = event.getRawY();break;case MotionEvent.ACTION_UP:myCountDownTimer.start();//重新開始計時if (wmParams.x < screenWidth / 2) {//在屏幕右側(cè)wmParams.x = 0;wmParams.y = wmParams.y - 0;} else {//在屏幕左側(cè)wmParams.x = screenWidth;wmParams.y = wmParams.y - 0;}mWindowManager.updateViewLayout(mFloatLayout, wmParams);break;}return false;//此處必須返回false,否則OnClickListener獲取不到監(jiān)聽}});

獲取屏幕大小
嘗試了好幾種獲取屏幕大小的代碼,此方法親測有效。

Display display = mWindowManager.getDefaultDisplay();Point point = new Point();display.getRealSize(point);screenWidth = point.x;screenHeight = point.y;Log.i("qqq", "screenWidth------: " + screenWidth + "\n" + "screenHeight---" + screenHeight);

停靠功能

說明:當(dāng)手指滑動到屏幕中央右側(cè)時,比如在圖中的A點(x,y),最終懸浮窗將會停靠在圖中的B點,A點向右平移到B點,縱坐標(biāo)不變,橫坐標(biāo)為0,在屏幕左側(cè)同理,可詳見代碼case MotionEvent.ACTION_UP部分,前提是需要設(shè)置 wmParams.gravity = Gravity.RIGHT | Gravity.BOTTOM,可以滑動最下面看詳細(xì)代碼。

點擊懸浮窗按鈕可以返回到桌面

其實就是對按鈕設(shè)置了監(jiān)聽setOnClickListener,點擊之后跳轉(zhuǎn)到桌面的main。

Service中的代碼

package com.lightingcontour.toucher;import android.annotation.SuppressLint; import android.app.Service; import android.content.Intent; import android.content.res.Resources; import android.graphics.PixelFormat; import android.graphics.Point; import android.os.CountDownTimer; import android.os.IBinder; import android.util.DisplayMetrics; import android.util.Log; import android.view.Display; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.Toast;public class FloatViewService extends Service {private static final String TAG = "FloatViewService";// 定義浮動窗口布局private LinearLayout mFloatLayout;private WindowManager.LayoutParams wmParams;// 創(chuàng)建浮動窗口設(shè)置布局參數(shù)的對象private WindowManager mWindowManager;private ImageButton go_mainhome;private ImageButton go_back; // private LinearLayout toucher_layout;private int screenHeight;private int screenWidth;private MyCountDownTimer myCountDownTimer;@Overridepublic void onCreate() {super.onCreate();Log.i(TAG, "onCreate");createFloatView();myCountDownTimer = new MyCountDownTimer(2500, 1000); //設(shè)置計時2.5smyCountDownTimer.start();}@SuppressWarnings("static-access")@SuppressLint("InflateParams")private void createFloatView() {wmParams = new WindowManager.LayoutParams();// 通過getApplication獲取的是WindowManagerImpl.CompatModeWrappermWindowManager = (WindowManager) getApplication().getSystemService(getApplication().WINDOW_SERVICE);Display display = mWindowManager.getDefaultDisplay();Point point = new Point();display.getRealSize(point);screenWidth = point.x;screenHeight = point.y;Log.i("qqq", "screenWidth------: " + screenWidth + "\n" + "screenHeight---" + screenHeight);// 設(shè)置window typewmParams.type = WindowManager.LayoutParams.TYPE_PHONE;// 設(shè)置圖片格式,效果為背景透明wmParams.format = PixelFormat.RGBA_8888;// 設(shè)置浮動窗口不可聚焦(實現(xiàn)操作除浮動窗口外的其他可見窗口的操作)wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;// 調(diào)整懸浮窗顯示的??课恢脼橛覀?cè)底部wmParams.gravity = Gravity.RIGHT | Gravity.BOTTOM;// 以屏幕左上角為原點,設(shè)置x、y初始值,相對于gravitywmParams.x = 0;wmParams.y = 0;// 設(shè)置懸浮窗口長寬數(shù)據(jù)wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;LayoutInflater inflater = LayoutInflater.from(getApplication());// 獲取浮動窗口視圖所在布局mFloatLayout = (LinearLayout) inflater.inflate(R.layout.toucherlayout, null);// 添加mFloatLayoutmWindowManager.addView(mFloatLayout, wmParams);// 浮動窗口按鈕go_mainhome = (ImageButton) mFloatLayout.findViewById(R.id.go_mainhome);go_back = (ImageButton) mFloatLayout.findViewById(R.id.go_back);//UNSPECIFIED是未指定模式mFloatLayout.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));// 設(shè)置監(jiān)聽浮動窗口的觸摸移動go_mainhome.setOnTouchListener(new View.OnTouchListener() {private float rawX;private float rawY;@SuppressLint("ClickableViewAccessibility")@Overridepublic boolean onTouch(View v, MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN: // Log.i("qqq", "onTouch------------------------------ACTION_DOWN: ");mFloatLayout.setAlpha(1.0f);//設(shè)置其透明度myCountDownTimer.cancel();//取消計時rawX = event.getRawX();rawY = event.getRawY();break;case MotionEvent.ACTION_MOVE: // Log.i("qqq", "onTouch------------------------------ACTION_MOVE: ");// getRawX是觸摸位置相對于屏幕的坐標(biāo),getX是相對于按鈕的坐標(biāo)int distanceX = (int) (event.getRawX() - rawX);int distanceY = (int) (event.getRawY() - rawY);//mFloatView.getMeasuredWidth()和mFloatView.getMeasuredHeight()都是100wmParams.x = wmParams.x - distanceX;wmParams.y = wmParams.y - distanceY;// 刷新mWindowManager.updateViewLayout(mFloatLayout, wmParams);rawX = event.getRawX();rawY = event.getRawY();break;case MotionEvent.ACTION_UP:myCountDownTimer.start();//重新開始計時if (wmParams.x < screenWidth / 2) {//在屏幕右側(cè)wmParams.x = 0;wmParams.y = wmParams.y - 0;} else {wmParams.x = screenWidth;wmParams.y = wmParams.y - 0;}mWindowManager.updateViewLayout(mFloatLayout, wmParams);break;}return false;//此處必須返回false,否則OnClickListener獲取不到監(jiān)聽}});// 設(shè)置監(jiān)聽浮動窗口的觸摸移動go_back.setOnTouchListener(new View.OnTouchListener() {private float rawX;private float rawY;@SuppressLint("ClickableViewAccessibility")@Overridepublic boolean onTouch(View v, MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN: // Log.i("qqq", "onTouch------------------------------ACTION_DOWN: ");mFloatLayout.setAlpha(1.0f);//設(shè)置其透明度myCountDownTimer.cancel();//取消計時rawX = event.getRawX();rawY = event.getRawY();break;case MotionEvent.ACTION_MOVE: // Log.i("qqq", "onTouch------------------------------ACTION_MOVE: ");// getRawX是觸摸位置相對于屏幕的坐標(biāo),getX是相對于按鈕的坐標(biāo)int distanceX = (int) (event.getRawX() - rawX);int distanceY = (int) (event.getRawY() - rawY);//mFloatView.getMeasuredWidth()和mFloatView.getMeasuredHeight()都是100wmParams.x = wmParams.x - distanceX;wmParams.y = wmParams.y - distanceY;// 刷新mWindowManager.updateViewLayout(mFloatLayout, wmParams);rawX = event.getRawX();rawY = event.getRawY();break;case MotionEvent.ACTION_UP:myCountDownTimer.start();//重新開始計時if (wmParams.x < screenWidth / 2) {//在屏幕右側(cè)wmParams.x = 0;wmParams.y = wmParams.y - 0;} else {wmParams.x = screenWidth;wmParams.y = wmParams.y - 0;}mWindowManager.updateViewLayout(mFloatLayout, wmParams);break;}return false;//此處必須返回false,否則OnClickListener獲取不到監(jiān)聽}});go_mainhome.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(FloatViewService.this, "返回到桌面",Toast.LENGTH_SHORT).show();Intent intent = new Intent();// 為Intent設(shè)置Action、Category屬性intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.setAction(Intent.ACTION_MAIN);// "android.intent.action.MAIN"intent.addCategory(Intent.CATEGORY_HOME); //"android.intent.category.HOME"CATEGORY_HOME 目標(biāo)Activity是HOME Activity,即手機(jī)開機(jī)啟動后顯示的Activity,或按下HOME鍵后顯示的ActivitystartActivity(intent);}});go_back.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(FloatViewService.this, "返回",Toast.LENGTH_SHORT).show();}});}@Overridepublic void onDestroy() {super.onDestroy();if (mFloatLayout != null) {// 移除懸浮窗口mWindowManager.removeView(mFloatLayout);}}@Overridepublic IBinder onBind(Intent intent) {return null;}public class MyCountDownTimer extends CountDownTimer {public MyCountDownTimer(long millisInFuture, long countDownInterval) {super(millisInFuture, countDownInterval);}@Overridepublic void onTick(long millisUntilFinished) {}@Overridepublic void onFinish() {mFloatLayout.setAlpha(0.4f);}}}

manifest中的代碼

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.lightingcontour.toucher"><uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /><uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" /><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/AppTheme"><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><service android:name=".FloatViewService" /></application></manifest>

布局中的代碼

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><LinearLayoutandroid:id="@+id/toucher_layout"android:orientation="vertical"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageButtonandroid:id="@+id/go_mainhome"android:layout_marginBottom="10dp"android:layout_width="50dp"android:layout_height="50dp"android:background="@drawable/go_mainhome" /><ImageButtonandroid:id="@+id/go_back"android:layout_width="50dp"android:layout_height="50dp"android:background="@drawable/go_back" /></LinearLayout></LinearLayout>

總結(jié)

以上是生活随笔為你收集整理的android仿苹果悬浮窗(自动停靠、随手指滑动、返回主屏幕)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。