移动ExtendedFloatingActionButton至左侧时视图缺失问题求助
我在使用ExtendedFloatingActionButton实现屏幕可移动功能时,发现将按钮拖动到屏幕左侧时,按钮视图会出现缺失情况。
右侧正常显示效果:

左侧视图缺失效果:

XML布局文件
<FrameLayout 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:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" app:icon="@drawable/ic_baseline_4g_mobiledata_24" android:text="" app:iconTint="@color/white" android:textColor="@color/white" app:backgroundTint="@color/black" app:iconGravity="end" android:textAllCaps="false" android:layout_gravity="center|end" android:layout_marginBottom="20dp" android:layout_marginEnd="20dp"/> </FrameLayout>
MainActivity代码
class MainActivity : AppCompatActivity(), View.OnTouchListener { lateinit var fab: ExtendedFloatingActionButton private val CLICK_DRAG_TOLERANCE = 10f // Often, there will be a slight, unintentional, drag when the user taps the FAB, so we need to account for this. private var downRawX = 0f private var downRawY:kotlin.Float = 0f private var dX = 0f private var dY:kotlin.Float = 0f override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val view: View = findViewById(R.id.fab); view.setOnTouchListener(this); fab = findViewById(R.id.fab) fab.shrink() fab.setOnClickListener { if (!fab.isExtended) { fab.extend() fab.text = "Boost: Off \nPing: 99 Jitter: 15" fab.maxLines = 2 fab.background = applicationContext.getDrawable(R.drawable.fab_back) } else { fab.shrink() fab.background = applicationContext.getDrawable(R.drawable.circle_fab) } } } override fun onTouch(view: View?, motionEvent: MotionEvent): Boolean { val action = motionEvent.action return if (action == MotionEvent.ACTION_DOWN) { downRawX = motionEvent.rawX downRawY = motionEvent.rawY dX = view!!.x - downRawX dY = view!!.y - downRawY true // Consumed } else if (action == MotionEvent.ACTION_MOVE) { val viewWidth = view!!.width val viewHeight = view!!.height val viewParent = view!!.parent as View val parentWidth = viewParent.width val parentHeight = viewParent.height var newX = motionEvent.rawX + dX newX = Math.max(0f, newX) // Don't allow the FAB past the left hand side of the parent newX = Math.min( (parentWidth - viewWidth).toFloat(), newX) if (newX < 1.0) { fab.iconGravity = MaterialButton.ICON_GRAVITY_START } else { fab.iconGravity = MaterialButton.ICON_GRAVITY_END } Log.e("error", newX.toString()) // Don't allow the FAB past the right hand side of the parent var newY = motionEvent.rawY + dY newY = Math.max(0f, newY) // Don't allow the FAB past the top of the parent newY = Math.min( (parentHeight - viewHeight).toFloat(), newY ) // Don't allow the FAB past the bottom of the parent view!!.animate() .x(newX) .y(newY) .setDuration(0) .start() true // Consumed } else if (action == MotionEvent.ACTION_UP) { val upRawX = motionEvent.rawX val upRawY = motionEvent.rawY val upDX = upRawX - downRawX val upDY = upRawY - downRawY if (Math.abs(upDX) < CLICK_DRAG_TOLERANCE && Math.abs(upDY) < CLICK_DRAG_TOLERANCE) { // A click performClick() } else { // A drag true // Consumed } } else { super.onTouchEvent(motionEvent) } } private fun performClick(): Boolean { if (!fab.isExtended) { fab.extend() fab.text = "Boost: Off \nPing: 99 Jitter: 15" fab.maxLines = 2 fab.background = applicationContext.getDrawable(R.drawable.fab_back) } else { fab.shrink() fab.background = applicationContext.getDrawable(R.drawable.circle_fab) } return true } }
问题原因及解决方案
原因
- 边界计算未考虑视图内边距:ExtendedFloatingActionButton内部有默认padding,直接将
newX限制为0f会让按钮贴紧父容器左边缘,导致部分内容超出可视范围被截断。 - 图标重力切换阈值不合理:当前用
newX < 1.0作为切换条件,阈值过小,按钮未移动到合理位置就触发布局变更,引发显示异常。 - 手动替换背景破坏默认布局逻辑:直接修改
fab.background会覆盖ExtendedFloatingActionButton的默认样式,导致布局计算出错。
解决方案
1. 调整边界计算,添加安全边距
在ACTION_MOVE中修改newX的最小值限制,给左侧保留足够边距:
// 先在res/values/dimens.xml中定义<dimen name="fab_safe_margin">16dp</dimen> val safeMargin = resources.getDimensionPixelSize(R.dimen.fab_safe_margin).toFloat() newX = Math.max(safeMargin, newX)
2. 优化图标重力切换阈值
将切换条件改为基于屏幕中线判断,逻辑更合理:
if (newX < parentWidth / 2f) { fab.iconGravity = MaterialButton.ICON_GRAVITY_START } else { fab.iconGravity = MaterialButton.ICON_GRAVITY_END }
3. 用背景色 tint 替代直接替换背景
保留控件默认布局逻辑,通过backgroundTint修改背景:
// 替换原点击事件中的background设置代码 if (!fab.isExtended) { fab.extend() fab.text = "Boost: Off \nPing: 99 Jitter: 15" fab.maxLines = 2 fab.backgroundTint = ContextCompat.getColorStateList(this, R.color.fab_extended_bg) } else { fab.shrink() fab.backgroundTint = ContextCompat.getColorStateList(this, R.color.black) }
在res/values/colors.xml中添加对应颜色定义。
内容的提问来源于stack exchange,提问作者Abdul Rehman Jamil
相关产品推荐
相关产品推荐

