美文网首页
ShapedTextView探索与实现

ShapedTextView探索与实现

作者: Brian512 | 来源:发表于2019-09-25 16:02 被阅读0次

    项目中大部分场景的按钮是使用的TextView,然后设置shape作为背景,如果需要有点击效果反馈,则在drawable中使用selector。
    这一套标准用法没啥毛病,唯一大缺点的就是繁琐。当然,如果项目的UI比较规范,定义几个常用的,复用起来也会很顺手。
    但对于UI设计不规范的项目,写一堆的selector+shape还是很恶心的。

    想法:shape的几个属性比较固定,就圆角和背景色等几个。能不能在xml写TextView时直接把这几个属性也传进去,这样就不用再去添加shape.xml了?

    如果只是传几个属性,还是比较容易的,关键是如何把设置的参数使用起来。
    第一思路:ShapeDrawable

    float radius = DensityUtil.dip2px(this, 10);
    float[] outerR = new float[]{radius, radius, radius, radius, radius, radius, radius, radius};
    RoundRectShape rr = new RoundRectShape(outerR, null, null);
    ShapeDrawable drawable = new ShapeDrawable(rr);
    drawable.getPaint().setColor(0xff333333);
    drawable.getPaint().setStyle(Paint.Style.STROKE);
    drawable.getPaint().setStrokeWidth(2);
    code.setBackgroundDrawable(drawable);
    

    看看drawable.xml加载的源码,发现shape加载出来的不是ShapeDrawable,而是GradientDrawable。

    float radius = DensityUtil.dip2px(this, 10);
    GradientDrawable drawable = new GradientDrawable();
    drawable.setCornerRadius(radius);
    drawable.setStroke(2, 0xff333333);
    code.setBackgroundDrawable(drawable);
    

    方案有了,就开干:

        <declare-styleable name="CompatTextView">
            <attr name="backgroundType">
                <enum name="solid" value="0" />
                <enum name="stroke" value="1" />
                <!--<enum name="gradient" value="2" />-->
            </attr>
            <attr name="solidColor" format="color|reference"/>
            <attr name="strokeColor" format="color|reference"/>
            <attr name="strokeWidth" format="dimension|reference"/>
            <attr name="radius" format="dimension|reference"/>
        </declare-styleable>
    
    public class CompatTextView extends AppCompatTextView {
    
        public static final int TYPE_SOLID = 0;
        public static final int TYPE_STROKE = 1;
        public static final int TYPE_GRADIENT = 2;
    
        public CompatTextView(Context context) {
            this(context, null, 0);
        }
        public CompatTextView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
        public CompatTextView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
    
            if (attrs != null) {
                initAttrs(context, attrs);
            }
        }
    
        private void initAttrs(Context context, AttributeSet attrs) {
            final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CompatTextView);
            int bgType = typedArray.getInt(R.styleable.CompatTextView_backgroundType, -1);
            if (0 <= bgType && bgType <= 2) {
                float radius = typedArray.getDimension(R.styleable.CompatTextView_radius, 0);
                GradientDrawable drawable = new GradientDrawable();
                if (bgType == TYPE_SOLID) {
                    int solidColor = typedArray.getInt(R.styleable.CompatTextView_solidColor, 0);
                    drawable.setColor(solidColor);
                } else if (bgType == TYPE_STROKE) {
                    int strokeWidth = (int) typedArray.getDimension(R.styleable.CompatTextView_strokeWidth, 0);
                    int strokeColor = typedArray.getInt(R.styleable.CompatTextView_strokeColor, 0);
                    drawable.setStroke(strokeWidth, strokeColor);
                }
                drawable.setCornerRadius(radius);
                setBackgroundDrawable(drawable);
            }
            typedArray.recycle();
        }
    }
    

    使用

        <com.brian.views.CompatTextView
            android:id="@+id/btn_logout"
            android:layout_width="288dp"
            android:layout_height="48dp"
            android:text="退出登录"
            android:textColor="#ffffffff"
            android:textSize="14sp"
            android:textStyle="bold"
            android:gravity="center"
            app:backgroundType="solid"
            app:solidColor="@color/popi_dark_btn_bg"
            app:radius="24dp"
            />
    

    可以看到,使用时只添加了三个自定义属性,这样就省去了定义一个shape,效率提升,性能也稍微好一点(不需要加载drawable.xml);

    如果需要添加点击态,也很简单,加一个属性,然后使用ColorStateList搞定:

     int[][] stateList = new int[][]{
             new int[]{android.R.attr.state_pressed},
             new int[]{}
     };
     int[] stateColorList = new int[]{
             pressedColor,
             typedArray.getInt(R.styleable.CompatTextView_solidColor, 0),
     };
     ColorStateList colorStateList = new ColorStateList(stateList, stateColorList);
     drawable.setColor(colorStateList);
    

    现在Android版本也更新较快,目前Android4.x及以下的设备持有量较少,是时候普及 Android5.0引入的Ripple点击态了。
    第一想法就是看看有没有RippleDrawable,搜索源码发现还真有这个类。
    有就好办了,看api,然后CV一下就差不多完工了:

    public class CompatTextView extends AppCompatTextView {
    
        public static final int TYPE_SOLID = 0;
        public static final int TYPE_STROKE = 1;
        public static final int TYPE_GRADIENT = 2;
    
        public CompatTextView(Context context) {
            this(context, null, 0);
        }
        public CompatTextView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
        public CompatTextView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
    
            if (attrs != null) {
                initAttrs(context, attrs);
            }
        }
    
        private void initAttrs(Context context, AttributeSet attrs) {
            final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CompatTextView);
            int bgType = typedArray.getInt(R.styleable.CompatTextView_backgroundType, -1);
            if (0 <= bgType && bgType <= 2) {
                float radius = typedArray.getDimension(R.styleable.CompatTextView_radius, 0);
                if (bgType == TYPE_SOLID) {
                    int solidColor = typedArray.getInt(R.styleable.CompatTextView_solidColor, 0);
                    GradientDrawable drawable = new GradientDrawable();
                    drawable.setColor(solidColor);
                    drawable.setCornerRadius(radius);
    
                    int rippleColor = typedArray.getInt(R.styleable.CompatTextView_rippleColor, 0);
                    if (rippleColor != 0) {
                        int[][] stateList = new int[][]{
                                new int[]{android.R.attr.state_pressed},
                                new int[]{}
                        };
                        int[] stateColorList = new int[]{
                                rippleColor,
                                solidColor,
                        };
                        ColorStateList colorStateList = new ColorStateList(stateList, stateColorList);
                        RippleDrawable rippleDrawable = new RippleDrawable(colorStateList, drawable, null);
                        setBackgroundDrawable(rippleDrawable);
                    } else {
                        setBackgroundDrawable(drawable);
                    }
    
                } else if (bgType == TYPE_STROKE) {
                    int strokeWidth = (int) typedArray.getDimension(R.styleable.CompatTextView_strokeWidth, 0);
                    int strokeColor = typedArray.getInt(R.styleable.CompatTextView_strokeColor, 0);
                    GradientDrawable drawable = new GradientDrawable();
                    drawable.setStroke(strokeWidth, strokeColor);
                    drawable.setCornerRadius(radius);
                    setBackgroundDrawable(drawable);
                }
            }
            typedArray.recycle();
        }
    }
    
        <declare-styleable name="CompatTextView">
            <attr name="backgroundType">
                <enum name="solid" value="0" />
                <enum name="stroke" value="1" />
                <!--<enum name="gradient" value="2" />-->
            </attr>
            <attr name="rippleColor" format="color|reference"/>
            <attr name="solidColor" format="color|reference"/>
            <attr name="strokeColor" format="color|reference"/>
            <attr name="strokeWidth" format="dimension|reference"/>
            <attr name="radius" format="dimension|reference"/>
        </declare-styleable>
    

    至此,思路和方案都比较清楚了,根据个人喜好再封装一下就很顺手了

    参考链接:
    从代码创建 Shape Drawable
    Android:RippleDrawable 水波纹/涟漪效果

    相关文章

      网友评论

          本文标题:ShapedTextView探索与实现

          本文链接:https://www.haomeiwen.com/subject/dzxwuctx.html