使用Android實現(xiàn)跨頁面懸浮窗效果
實現(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(), "測試點擊懸浮窗", 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> * 跨頁面懸浮窗 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(), "測試點擊懸浮窗", 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()); } }; // 注冊 Activity 生命周期回調(diào) application.registerActivityLifecycleCallbacks(activityLifecycleCallbacks); } public FloatingWindowManager setImg(int icon) { // 使用資源ID直接設(shè)置背景 if (floatingView != null) { floatingView.setBackgroundResource(icon); } // // 獲取Drawable對象 // 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; // 點擊和拖動之間的容差距離 @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) { // 如果移動的距離小于容差,那么我們認為這是一個點擊 if (floatingView.performClick()) { return true; // 點擊已被處理 } } else { // 如果移動的距離大于容差,那么我們認為這是一個拖動 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() { // 實現(xiàn)貼邊功能 // 這里只是一個簡單的例子,你可以根據(jù)自己的需求調(diào)整貼邊的具體實現(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處作為貼邊判斷點 if (x < halfScreenWidth) { floatingView.setX(0); } else { floatingView.setX(screenWidth - floatingView.getWidth()); } } public interface OnFloatingClickListener { void onClick(FrameLayout floatingView); } /** * 顯示一個列表對話框,用戶點擊列表項后會將該項內(nèi)容復(fù)制到剪貼板 * * @param context 上下文 * @param items 列表項數(shù)據(jù) */ public static void showListDialogCopy(Context context, String title, List<String> items) { // 創(chuàng)建一個AlertDialog.Builder對象 AlertDialog.Builder builder = new AlertDialog.Builder(context); // 設(shè)置對話框標題 builder.setTitle(title); // 設(shè)置列表項 builder.setItems(items.toArray(new CharSequence[0]), (dialog, which) -> { // 獲取用戶選擇的項 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)閉對話框 dialog.dismiss(); }); // 創(chuàng)建并顯示對話框 AlertDialog dialog = builder.create(); dialog.show(); } }
代碼很簡單不多解釋 無非是添加隱藏
到此這篇關(guān)于使用Android實現(xiàn)跨頁面懸浮窗效果的文章就介紹到這了,更多相關(guān)Android跨頁面懸浮窗內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android Activity之間相互調(diào)用與傳遞參數(shù)的原理與用法分析
這篇文章主要介紹了Android Activity之間相互調(diào)用與傳遞參數(shù)的原理與用法,較為詳細的分析了Android組件的構(gòu)成以及Activity的創(chuàng)建、調(diào)用、切換等相關(guān)操作技巧,需要的朋友可以參考下2016-08-08Android實現(xiàn)圖像灰度化、線性灰度變化和二值化處理方法
這篇文章主要介紹了Android實現(xiàn)圖像灰度化、線性灰度變化和二值化處理方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-10-10用于cocos2d-x引擎(ndk)中為android項目生成編譯文件列表
在android的ndk項目中,添加很多源文件之后總要手動編寫makefile來添加所有的源文件, 很麻煩,所以寫了一個自動生成編譯源文件列表的小工具2014-05-05自定義一個theme在不同的sdk環(huán)境下繼承不同的值
可能很多在高版本下編繹apk的同學(xué),可能都曾有和我一樣的困惑,就是如何讓低版本的用戶也能有高版本的體驗?zāi)?/div> 2013-01-01Android中通知Notification使用實例(振動、燈光、聲音)
這篇文章主要介紹了Android中通知Notification使用實例,實現(xiàn)振動,燈光,聲音等效果,感興趣的小伙伴們可以參考一下2016-01-01最新評論