GradientDrawable和StateListDrawable 的简单使用,以及如何动态改变TextView的背景颜色和文字颜色

什么都别管,先看效果图
在这里插入图片描述
封装的demo请自行下载
https://github.com/linqinen708/MyCheckedTextView

Android开发的小伙伴,在遇到TextView 需要设置背景色时,应该会联想到shape

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <corners android:radius="2dp"/>
    <solid android:color="@color/green_61A71F"/>
</shape>

或则根据某种状态,比如 按压、选择和点击等条件,而改变背景色时,肯定第一时间就想到了selector

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/shape_bg_check" android:state_checked="true"/>
    <item android:drawable="@drawable/shape_bg_uncheck" android:state_checked="false"/>
</selector>

然后再在布局中设置background就行了。

但是如果每次需要设置,都要去创建drawable下的文件,非常的繁琐,而且耦合度高,能否有其他的方式来实现呢?

上网搜索后,发现了两个个神器 GradientDrawable 和 StateListDrawable

细心的小伙伴会发现,当我们需要动态设置TextView的背景,也就是setBackground时,传入的参数实际上是一个Drawable对象,这也是为什么我们的shape和selector文件需要放在drawable文件夹下的原因。而GradientDrawable 和 StateListDrawable就是继承自Drawable的

1.GradientDrawable

如果我们需要动态设置TextView的背景颜色、形状和圆角等属性时,就可以使用GradientDrawable

GradientDrawable gradientDrawable = new GradientDrawable();
gradientDrawable.setColor(Color.RED);//背景颜色,对应标签shape 中的solid属性
gradientDrawable.setShape(GradientDrawable.RECTANGLE);//形状
gradientDrawable.setStroke(1,Color.GREEN);//边框颜色,对应标签shape 中的stroke属性
gradientDrawable.setCornerRadius(5);//四个圆角的角度,对应标签shape 中的radius属性
//如果需要设置某一个圆角的角度,使用setCornerRadii方法
//1、2两个参数表示左上角,3、4表示右上角,5、6表示右下角,7、8表示左下角
//gradientDrawable.setCornerRadii(new float[]{mRadiusTopLeft, mRadiusTopLeft, mRadiusTopRight, mRadiusTopRight, mRadiusBottomRight, mRadiusBottomRight, mRadiusBottomLeft, mRadiusBottomLeft});
//gradientDrawable.setOrientation();//只有GradientDrawable.LINEAR_GRADIENT,才有Orientation属性,默认GradientDrawable.Orientation.TOP_BOTTOM
//gradientDrawable.setColors(new int[]{Color.RED, Color.GREEN});//设置setColors之后,setColor无效
//gradientDrawable.setGradientType(mGradientType);//默认GradientDrawable.LINEAR_GRADIENT
//gradientDrawable.setGradientRadius(10);//gradient 角度
textView.setBackground(gradientDrawable);

2.StateListDrawable用法也很简单

StateListDrawable backgroundDrawable = new StateListDrawable();
backgroundDrawable.addState(new int[]{android.R.attr.state_pressed}, mPressedDrawable);
backgroundDrawable.addState(new int[]{android.R.attr.state_checked}, mCheckedDrawable);
backgroundDrawable.addState(new int[]{android.R.attr.state_enabled}, mNormalDrawable);
backgroundDrawable.addState(new int[]{-android.R.attr.state_enabled}, mUnableDrawable);

textView.setBackground(backgroundDrawable);

其中在属性,比如android.R.attr.state_enabled 前面加上 “-”,也就是 -android.R.attr.state_enabled,就表示false,其他属性也有很多,我就不一一列出了。
方法 addState(int[] stateSet, Drawable drawable) 当中有2个参数,第一个是传入一个int类型的数组,里面是你需要改变的属性,因为是数组,所以你可以同时选择多个,第二个参数就是一个Drawable 对象,比如context.getResources().getDrawable(R.drawable.bg),也可以是上面提到的GradientDrawable。

设置完之后,你设置属性,比如setEnabled,就可以触发状态的改变,从而动态改变颜色

textView.setEnabled(!textView.isEnabled());

那如果想要动态设置TextView的颜色,并且也能根据状态动态改变呢?
3.ColorStateList

int[] colors = new int[]{
    getResources().getColor(R.color.yellow_F9F1DC),
    getResources().getColor(R.color.blue_0a57f3),
    getResources().getColor(R.color.red_E66051),
    getResources().getColor(R.color.gray_f1f2f4),
};
int[][] states = new int[4][];
states[0] = new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled};
states[1] = new int[]{android.R.attr.state_checked, android.R.attr.state_enabled};
states[2] = new int[]{android.R.attr.state_enabled};
states[3] = new int[]{-android.R.attr.state_enabled};
ColorStateList colorList = new ColorStateList(states, colors);
textView1.setTextColor(colorList);

4.Checkable
细心的小伙伴会发现,在我上面的代码中,出现了TextView本身不具备的属性R.attr.state_checked。
没错,这个属性是我自己手动添加上去的。因为平常经常会碰到需要动态选择的状态,虽然有系统自带的CheckedTextView,但是用起来还是不方便,所以就自己额外对TextView进行了封装。

用起来也很方便,首先自己定义常量CHECKED_STATE_SET ,然后重写onCreateDrawableState方法,在里面对原有的状态属性额外添加一个属性,即extraSpace + 1,之后调用mergeDrawableStates方法,将自己的属性和原本的属性结合,返回drawableState即可。

