Android自定义LinearLayout控件边框布局切换问题排查与实现
正确实现方案
1. 确保EditText的初始化时机正确
ValidLayout不要在构造方法里添加EditText,而是在onFinishInflate()方法中执行——这个方法会在布局inflation完成后调用,此时View树已构建完成,添加子View更可靠:
public class ValidLayout extends LinearLayout { protected EditText mEditText; // 三个构造方法统一调用init() public ValidLayout(Context context) { super(context); init(); } public ValidLayout(Context context, AttributeSet attrs) { super(context, attrs); init(); } public ValidLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { // 仅做基础初始化,不添加子View setOrientation(LinearLayout.HORIZONTAL); } @Override protected void onFinishInflate() { super.onFinishInflate(); // 布局加载完成后添加EditText mEditText = new EditText(getContext()); LayoutParams params = new LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT ); // 按需设置padding、margin等属性 mEditText.setLayoutParams(params); addView(mEditText); } }
2. 正确解析自定义属性并延迟切换边框
在SupplementEditText中,解析属性后不要立刻调用setBorderSide,而是用post()延迟到View初始化完成后执行,避免EditText为空:
public class SupplementEditText extends ValidLayout { public enum BorderSide { LEFT, RIGHT } private BorderSide mBorderSide = BorderSide.RIGHT; // 默认右侧边框 public SupplementEditText(Context context) { super(context); init(null); } public SupplementEditText(Context context, AttributeSet attrs) { super(context, attrs); init(attrs); } public SupplementEditText(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(attrs); } private void init(AttributeSet attrs) { if (attrs != null) { TypedArray ta = getContext().obtainStyledAttributes( attrs, R.styleable.SupplementEditText ); // 解析枚举值,对应attrs.xml中的配置 int sideIndex = ta.getInt( R.styleable.SupplementEditText_borderSide, 1 // 默认对应RIGHT,与attrs.xml中的value=1匹配 ); mBorderSide = BorderSide.values()[sideIndex]; ta.recycle(); } // 延迟执行,确保EditText已被添加 post(() -> setBorderSide(mBorderSide)); } public void setBorderSide(BorderSide borderSide) { mBorderSide = borderSide; if (mEditText == null) { Log.w("SupplementEditText", "EditText尚未初始化,无法设置边框"); return; } Drawable bgDrawable; if (borderSide == BorderSide.RIGHT) { bgDrawable = ContextCompat.getDrawable( getContext(), R.drawable.supplemental_edittext ); } else { bgDrawable = ContextCompat.getDrawable( getContext(), R.drawable.supplemental_edittext_left ); } // 兼容不同Android版本的背景设置 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { mEditText.setBackground(bgDrawable); } else { mEditText.setBackgroundDrawable(bgDrawable); } // 触发重绘和布局更新 mEditText.invalidate(); requestLayout(); } }
3. 确保自定义属性配置正确
在res/values/attrs.xml中,枚举值的顺序要与代码中的BorderSide枚举完全对应:
<declare-styleable name="SupplementEditText"> <attr name="borderSide" format="enum"> <enum name="left" value="0"/> <enum name="right" value="1"/> </attr> </declare-styleable>
4. 验证Drawable文件的边框方向
右侧边框的supplemental_edittext.xml示例:
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_focused="true"> <shape android:shape="rectangle"> <solid android:color="@android:color/white"/> <stroke android:width="2dp" android:color="@color/your_blue_color"/> <corners android:topRightRadius="16dp" android:bottomRightRadius="16dp"/> </shape> </item> <item> <shape android:shape="rectangle"> <solid android:color="@android:color/white"/> <stroke android:width="1dp" android:color="@color/your_gray_color"/> <corners android:topRightRadius="16dp" android:bottomRightRadius="16dp"/> </shape> </item> </selector>
左侧边框的supplemental_edittext_left.xml只需将圆角设置在左侧:
<corners android:topLeftRadius="16dp" android:bottomLeftRadius="16dp"/>
内容的提问来源于stack exchange,提问作者Nodoid




