Android中如何根据View焦点状态动态修改内边距?
问题
我有一个RelativeLayout布局button_layout.xml,通过代码给它设置了outline.xml作为背景Selector Drawable。预期效果是:View获取焦点时显示2dp白色边框,同时设置2dp内边距;失焦时隐藏边框,内边距设为0dp。但现在失焦后边框消失了,内边距却还是保持2dp,即使在失焦状态的item里配置了0dp内边距也无效。请问怎么实现根据焦点状态动态修改内边距?
布局代码
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/kiosk_tile_button" android:layout_width="0dp" android:layout_height="0dp" android:orientation="vertical" android:gravity="bottom|center_horizontal" > <ImageView android:id="@+id/kiosk_image" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </RelativeLayout>
代码片段
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater buttonLayout = inflater.inflate(R.layout.button_layout, null, false) ... buttonLayout.setBackgroundResource(R.drawable.outline)
原outline.xml代码
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_focused="true"> <shape android:shape="rectangle"> <stroke android:width="2dp" android:color="@color/white" /> <padding android:left="2dp" android:top="2dp" android:right="2dp" android:bottom="2dp"/> </shape> </item> <item android:state_focused="false"> <shape android:shape="rectangle"> <stroke android:width="0dp" android:color="@color/white"/> <padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp"/> </shape> </item> </selector>
解决方案
Android的Drawable Selector里的<padding>只会在Drawable首次绑定到View时计算一次,后续状态切换不会自动更新View的实际内边距,这就是问题所在。这里提供两种可行的解决方法:
方法一:用StateListAnimator动态控制内边距
通过StateListAnimator配合Animator资源,在焦点状态变化时直接修改View的内边距,同时保留Selector的边框效果:
- 创建
animator/focus_padding_animator.xml:
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_focused="true"> <objectAnimator android:propertyName="padding" android:valueFrom="0dp" android:valueTo="2dp" android:duration="0" android:valueType="intType"/> </item> <item> <objectAnimator android:propertyName="padding" android:valueFrom="2dp" android:valueTo="0dp" android:duration="0" android:valueType="intType"/> </item> </selector>
- 修改
button_layout.xml,给RelativeLayout添加android:stateListAnimator属性:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/kiosk_tile_button" android:layout_width="0dp" android:layout_height="0dp" android:orientation="vertical" android:gravity="bottom|center_horizontal" android:stateListAnimator="@animator/focus_padding_animator"> <ImageView android:id="@+id/kiosk_image" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </RelativeLayout>
- 简化
outline.xml,移除所有<padding>配置,只保留边框逻辑:
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_focused="true"> <shape android:shape="rectangle"> <stroke android:width="2dp" android:color="@color/white" /> </shape> </item> <item> <shape android:shape="rectangle"> <stroke android:width="0dp" android:color="@color/white"/> </shape> </item> </selector>
方法二:通过焦点监听手动设置内边距
在代码中给View添加焦点变化监听器,根据焦点状态直接设置内边距:
修改你的代码片段,添加监听逻辑:
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater buttonLayout = inflater.inflate(R.layout.button_layout, null, false) buttonLayout.setBackgroundResource(R.drawable.outline) // 添加焦点变化监听 buttonLayout.setOnFocusChangeListener { view, hasFocus -> val padding = if (hasFocus) dpToPx(2) else 0 view.setPadding(padding, padding, padding, padding) } // 辅助函数:dp转像素 private fun dpToPx(dp: Int): Int { return (dp * Resources.getSystem().displayMetrics.density).toInt() }
同样需要简化outline.xml,移除所有<padding>标签,只保留边框的状态切换逻辑。
内容的提问来源于stack exchange,提问作者CrazyCSGuy
相关产品推荐
相关产品推荐