然后让自己的TextView添加Checkable 接口,重写setChecked方法,其中要调用refreshDrawableState这个方法,表示刷新TextView的状态

经过日志发现,TextView在被点击触发的状态顺序依次是:

点击 > onCreateDrawableState > drawableStateChanged(state_pressed) > setChecked > onCreateDrawableState > drawableStateChanged(state_pressed 、state_checked) > onCreateDrawableState > drawableStateChanged(state_checked)

也就是说,会先触发press效果,然后触发setChecked 改变状态,此时TextView有press和check两种状态,刷新界面后,保留check状态,显示在界面上


public class MyTextView extends AppCompatTextView implements Checkable {

  private static final int[] CHECKED_STATE_SET = new int[]{android.R.attr.state_checked};
  private boolean mChecked;
  
  @Override
  protected int[] onCreateDrawableState(int extraSpace) {
    final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
    if (isChecked()) {
       //LogT.i("被选中");
       mergeDrawableStates(drawableState, CHECKED_STATE_SET);
    }
    return drawableState;
  }
   @Override
   public void setChecked(boolean checked) {
        LogT.i("mChecked:" + mChecked + ", checked:" + checked);
        if (this.mChecked != checked) {
            this.mChecked = checked;
            refreshDrawableState();
        }
    }
    @Override
    public boolean isChecked() {
        return mChecked;
    }

    @Override
    public void toggle() {
        setChecked(!mChecked);
    }
}

同理,setEnabled(false) 的状态也是这样改变的

按照以上步骤基本可以完成常见的TextView的动态设置背景和文字颜色,不过这样用起来还是有点麻烦,所以我们要多做一步,把这些功能封装起来
封装后,就可以在xml布局中直接使用这些属性,不再需要在drawable文件夹下创建shape和selector了

真香 O(∩_∩)O哈哈~

<com.linqinen708.mycustomview.mycheckedtextview.MyCheckedTextView
   android:id="@+id/mtv_001"
   android:layout_width="100dp"
   android:layout_height="50dp"
   android:layout_marginStart="15dp"
   android:layout_marginTop="10dp"
   android:gravity="center"
   android:text="确定"
   app:mtv_checked_solid_color="@color/blue_8daef3"
   app:mtv_checked_stroke_color="@color/blue_8daef3"
   app:mtv_pressed_solid_color="@color/green_5FA51F"
   app:mtv_pressed_stroke_color="@color/green_5FA51F"
   app:mtv_unable_solid_color="@color/gray_D5D5D5"
   app:mtv_unable_stroke_color="@color/gray_D5D5D5"
   app:mtv_solid_color="@color/yellow_F9F1DC"
   app:mtv_stroke_color="@color/red_E66051"
   app:mtv_stroke_width="2dp"
   app:mtv_checked_text_color="@color/white_ffffff"
   app:mtv_pressed_text_color="@color/yellow_F9F1DC"
   app:mtv_unable_text_color="@color/white_ffffff"
/>

坑1:所有系统自带的属性,默认是true,在前面添加“-”,则表示false,比如android.R.attr.state_enabled 前面加上 “-”,也就是 -android.R.attr.state_enabled,就表示false
坑2:当有多个状态需要判断时,注意顺序!!!注意顺序!!!注意顺序!!!重要的事情说三遍!!!
比如你先判断state_enabled,再判断state_pressed,不好意思,state_pressed就没有效果了,所以state_enabled要放在最后来判断,优先判断state_pressed、state_checked等状态。
别问我为什么知道,我TM一直以为我代码哪里写错了。。。o(╥﹏╥)o

//正确案例
states[0] = new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled};
states[1] = new int[]{android.R.attr.state_checked, android.R.attr.state_enabled};
states[2] = new int[]{android.R.attr.state_enabled};
states[3] = new int[]{-android.R.attr.state_enabled};
//错误案例
states[0] = new int[]{android.R.attr.state_enabled};
states[1] = new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled};
states[2] = new int[]{android.R.attr.state_checked, android.R.attr.state_enabled};
states[3] = new int[]{-android.R.attr.state_enabled};
//正确案例
StateListDrawable backgroundDrawable = new StateListDrawable();
backgroundDrawable.addState(new int[]{android.R.attr.state_pressed}, mPressedDrawable);
backgroundDrawable.addState(new int[]{android.R.attr.state_checked}, mCheckedDrawable);
backgroundDrawable.addState(new int[]{android.R.attr.state_enabled}, mNormalDrawable);
backgroundDrawable.addState(new int[]{-android.R.attr.state_enabled}, mUnableDrawable);
//错误案例
StateListDrawable backgroundDrawable = new StateListDrawable();
backgroundDrawable.addState(new int[]{android.R.attr.state_enabled}, mNormalDrawable);
backgroundDrawable.addState(new int[]{android.R.attr.state_pressed}, mPressedDrawable);
backgroundDrawable.addState(new int[]{android.R.attr.state_checked}, mCheckedDrawable);
backgroundDrawable.addState(new int[]{-android.R.attr.state_enabled}, mUnableDrawable);

参考资料:
https://blog.csdn.net/qq_31796651/article/details/75010332
https://blog.csdn.net/cui130/article/details/89181691
https://www.jianshu.com/p/64a825915da9

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值