Android 进阶学习(三十五) Android 中给Vi
问题回顾
在上一篇博客 Android 进阶学习(二十四) Android 中给View 添加Drawable的思考 的结尾处 ,我说出了一个问题,那就是我自己写的drawable 缓存,缓存的是drawable 本身,这就使得不同的View 在使用相同的drawable时,drawable 不适配的情况,比如 RecyclerView 的item 是一个 水平自动填充的item , 如果上一个使用这个drawable 的item 宽度比当前这个item的宽度宽,就会导致当前这个item 的背景drawable 的宽度超过了item的宽度,出现不适配的情况,那么这种情况 源码是如何解决的呢,
@Nullable
Drawable loadDrawable(@NonNull Resources wrapper, @NonNull TypedValue value, int id,
int density, @Nullable Resources.Theme theme)
throws NotFoundException {
...省略部分代码
if (!mPreloading && useCache) {
这里就是获取缓存的drawable
final Drawable cachedDrawable = caches.getInstance(key, wrapper, theme);
if (cachedDrawable != null) {
cachedDrawable.setChangingConfigurations(value.changingConfigurations);
return cachedDrawable;
}
}
...省略部分代
}
}
class DrawableCache extends ThemedResourceCache<Drawable.ConstantState> {
@UnsupportedAppUsage
public Drawable getInstance(long key, Resources resources, Resources.Theme theme) {
final Drawable.ConstantState entry = get(key, theme);
if (entry != null) {
每次都是利用 Drawable.ConstantState 重新创建一个drawable
return entry.newDrawable(resources, theme);
}
return null;
}
}
从源码这里我们可以看到,即使使用的是同样的drawable.xml 文件,每个view 的drawable 都是单独创建的,只不过这个模板只创建了一次,所以我们的缓存也需要缓存 Drawable.ConstantState ,每次获取后,重新创建一个新的Drawable
Drawable 组件化
我们app 在探究组件化的过程中关于drawable 的问题也遇到了不少, 由于组件化开发的过程是先有的app,我们再对app进行组件化拆分,在组件化的初期,很多资源文件都是到处copy 的,虽然我们给组件添加了 resourcePrefix 这个属性,为每个组件的xml 资源命名做了限制,还是造成了不少打最终包成功后样式对不上的问题,而且随着项目越来越大,新写一个drawable 所耗费的时间,比找到一个相同属性的drawable 的xml文件还要快一点,这样就造成了恶性循环,大家就不停的创建新的drawable.xml 文件,找到相同属性的就更难了...
伴随着这样的思考,能不能找到一个摆脱xml来设置背景drawable 的方法就这样诞生了,但是这个过程也遇到了好几个问题
1.也就是上面我们所说的 drawable 缓存的问题,通过缓存 Drawable.ConstantState 每次获取都重新创建drawable 这个问题已经解决了
2.我在最开始的时候为整个项目重新定义了一套 DrawableView ,比如 DrawableTextView , DrawableLinearLayout , DrawableRelativeLayout 等等, 在为每个View 添加属性的时候,就产生了一个问题,那就是
<declare-styleable name="DrawableRelativeLayout">
<attr name="rl_zhome_end_color" format="color"/>
<attr name="rl_zhome_bottom_line" format="boolean"/>
</declare-styleable>
<declare-styleable name="DrawableLinearLayout">
<attr name="ll_zhome_end_color" format="color"/>
<attr name="ll_zhome_bottom_line" format="boolean"/>
</declare-styleable>
每一个View 在使用 xml 添加drawable 属性的时候,他们属性命名是不同的,LinearLayout 的属性就是 app:ll_zhome_color=""
RelativeLayout 的属性就是 app:rl_zhome_color="",这样就导致了不了解这个机制的人,如果只是copy属性,就会导致使用不了这个问题,那么能不能像 ConstraintLayout 一样, 所有view 的属性名都一致,并且android studio 还会自动提示出属性的名称呢,
这个问题的解决方案自己想一下其实也特别简单,那就是事先将属性声明出来,在自定义View 的 declare-styleable 引用这个属性就可以了,举个栗子
<resources>
先把这个 属性在 resources 标签下声明出来
<attr name="zhome_div_color" format="color"/>
<declare-styleable name="DrawableRelativeLayout">
<attr name="zhome_div_color" />
</declare-styleable>
<declare-styleable name="DrawableLinearLayout">
<attr name="zhome_div_color" />
</declare-styleable>
<resources>
这样就可以了,虽然在获取属性的时候是根据不同的View 来获取的,但是在编写 layout.xml 的过程中, 属性的命名是一致的,
<com.tsm.tsmbottomsheetdialog.drawable.ZHomeLinearLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center_vertical"
android:paddingHorizontal="16dp"
android:orientation="horizontal"
app:zhome_div_bottom_line="true"
app:zhome_div_bottom_line_padding="16dp">
<com.tsm.tsmbottomsheetdialog.drawable.ZHomeTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/color_ct1_85"
android:text="ZHomeLinearLayout 下划线"
app:zhome_div_bottom_line="true"
app:zhome_div_bottom_line_padding="16dp"/>
</com.tsm.tsmbottomsheetdialog.drawable.ZHomeLinearLayout>
插个图
动画.gif
下面就是我们在使用过程中经常遇到的属性,我把它们每个属性都标注了出来,方便使用的人来查看并使用相关的属性,
<!-- 选中方式 nor 没有 select->state_Selected checked->state_Checked enable->state_Enable pressed->state_Pressed -->
<attr name="zhome_div_selector">
<enum name="nor" value="0" />
<enum name="select" value="1" />
<enum name="checked" value="2" />
<enum name="enable" value="3" />
<enum name="pressed" value="4" />
</attr>
<!-- 设置描边 描边宽度 虚线等属性选中与未选中状态公用 -->
<!-- 未选中 描边颜色-->
<attr name="zhome_div_stroken_color" format="color"/>
<!-- 选中 描边颜色-->
<attr name="zhome_div_select_state_stroken_color" format="color"/>
<!-- 未选中与选中 描边宽度-->
<attr name="zhome_div_stroken_width" format="dimension"/>
<!-- 未选中与选中 间隔-->
<attr name="zhome_div_stroken_dashgap" format="dimension"/>
<!-- 未选中与选中 虚线宽度-->
<attr name="zhome_div_stroken_dashwidth" format="dimension"/>
<!-- 背景色-->
<!-- 未选中 颜色-->
<attr name="zhome_div_color" format="color"/>
<!-- 选中 颜色-->
<attr name="zhome_div_select_state_color" format="color"/>
<!-- 圆角 选中圆角与未选中圆角相同-->
<attr name="zhome_div_radius" format="dimension"/>
<!-- 左上圆角-->
<attr name="zhome_div_top_left_radius" format="dimension"/>
<!-- 右上圆角-->
<attr name="zhome_div_top_right_radius" format="dimension"/>
<!-- 左下圆角-->
<attr name="zhome_div_bottom_left_radius" format="dimension"/>
<!-- 右下圆角-->
<attr name="zhome_div_bottom_right_radius" format="dimension"/>
<!-- 顶部圆角 左上 右上-->
<attr name="zhome_div_top_radius" format="dimension"/>
<!-- 底部圆角 左下 右下-->
<attr name="zhome_div_bottom_radius" format="dimension"/>
<!-- 渐变色-->
<!-- 渐变色角度-->
<attr name="zhome_div_angle" format="integer"/>
<!-- 渐变色开始颜色-->
<attr name="zhome_div_start_color" format="color"/>
<!-- 渐变色中间颜色-->
<attr name="zhome_div_center_color" format="color"/>
<!-- 渐变色结束颜色-->
<attr name="zhome_div_end_color" format="color"/>
<!-- 底部横线-->
<attr name="zhome_div_bottom_line" format="boolean"/>
<!-- 底部横线边距 部分UI 可能会出现底部横线距离两端存在一定间距-->
<attr name="zhome_div_bottom_line_padding" format="dimension"/>
<!-- 水波纹效果 由于有版本限制,这里使用前景色实现-->
<attr name="zhome_div_ripple" format="boolean"/>
<!-- 点击缩放效果-->
<attr name="zhome_div_pressed_scale" format="boolean"/>
到了这里整个过程就告一段落了,这个一套drawable 在我们的组件里面已经试用了很长一段时间了,并没有出现什么问题,下面我方一下Github 地址 TsmBottomSheetDialog,这里就能找到相关代码