阅读 491

自定义控件中drawableStart、drawableEnd属性不生效问题

​ 在项目中遇到一个问题,在自定义的EditTextWithDel控件中,drawableLeft设置正常,但drawableStart无法正常生效,按正常理解在常规的左右布局中,这俩应该效果是一样的。

​ 先说结论:其实是该自定义控件中的一个设置不完善,但如果你在网上拷贝代码的话,大家基本上都未对这个添加特别处理。

关键代码:

setCompoundDrawables(getCompoundDrawables()[0], getCompoundDrawables()[1], rightDrawable, getCompoundDrawables()[3]);
复制代码

解决方案也容易,添加处理如下:

Drawable leftDrawable = null == getCompoundDrawables()[0] ? getCompoundDrawablesRelative()[0] : getCompoundDrawables()[0];
setCompoundDrawables(leftDrawable, getCompoundDrawables()[1], rightDrawable, getCompoundDrawables()[3]);
复制代码

​ 在android sdk版本17之后,安卓新增了supportsRtl属性,在设置-开发者选项中有一个“强制使用从右到左布局方向”的选项,如果supportsRtl属性为true时,可使布局从右到左排列,其主要作用是在国际化项目中,当手机在诸如阿拉伯语、希伯来语等环境中,会自动修改为从右往左的布局。

<Application
     android:label="DT示例demo"
     android:supportsRtl="true">
	<activity />
</Application>
复制代码

​ 由此安卓中新增了诸如marginStart/End、paddingStart/End、drawableStart/End等属性及***其它一些相关设置方法***,而在Android Studio等IDE的默认inspection中也会标黄提示建议用Start、End代替。也正是因此导致本人这种强迫症患者在日常开发中也喜欢用Start/End这种设置方式,即便不是国际化项目。

​ 回到上面提到的问题,我的代码是这样的:

<com.xxx.view.EditTextWithDel
            android:id="@+id/search_et"
            android:layout_width="0dp"
            android:layout_height="36dp"
            android:layout_weight="5"
            android:background="@null"
            android:drawableLeft="@drawable/search_icon"
            android:gravity="center_vertical"
            android:hint="搜索"
            android:imeOptions="actionDone"
            android:singleLine="true"
            android:textColorHint="@color/light_text_color"
            android:textSize="16sp" />
复制代码

按常规理解,drawableLeft和drawableStart应该效果是等同的,但工程运行后的效果是,如果用drawableLeft显示正常,但用drawableStart就无效。

因为EditText是TextView子类,看TextView源码中有以下四个方法(***WithIntrinsicBounds方法类似):

/**
 * ...
 * Calling this method will overwrite any Drawables previously set using
 * {@link #setCompoundDrawablesRelative} or related methods.
*/
public void setCompoundDrawables(@Nullable Drawable left, @Nullable Drawable top,
            @Nullable Drawable right, @Nullable Drawable bottom){
            //...
}//方法1

/**
 * ...
 * Calling this method will overwrite any Drawables previously set using
 * {@link #setCompoundDrawables} or related methods.
*/
public void setCompoundDrawablesRelative(@Nullable Drawable start, @Nullable Drawable top,
            @Nullable Drawable end, @Nullable Drawable bottom) {
            //...
}//方法2

/**
 * Returns drawables for the left, top, right, and bottom borders.
 *
 * @attr ref android.R.styleable#TextView_drawableLeft
 * @attr ref android.R.styleable#TextView_drawableTop
 * @attr ref android.R.styleable#TextView_drawableRight
 * @attr ref android.R.styleable#TextView_drawableBottom
*/
public Drawable[] getCompoundDrawables() {
    //...
}//方法3

/**
 * Returns drawables for the start, top, end, and bottom borders.
 *
 * @attr ref android.R.styleable#TextView_drawableStart
 * @attr ref android.R.styleable#TextView_drawableTop
 * @attr ref android.R.styleable#TextView_drawableEnd
 * @attr ref android.R.styleable#TextView_drawableBottom
*/
public Drawable[] getCompoundDrawablesRelative() {
		//...
}//方法4
复制代码

网上找到的类似自定义EditTextWithDel控件都是上述方法1和方法3的结合,即默认都走left/right这一套。

而通过源码中的注释可知,方法1和方法2可以覆盖彼此。所以单调其中任一个其实没什么影响,但方法3和方法4就不同了

仔细查阅源码可知,在TextView中有一个内部类Drawables,它包含若干个Drawable如: mDrawableTop, mDrawableBottom, mDrawableLeft, mDrawableRight, mDrawableStart, mDrawableEnd, mDrawableError, mDrawableTemp, mDrawableLeftInitial, mDrawableRightInitial等,

getCompoundDrawables()取到的数组是new Drawable[] {dr.mDrawableLeft, dr.mDrawableTop, dr.mDrawableRight, dr.mDrawableBottom}

getCompoundDrawablesRelative()取到的数组是new Drawable[] {dr.mDrawableStart, dr.mDrawableTop, dr.mDrawableEnd, dr.mDrawableBottom}

所以getCompoundDrawables()只能取到left位的drawable,这也是drawableStart未生效的原因,实际上

setCompoundDrawables(getCompoundDrawables()[0], getCompoundDrawables()[1], rightDrawable, getCompoundDrawables()[3]);方法中第一个参数取到的是null,将drawableStart设置的图片替换掉了,所以运行结果是drawableStart看似未生效。

那么为什么正常设置drawableStart属性没有问题呢?查源码可知,在TextView的onResolveDrawables周期方法中有调用Drawables的**resolveWithLayoutDirection()**方法,正是在这个方法中将mDrawableStart(!=null)赋值给mDrawableLeft的。

文章分类
Android
文章标签