Android可拖动可吸附悬浮窗

您所在的位置:网站首页 oppor9的悬浮球怎么关掉 Android可拖动可吸附悬浮窗

Android可拖动可吸附悬浮窗

2023-09-20 22:27| 来源: 网络整理| 查看: 265

第一次写稿,写的不好,请大神多提建议

前言

前一段时间由于项目需要,写了一个可拖动可靠边吸附的悬浮窗,特意记录下来,方便大家一起学习

一、FloatingViewMagnet

悬浮窗的吸附管理类,代码示例:

package com.example.myapplication; import android.content.Context; import android.content.res.Configuration; import android.os.Handler; import android.os.Looper; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.ViewGroup; import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; /** * 吸附悬浮窗管理类 * @author zgp * @data 2022/2/17 */ public class FloatingViewMagnet extends FrameLayout { public static final int MARGIN_EDGE = 13; private static final int TOUCH_TIME_THRESHOLD = 150; private float mOriginalRawX; private float mOriginalRawY; private float mOriginalX; private float mOriginalY; private MagnetViewListener mMagnetViewListener; private long mLastTouchDownTime; protected MoveAnimator mMoveAnimator; protected int mScreenWidth; private int mScreenHeight; private int mStatusBarHeight; private boolean isNearestLeft = true; private float mPortraitY; private Context mContext; public FloatingViewMagnet(@NonNull Context context) { this(context, null); } public FloatingViewMagnet(@NonNull Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public FloatingViewMagnet(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.mContext = context; init(); } private void init() { mMoveAnimator = new MoveAnimator(); mStatusBarHeight = AppUtils.INSTANCE.getStatusBarHeight(mContext); setClickable(true); } @Override public boolean onTouchEvent(MotionEvent event) { if (event == null) { return false; } switch (event.getAction()) { // 手指按下状态 case MotionEvent.ACTION_DOWN: changeOriginalTouchParams(event); updateSize(); mMoveAnimator.stop(); break; // 手指拖动状态 case MotionEvent.ACTION_MOVE: updateViewPosition(event); break; // 手指抬起状态 case MotionEvent.ACTION_UP: clearPortraitY(); moveToEdge(); if (isOnClickEvent()) { dealClickEvent(); } break; } return true; } protected void dealClickEvent() { if (mMagnetViewListener != null) { mMagnetViewListener.onClick(this); } } protected boolean isOnClickEvent() { return System.currentTimeMillis() - mLastTouchDownTime < TOUCH_TIME_THRESHOLD; } public void moveToEdge() { moveToEdge(isNearestLeft(), false); } private void moveToEdge(boolean isLeft, boolean isLandscape) { float moveDistance = isLeft ? MARGIN_EDGE : mScreenWidth - MARGIN_EDGE; float y = getY(); if (!isLandscape && mPortraitY != 0) { y = mPortraitY; clearPortraitY(); } mMoveAnimator.start(moveDistance, Math.min(Math.max(0, y), mScreenHeight - getHeight())); } protected boolean isNearestLeft() { int middle = mScreenWidth / 2; isNearestLeft = getX() < middle; return isNearestLeft; } private void clearPortraitY() { mPortraitY = 0; } private void updateViewPosition(MotionEvent event) { setX(mOriginalX + event.getRawX() - mOriginalRawX); // 限制不可超出屏幕高度 float desY = mOriginalY + event.getRawY() - mOriginalRawY; if (desY < mStatusBarHeight) { desY = mStatusBarHeight; } if (desY > mScreenHeight - getHeight()) { desY = mScreenHeight - getHeight(); } setY(desY); } private void updateSize() { ViewGroup viewGroup = (ViewGroup) getParent(); if (viewGroup != null) { mScreenWidth = viewGroup.getWidth() - getWidth(); mScreenHeight = viewGroup.getHeight(); } } private void changeOriginalTouchParams(MotionEvent event) { mOriginalX = getX(); mOriginalY = getY(); mOriginalRawX = event.getRawX(); mOriginalRawY = event.getRawY(); mLastTouchDownTime = System.currentTimeMillis(); } public void onRemove() { if (mMagnetViewListener != null) { mMagnetViewListener.onRemove(this); } } public void setMagnetViewListener(MagnetViewListener magnetViewListener) { this.mMagnetViewListener = magnetViewListener; } private class MoveAnimator implements Runnable { private Handler handler = new Handler(Looper.getMainLooper()); private float destinationX; private float destinationY; private long startingTime; void start(float x, float y) { this.destinationX = x; this.destinationY = y; startingTime = System.currentTimeMillis(); handler.post(this); } @Override public void run() { if (getRootView() == null || getRootView().getParent() == null) { return; } float progress = Math.min(1, (System.currentTimeMillis() - startingTime) / 400f); float deltaX = (destinationX - getX()) * progress; float deltaY = (destinationY - getY()) * progress; move(deltaX, deltaY); if (progress < 1) { handler.post(this); } } private void stop() { handler.removeCallbacks(this); } } private void move(float deltaX, float deltaY) { setX(getX() + deltaX); setY(getY() + deltaY); } @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if (getParent() != null) { final boolean isLandscape = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE; markPortraitY(isLandscape); ((ViewGroup)getParent()).post(new Runnable() { @Override public void run() { updateSize(); moveToEdge(isNearestLeft, isLandscape); } }); } } private void markPortraitY(boolean isLandscape) { if (isLandscape) { mPortraitY = getY(); } } } 2.FloatingView

悬浮窗,代码如下(示例):

package com.example.myapplication import android.app.Activity import android.content.Context import android.os.Handler import android.os.Looper import android.view.Gravity import android.view.View import android.view.ViewGroup import android.widget.FrameLayout import android.widget.RelativeLayout import android.R import androidx.activity.ComponentActivity import androidx.core.view.ViewCompat import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.OnLifecycleEvent import java.lang.ref.WeakReference /** * * @author zgp * @data 2022/2/17 */ object FloatingView: BaseFloatingBiew, LifecycleObserver { private var sInstance: FloatingView? = null // 悬浮窗吸附管理类对象 private var mFloatingViewMagnet: FloatingViewMagnet? = null private var mContainer: WeakReference? = null private var mLayoutParams: ViewGroup.LayoutParams = getParams() private var mContext: Context? = null open fun getInstance(context: Context): FloatingView { if (sInstance == null) { synchronized(FloatingView::class.java) { if (sInstance == null) { sInstance = FloatingView mContext = context if (context is ComponentActivity) { (context as ComponentActivity).lifecycle.addObserver(this) } } } } return sInstance!! } override fun remove() { Handler(Looper.getMainLooper()).post(object : Runnable{ override fun run() { if (mFloatingViewMagnet == null) { return } if (ViewCompat.isAttachedToWindow(mFloatingViewMagnet!!) && getContainer() != null) { getContainer()?.removeView(mFloatingViewMagnet) } mFloatingViewMagnet = null } }) if (sInstance != null) { sInstance = null } } private fun ensureFloatingView(context: Context) { synchronized(this) { if (mFloatingViewMagnet != null) { return } mFloatingViewMagnet = EnFloatingView(context) mFloatingViewMagnet?.layoutParams = mLayoutParams // 关闭按钮的点击监听 (mFloatingViewMagnet as EnFloatingView).mCloseBUtton?.setOnClickListener { mFloatingViewMagnet?.onRemove() remove() } addViewToWindow(mFloatingViewMagnet!!) } } @OnLifecycleEvent(Lifecycle.Event.ON_START) fun onStart() { // AppLog.d("FloatingView", "onResume, mContext=$mContext") attach(mContext as Activity) } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) fun onStop() { // AppLog.d("FloatingView", "onStop, mContext=$mContext") detach(mContext as Activity) } /** * 添加显示悬浮窗 */ override fun add(): FloatingView { AppLog.d("FloatingView", "add, mContext=$mContext") ensureFloatingView(mContext!!) return this } /** * 注册悬浮窗 */ override fun attach(activity: Activity): FloatingView { getActivityRoot(activity)?.let { attach(it) } return this } private fun attach(frameLayout: FrameLayout) { if (frameLayout == null || mFloatingViewMagnet == null) { mContainer = WeakReference(frameLayout) return } if (mFloatingViewMagnet!!.parent == frameLayout) { return } if (mFloatingViewMagnet!!.parent != null) { (mFloatingViewMagnet!!.parent as ViewGroup).removeView(mFloatingViewMagnet) } mContainer = WeakReference(frameLayout) frameLayout.addView(mFloatingViewMagnet) } /** * */ override fun detach(activity: Activity): FloatingView { getActivityRoot(activity)?.let { detach(it) } return this } private fun detach(frameLayout: FrameLayout) { if (mFloatingViewMagnet != null && frameLayout != null && ViewCompat.isAttachedToWindow(mFloatingViewMagnet!!)) { frameLayout.removeView(mFloatingViewMagnet) mFloatingViewMagnet = null } if (getContainer() == frameLayout) { mContainer = null } if (sInstance != null) { sInstance = null } } override fun getView(): FloatingViewMagnet? { return mFloatingViewMagnet } override fun customView(viewGroup: FloatingViewMagnet): FloatingView { mFloatingViewMagnet = viewGroup return this } override fun layoutParams(params: ViewGroup.LayoutParams): FloatingView { mLayoutParams = params mFloatingViewMagnet?.layoutParams = params return this } override fun listener(magnetViewListener: MagnetViewListener): FloatingView { mFloatingViewMagnet?.setMagnetViewListener(magnetViewListener) return this } private fun addViewToWindow(view: View) { if (getContainer() == null) { return } getContainer()?.addView(view) } private fun getContainer(): FrameLayout?{ if (mContainer == null) { return null } return mContainer!!.get() } private fun getParams(): FrameLayout.LayoutParams { var params = FrameLayout.LayoutParams( RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT ) params.gravity = Gravity.CENTER_VERTICAL params.setMargins(15, params.topMargin, params.rightMargin, params.bottomMargin) return params } private fun getActivityRoot(activity: Activity): FrameLayout? { try { return activity.window.decorView.findViewById(R.id.content) } catch (e: Exception) { e.printStackTrace() } return null } }

该类我加入了Lifecycle,是为了感知Activity生命周期的变化,不用再手动的去关闭悬浮窗。

 3.MainActivity使用 public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView() { FloatingView.INSTANCE.add().listener(new MagnetViewListener() { @Override public void onRemove(@NotNull FloatingViewMagnet magnetView) { } @Override public void onClick(@NotNull FloatingViewMagnet magnetView) { // 点击监听方法 } }); } }

最后感谢简书的这篇文章对我的启发安卓可拖拽悬浮按钮二,文章里面有吸附的效果,如果项目中需要可以参考下~



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3