使用Android實(shí)現(xiàn)跨頁(yè)面懸浮窗效果
實(shí)現(xiàn)效果

在 Application#onCreate初始化
FloatingWindowManager
.getInstance(App.this).setImg(R.drawable.ic_test).setWidthHeight(280,280)
.setOnFloatingClickListener(new FloatingWindowManager.OnFloatingClickListener() {
@Override
public void onClick(FrameLayout floatingView) {
Toast.makeText(floatingView.getContext(), "測(cè)試點(diǎn)擊懸浮窗", Toast.LENGTH_SHORT).show();
}
});完整代碼
import android.app.Activity;
import android.app.Application;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import java.util.List;
/**
* https://blog.csdn.net/xiaoerbuyu1233/article/details/143595475
* <p>
* 跨頁(yè)面懸浮窗 Application#onCreate 初始化
* <p>
* FloatingWindowManager
* .getInstance(App.this).setImg(R.drawable.ic_test).setWidthHeight(280,280)
* .setOnFloatingClickListener(new FloatingWindowManager.OnFloatingClickListener() {
*
* @Override public void onClick(FrameLayout floatingView) {
* Toast.makeText(floatingView.getContext(), "測(cè)試點(diǎn)擊懸浮窗", Toast.LENGTH_SHORT).show();
* }
* });
*/
public class FloatingWindowManager {
private static FloatingWindowManager instance;
private final Context context;
private FrameLayout floatingView;
private ViewGroup rootView;
private OnFloatingClickListener listener;
private int width = 200, height = 200;
private Application.ActivityLifecycleCallbacks activityLifecycleCallbacks;
private FloatingWindowManager(Context context) {
this.context = context;
initFloatingView();
registerActivityLifecycleCallbacks();
}
public static synchronized FloatingWindowManager getInstance(Context context) {
if (instance == null) {
instance = new FloatingWindowManager(context);
}
return instance;
}
private void registerActivityLifecycleCallbacks() {
Application application = (Application) context.getApplicationContext();
activityLifecycleCallbacks = new Application.ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
Log.d("FloatingWindowManager", "onActivityCreated: " + activity.getLocalClassName());
// 可以在這里附加懸浮窗口到新創(chuàng)建的 Activity
attachToActivity(activity);
}
@Override
public void onActivityStarted(Activity activity) {
Log.d("FloatingWindowManager", "onActivityStarted: " + activity.getLocalClassName());
}
@Override
public void onActivityResumed(Activity activity) {
Log.d("FloatingWindowManager", "onActivityResumed: " + activity.getLocalClassName());
attachToActivity(activity);
}
@Override
public void onActivityPaused(Activity activity) {
detachFromActivity();
Log.d("FloatingWindowManager", "onActivityPaused: " + activity.getLocalClassName());
}
@Override
public void onActivityStopped(Activity activity) {
Log.d("FloatingWindowManager", "onActivityStopped: " + activity.getLocalClassName());
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
Log.d("FloatingWindowManager", "onActivitySaveInstanceState: " + activity.getLocalClassName());
}
@Override
public void onActivityDestroyed(Activity activity) {
Log.d("FloatingWindowManager", "onActivityDestroyed: " + activity.getLocalClassName());
}
};
// 注冊(cè) Activity 生命周期回調(diào)
application.registerActivityLifecycleCallbacks(activityLifecycleCallbacks);
}
public FloatingWindowManager setImg(int icon) {
// 使用資源ID直接設(shè)置背景
if (floatingView != null) {
floatingView.setBackgroundResource(icon);
}
// // 獲取Drawable對(duì)象
// Drawable drawable = ContextCompat.getDrawable(context, R.drawable.ic_test);
//
// // 設(shè)置背景
// if (drawable != null) {
// floatingView.setBackground(drawable);
// }
return this;
}
public FloatingWindowManager setWidthHeight(int width, int height) {
this.width = width;
this.height = height;
// 設(shè)置布局參數(shù)
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(width, height); // 寬度和高度
layoutParams.gravity = Gravity.TOP | Gravity.START;
floatingView.setLayoutParams(layoutParams);
return this;
}
private void initFloatingView() {
// 初始化懸浮窗視圖
floatingView = new FrameLayout(context) {
@Override
public boolean performClick() {
if (super.performClick()) {
return true;
}
if (listener != null) {
floatingView.getHandler().post(() -> listener.onClick(floatingView));
}
return true;
}
};
floatingView.setOnTouchListener(new View.OnTouchListener() {
private int initialX, initialY;
private float startX, startY;
private static final int CLICK_DRAG_TOLERANCE = 40; // 點(diǎn)擊和拖動(dòng)之間的容差距離
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
initialX = (int) (event.getRawX() - v.getX());
initialY = (int) (event.getRawY() - v.getY());
startX = event.getRawX();
startY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
int newX = (int) (event.getRawX() - initialX);
int newY = (int) (event.getRawY() - initialY);
if (rootView != null) {
v.setX(newX);
v.setY(newY);
}
break;
case MotionEvent.ACTION_UP:
float endX = event.getRawX();
float endY = event.getRawY();
float distanceX = Math.abs(endX - startX);
float distanceY = Math.abs(endY - startY);
if (distanceX < CLICK_DRAG_TOLERANCE && distanceY < CLICK_DRAG_TOLERANCE) {
// 如果移動(dòng)的距離小于容差,那么我們認(rèn)為這是一個(gè)點(diǎn)擊
if (floatingView.performClick()) {
return true; // 點(diǎn)擊已被處理
}
} else {
// 如果移動(dòng)的距離大于容差,那么我們認(rèn)為這是一個(gè)拖動(dòng)
snapToEdge();
}
break;
default:
break;
}
return true; // 消耗事件
}
});
// 設(shè)置布局參數(shù)
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(width, height); // 寬度和高度
layoutParams.gravity = Gravity.TOP | Gravity.START;
floatingView.setLayoutParams(layoutParams);
}
public void attachToActivity(Activity activity) {
if (activity == null || floatingView == null) return;
rootView = (ViewGroup) activity.findViewById(android.R.id.content);
if (rootView != null && floatingView.getParent() == null) {
rootView.addView(floatingView);
}
}
public void detachFromActivity() {
if (rootView != null && floatingView != null && floatingView.getParent() != null) {
rootView.removeView(floatingView);
}
}
public void setOnFloatingClickListener(OnFloatingClickListener listener) {
this.listener = listener;
}
private void snapToEdge() {
// 實(shí)現(xiàn)貼邊功能
// 這里只是一個(gè)簡(jiǎn)單的例子,你可以根據(jù)自己的需求調(diào)整貼邊的具體實(shí)現(xiàn)
int[] location = new int[2];
floatingView.getLocationOnScreen(location);
int x = location[0];
int y = location[1];
int screenWidth = rootView.getWidth();
int screenHeight = rootView.getHeight();
int halfScreenWidth = screenWidth / 4; // 假設(shè)屏幕寬度的3/4處作為貼邊判斷點(diǎn)
if (x < halfScreenWidth) {
floatingView.setX(0);
} else {
floatingView.setX(screenWidth - floatingView.getWidth());
}
}
public interface OnFloatingClickListener {
void onClick(FrameLayout floatingView);
}
/**
* 顯示一個(gè)列表對(duì)話框,用戶點(diǎn)擊列表項(xiàng)后會(huì)將該項(xiàng)內(nèi)容復(fù)制到剪貼板
*
* @param context 上下文
* @param items 列表項(xiàng)數(shù)據(jù)
*/
public static void showListDialogCopy(Context context, String title, List<String> items) {
// 創(chuàng)建一個(gè)AlertDialog.Builder對(duì)象
AlertDialog.Builder builder = new AlertDialog.Builder(context);
// 設(shè)置對(duì)話框標(biāo)題
builder.setTitle(title);
// 設(shè)置列表項(xiàng)
builder.setItems(items.toArray(new CharSequence[0]), (dialog, which) -> {
// 獲取用戶選擇的項(xiàng)
String selectedItem = items.get(which);
// 復(fù)制文本到剪貼板
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("label", selectedItem);
clipboard.setPrimaryClip(clip);
// 可選: 提示用戶已成功復(fù)制
Toast.makeText(context, "已復(fù)制: " + selectedItem, Toast.LENGTH_SHORT).show();
// 關(guān)閉對(duì)話框
dialog.dismiss();
});
// 創(chuàng)建并顯示對(duì)話框
AlertDialog dialog = builder.create();
dialog.show();
}
}代碼很簡(jiǎn)單不多解釋 無非是添加隱藏
到此這篇關(guān)于使用Android實(shí)現(xiàn)跨頁(yè)面懸浮窗效果的文章就介紹到這了,更多相關(guān)Android跨頁(yè)面懸浮窗內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Android懸浮窗的實(shí)現(xiàn)步驟
- Android開發(fā)懸浮窗踩坑解決
- Android 無障礙全局懸浮窗實(shí)現(xiàn)示例
- Android實(shí)現(xiàn)懸浮窗效果
- Android應(yīng)用內(nèi)懸浮窗Activity的簡(jiǎn)單實(shí)現(xiàn)
- Android超簡(jiǎn)單懸浮窗使用教程
- Android實(shí)現(xiàn)懸浮窗的簡(jiǎn)單方法實(shí)例
- Android創(chuàng)建懸浮窗的完整步驟
- Android?懸浮窗開發(fā)示例((動(dòng)態(tài)權(quán)限請(qǐng)求?|?前臺(tái)服務(wù)和通知?|?懸浮窗創(chuàng)建?)
相關(guān)文章
Flutter實(shí)現(xiàn)單選,復(fù)選和開關(guān)組件的示例代碼
在App開發(fā)過程中,選擇交互是非常常見的,今天主要介紹下關(guān)于選擇的三個(gè)組件的使用:開關(guān)、單選和復(fù)選,感興趣的小伙伴可以了解一下2022-04-04
Android開發(fā)學(xué)習(xí)筆記 Gallery和GridView淺析
這篇文章主要介紹了Android開發(fā)學(xué)習(xí)筆記 Gallery和GridView淺析,需要的朋友可以參考下2014-11-11
Android 自定義view實(shí)現(xiàn)TopBar效果
這篇文章主要為大家詳細(xì)介紹了Android 自定義view實(shí)現(xiàn)TopBar效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09
Android開發(fā)之Picasso通過URL獲取用戶頭像的圓形顯示
這篇文章主要介紹了android開發(fā)之Picasso通過URL獲取用戶頭像的圓形顯示,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-06-06
Android使用Handler實(shí)現(xiàn)打地鼠游戲
這篇文章主要為大家詳細(xì)介紹了Android使用Handler實(shí)現(xiàn)打地鼠游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06
Android利用ViewDragHelper輕松實(shí)現(xiàn)拼圖游戲的示例
本篇文章主要介紹了Android利用ViewDragHelper輕松實(shí)現(xiàn)拼圖游戲的示例,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-11-11
Android UI實(shí)現(xiàn)多行文本折疊展開效果
這篇文章主要為大家詳細(xì)介紹了Android UI實(shí)現(xiàn)多行文本折疊展開效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10
android 選項(xiàng)卡(TabHost)如何放置在屏幕的底部
如何將TAB放置在屏幕的底端,有很多的新手都想實(shí)現(xiàn)這種效果,本文搜集整理了一些,感興趣的朋友可以參考下哦2013-01-01

