持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第17天,点击查看活动详情
动态设置Drawable与Tint
本文讲一下ImageView设置Drawable设置tint改变颜色,以及代码中动态的给Drawable设置Tint, 以及 TextView 动态的设置 Drawable,以及它们的结合 给TextView 动态设置一个动态设置了Tint的Drawable。
有点绕,看看Demo示例,很简单。
一、Tint的使用
项目的图片如果是一样的,只是颜色不同,我们可以使用tint来改变颜色。可以减少Apk的大小
<ImageView
android:layout_width="@dimen/d_28dp"
android:layout_height="@dimen/d_28dp"
android:src="@mipmap/jobs_tips_date_icon"
app:tint="@color/colorAccent" />
这样显示的图片就变成了指定的红色了。
这里高版本只能使用 app 前缀, 不支持android 前缀了。也是为了兼容低版本。用法没有改变。
1.1 动态的设置Tint
动态的在 Activity 代码中设置 Tint 如何使用:
获取到Drawable,然后给Drawable设置Tint颜色即可
getDrawable(R.mipmap.jobs_tips_date_icon)?.let {
DrawableCompat.setTint(it, Color.GREEN)
mBinding.ivTint.setImageDrawable(it)
}
但是如果我想要状态切换颜色怎么办?未选中是橙色,选中是蓝色,你这个不能设置drawable-selector啊。
1.2 动态的设置TintList
我们可以使用color-selector来实现状态的切换:
getDrawable(R.mipmap.jobs_tips_date_icon)?.let {
DrawableCompat.setTintList(it, resources.getColorStateList(R.color.full_tab_color_selector))
mBinding.ivTint2.setImageDrawable(it)
}
mBinding.ivTint2.click { it.isSelected = true }
注意这里的方法是 setTintList 设置的是颜色组了。
我们看看效果:
可以看到第三个图片是橙色的,点击切换状态即可变为蓝色
很简单的就能实现切换,如果图片本身没有变化只是颜色的变化,我们可以使用这种方法,少导入一些图片,对内存也有一定的优化。
二、Left / Right Drawable的使用
<TextView
android:id="@+id/tv_center_text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@color/divider_color"
android:drawablePadding="@dimen/d_5dp"
android:drawableLeft="@mipmap/jobs_tips_date_icon"
android:gravity="center"
android:text="我是居中文本" />
这个在xml中设置,相信大家都会使用了,在代码中使用也很简单:
2.1 动态的设置setCompoundDrawables
如下,如果需要置顶我们就设置一个置顶icon,如果无需置顶,我们全部清除即可。
if (item.topping == 1) {
Drawable drawable = CommUtils.getDrawable(R.drawable.job_featured);
drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight());
tv_job_title.setCompoundDrawables(null, null, drawable, null);
} else {
tv_job_title.setCompoundDrawables(null, null, null, null);
}
如果我们不使用宽度自适应,用 padding 撑开宽度。 而是固定宽度或者matchParent,我们设置drawable则是这样的效果:
2.2 leftDrawable居中
这种情况下如果我们想要图片与文本居中显示,我们就需要使用自定义View的方式:
public class LeftDrawableCenterTextView extends AppCompatTextView {
public LeftDrawableCenterTextView(Context context) {
super(context);
}
public LeftDrawableCenterTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LeftDrawableCenterTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
Drawable[] drawables = getCompoundDrawables();
Drawable drawableLeft = drawables[0];
if (drawableLeft != null) {
int drawableWith = drawableLeft.getIntrinsicWidth();
int drawablePadding = getCompoundDrawablePadding();
int textWith = (int) getPaint().measureText(getText().toString());
int bodyWith = drawableWith + drawablePadding + textWith;
canvas.translate((getWidth() - bodyWith) / 2, 0);
}
super.onDraw(canvas);
}
}
我们修改xml指定为我们的textView
<xxx.LeftDrawableCenterTextView
android:id="@+id/tv_center_text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@color/divider_color"
android:drawablePadding="@dimen/d_5dp"
android:gravity="center"
android:text="我是居中文本" />
我们动态的设置或者在xml里设置drawable都是可以的:
//手动的设置leftDrawable
ContextCompat.getDrawable(this, R.mipmap.jobs_tips_date_icon)?.let {
it.setBounds(0, 0, it.minimumWidth, it.minimumHeight)
mBinding.tvCenterText.apply {
setCompoundDrawables(it, null, null, null)
gravity = Gravity.CENTER_VERTICAL
}
}
效果:
2.3 rightDrawable居中
看到自定义View的名字就知道,专门为 leftDrawable 设置的。如果是rightDrawable 那么计算的方式会有所不同:
public class RightDrawableCenterTextView extends AppCompatTextView {
public RightDrawableCenterTextView(Context context) {
super(context);
}
public RightDrawableCenterTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RightDrawableCenterTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
Drawable[] drawables = getCompoundDrawables();
Drawable drawable = drawables[2];
if (drawable != null) {
float textWidth = getPaint().measureText(getText().toString());
int drawablePadding = getCompoundDrawablePadding();
int drawableWidth = drawable.getMinimumWidth();
float bodyWidth = textWidth + drawableWidth + drawablePadding + getPaddingLeft() + getPaddingRight();
canvas.translate((bodyWidth - getWidth()) / 2, 0);
}
super.onDraw(canvas);
}
}
同样的用法,我们可以在java或者在xml里设置drawable都是可以的:
//手动的设置rightDrawable
ContextCompat.getDrawable(this, R.mipmap.jobs_tips_date_icon)?.let {
it.setBounds(0, 0, it.minimumWidth, it.minimumHeight)
mBinding.tvCenterText.apply {
setCompoundDrawables(null, null, it, null)
gravity = Gravity.CENTER_VERTICAL or Gravity.RIGHT
}
}
效果:
三、动态Drawable + 动态Tint
我们可以看到动态在Java代码中给 TextView 设置了 drawable 对象,那么在drawable 对象赋值给 TextView 之前,我们是不是可以对drawable设置tint呢?
试试看效果,代码如下:
//手动的设置rightDrawable
ContextCompat.getDrawable(this, R.mipmap.jobs_tips_date_icon)?.let {
it.setBounds(0, 0, it.minimumWidth, it.minimumHeight)
DrawableCompat.setTint(it, Color.GREEN)
mBinding.tvCenterText.apply {
setCompoundDrawables(null, null, it, null)
gravity = Gravity.CENTER_VERTICAL or Gravity.RIGHT
}
}
可以看到效果确实是变绿色了:
设置颜色状态选择器能不能生效?
//手动的设置rightDrawable
ContextCompat.getDrawable(this, R.mipmap.jobs_tips_date_icon)?.let {
it.setBounds(0, 0, it.minimumWidth, it.minimumHeight)
DrawableCompat.setTintList(it, resources.getColorStateList(R.color.full_tab_color_selector))
mBinding.tvCenterText.apply {
setCompoundDrawables(null, null, it, null)
gravity = Gravity.CENTER_VERTICAL or Gravity.RIGHT
click { it.isSelected = true }
}
}
效果:
点击之后:
总结
如果图片没有变化,不管是状态切换,还是手动的设置drawableLeft drawableRight 都可以使用tint,我们就无需找设计师要多套图片了。
需要注意的一点是,我们在实际开发过程中,需要注意设置Tint之后是否需要清除它Tint属性。
最这也算是一个很简单也很重要的小知识,如果有更好的办法,欢迎大家讨论。
代码都已在文中贴出,如果想直接看Demo效果,点击查看源码。
到此完结。