Android应用内悬浮窗Activity的简单实现
悬浮窗相信大家应该都不陌生,下面这篇文章主要给大家介绍了关于Android应用内悬浮窗Activity简单实现的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
目录
前言
缩放方法
悬浮样式
点击穿透空白
移动悬浮窗
例子的完整代码
styles.xml
layout
activity
运行效果
小结
前言
悬浮窗是一种比较常见的需求。例如把视频通话界面缩小成一个悬浮窗,然后用户可以在其他界面上处理事情。
本文给出一个简单的应用内悬浮窗实现。可缩小activity和还原大小。可悬浮在同一个app的其他activity上。使用TouchListener监听触摸事件,拖动悬浮窗。
缩放方法
缩放activity需要使用WindowManager.LayoutParams,控制window的宽高
在activity中调用
1 2 3 4 5 | android.view.WindowManager.LayoutParams p = getWindow().getAttributes(); p.height = 480 ; // 高度 p.width = 360 ; // 宽度 p.dimAmount = 0 .0f; // 不让下面的界面变暗 getWindow().setAttributes(p); |
dim: adj. 暗淡的; 昏暗的; 微弱的; 不明亮的; 光线暗淡的; v. (使)变暗淡,变微弱,变昏暗; (使)减弱,变淡漠,失去光泽;
修改了WindowManager.LayoutParams的宽高,activity的window大小会发生变化。
要变回默认大小,在activity中调用
1 | getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); |
如果缩小时改变了位置,需要把window的位置置为0
1 2 3 4 | WindowManager.LayoutParams lp = getWindow().getAttributes(); lp.x = 0 ; lp.y = 0 ; getWindow().setAttributes(lp); |
activity变小时,后面可能是黑色的背景。这需要进行下面的操作。
悬浮样式
在styles.xml里新建一个MeTranslucentAct。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <resources> <!-- Base application theme. --> <style name= "AppTheme" parent= "Theme.AppCompat.Light.DarkActionBar" > <!-- Customize your theme here. --> <item name= "colorPrimary" > @color /colorPrimary</item> <item name= "colorPrimaryDark" > @color /colorPrimaryDark</item> <item name= "colorAccent" > @color /colorAccent</item> <item name= "windowNoTitle" > true </item> </style> <style name= "TranslucentAct" parent= "AppTheme" > <item name= "android:windowBackground" ># 80000000 </item> <item name= "android:windowIsTranslucent" > true </item> <item name= "android:windowAnimationStyle" > @android :style/Animation.Translucent</item> </style> </resources> |
主要style是AppCompat的。
指定一个window的背景android:windowBackground
使用的Activity继承自androidx.appcompat.app.AppCompatActivity
activity缩小后,背景是透明的,可以看到后面的其他页面
点击穿透空白
activity缩小后,点击旁边空白处,其他组件能接到点击事件
在onCreate
方法的setContentView
之前,给WindowManager.LayoutParams添加标记FLAG_LAYOUT_NO_LIMITS
和FLAG_NOT_TOUCH_MODAL
1 2 3 4 5 | WindowManager.LayoutParams layoutParams = getWindow().getAttributes(); layoutParams.flags = WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; mBinding = DataBindingUtil.setContentView( this , R.layout.act_float_scale); |
移动悬浮窗
监听触摸事件,计算出手指移动的距离,然后移动悬浮窗。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | private boolean mIsSmall = false ; // 当前是否小窗口 private float mLastTx = 0 ; // 手指的上一个位置x private float mLastTy = 0 ; // .... mBinding.root.setOnTouchListener((v, event) -> { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.d(TAG, "down " + event); mLastTx = event.getRawX(); mLastTy = event.getRawY(); return true ; case MotionEvent.ACTION_MOVE: Log.d(TAG, "move " + event); float dx = event.getRawX() - mLastTx; float dy = event.getRawY() - mLastTy; mLastTx = event.getRawX(); mLastTy = event.getRawY(); Log.d(TAG, " dx: " + dx + ", dy: " + dy); if (mIsSmall) { WindowManager.LayoutParams lp = getWindow().getAttributes(); lp.x += dx; lp.y += dy; getWindow().setAttributes(lp); } break ; case MotionEvent.ACTION_UP: Log.d(TAG, "up " + event); return true ; case MotionEvent.ACTION_CANCEL: Log.d(TAG, "cancel " + event); return true ; } return false ; }); |
mIsSmall
用来记录当前activity是否变小(悬浮)。
在触摸监听器中返回true,表示消费这个触摸事件。
event.getX()
和event.getY()
获取到的是当前View的触摸坐标。 event.getRawX()
和event.getRawY()
获取到的是屏幕的触摸坐标。即触摸点在屏幕上的位置。
例子的完整代码
启用了databinding
1 2 3 4 5 | android { dataBinding { enabled = true } } |
styles.xml
新建一个样式
1 2 3 4 5 | < style name = "TranslucentAct" parent = "AppTheme" > < item name = "android:windowBackground" >#80000000</ item > < item name = "android:windowIsTranslucent" >true</ item > < item name = "android:windowAnimationStyle" >@android:style/Animation.Translucent</ item > </ style > |
layout
act_float_scale.xml里面放一些按钮,控制放大和缩小。 ConstraintLayout拿来监听触摸事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <?xml version= "1.0" encoding= "utf-8" ?> <layout xmlns:android= "http://schemas.android.com/apk/res/android" xmlns:app= "http://schemas.android.com/apk/res-auto" > <androidx.constraintlayout.widget.ConstraintLayout android:id= "@+id/root" android:layout_width= "match_parent" android:layout_height= "match_parent" android:background= "#555555" > <LinearLayout android:layout_width= "match_parent" android:layout_height= "wrap_content" android:gravity= "center" android:orientation= "vertical" app:layout_constraintTop_toTopOf= "parent" > <Button android:id= "@+id/to_small" style= "@style/NormalBtn" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:text= "变小" /> <Button android:id= "@+id/to_reset" style= "@style/NormalBtn" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_marginTop= "12dp" android:text= "还原" /> </LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout> </layout> |
activity
FloatingScaleAct
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | import android.os.Bundle; import android.util.Log; import android.view.Display; import android.view.MotionEvent; import android.view.ViewGroup; import android.view.WindowManager; import androidx.appcompat.app.AppCompatActivity; import androidx.databinding.DataBindingUtil; import com.rustfisher.tutorial2020.R; import com.rustfisher.tutorial2020.databinding.ActFloatScaleBinding; public class FloatingScaleAct extends AppCompatActivity { private static final String TAG = "rfDevFloatingAct" ; ActFloatScaleBinding mBinding; private boolean mIsSmall = false ; // 当前是否小窗口 private float mLastTx = 0 ; // 手指的上一个位置 private float mLastTy = 0 ; @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); WindowManager.LayoutParams layoutParams = getWindow().getAttributes(); layoutParams.flags = WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; mBinding = DataBindingUtil.setContentView( this , R.layout.act_float_scale); mBinding.toSmall.setOnClickListener(v -> toSmall()); mBinding.toReset.setOnClickListener(v -> { WindowManager.LayoutParams lp = getWindow().getAttributes(); lp.x = 0 ; lp.y = 0 ; getWindow().setAttributes(lp); getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); mIsSmall = false ; }); mBinding.root.setOnTouchListener((v, event) -> { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.d(TAG, "down " + event); mLastTx = event.getRawX(); mLastTy = event.getRawY(); return true ; case MotionEvent.ACTION_MOVE: Log.d(TAG, "move " + event); float dx = event.getRawX() - mLastTx; float dy = event.getRawY() - mLastTy; mLastTx = event.getRawX(); mLastTy = event.getRawY(); Log.d(TAG, " dx: " + dx + ", dy: " + dy); if (mIsSmall) { WindowManager.LayoutParams lp = getWindow().getAttributes(); lp.x += dx; lp.y += dy; getWindow().setAttributes(lp); } break ; case MotionEvent.ACTION_UP: Log.d(TAG, "up " + event); return true ; case MotionEvent.ACTION_CANCEL: Log.d(TAG, "cancel " + event); return true ; } return false ; }); } private void toSmall() { mIsSmall = true ; WindowManager m = getWindowManager(); Display d = m.getDefaultDisplay(); WindowManager.LayoutParams p = getWindow().getAttributes(); p.height = ( int ) (d.getHeight() * 0.35 ); p.width = ( int ) (d.getWidth() * 0.4 ); p.dimAmount = 0 .0f; getWindow().setAttributes(p); } } |
manifest里注册这个activity
1 2 3 | <activity android:name= ".act.FloatingScaleAct" android:theme= "@style/TranslucentAct" /> |
运行效果
在红米9A(Android 10,MIUI 12.5.1 稳定版)和荣耀(Android 5.1)上运行OK
小结
为实现悬浮窗效果,思路是改变activity大小,将activity所在window的背景设置透明,监听触摸事件改变window的位置。 主要使用的类 WindowManager.LayoutParams
到此这篇关于Android应用内悬浮窗Activity实现的文章就介绍到这了
原文链接:https://www.an.rustfisher.com/android/activity/floating-scale-act/