ShapedTextView探索与实现

项目中大部分场景的按钮是使用的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 水波纹/涟漪效果

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容

  • 今天妈妈在家给我做饺子了。我正在读书,突然闻到一股饺子的香味。我飞奔到厨房,看到妈妈端来一盘香喷喷的饺子。我坐到椅...
    不变的心_3d86阅读 102评论 0 0
  • 雪 梅 〔南宋〕卢梅坡 梅雪争春未肯降, 骚人搁笔费评章。 梅须逊雪三分白, 雪却输梅一段香。 ...
    深蓝清亭阅读 282评论 2 2
  • ❤ 前天上午十点多钟的时候,公主给我发来了一张截图--电影票的截图。告诉我晚上一起看电影,电影是好几个月之前就在网...
    孤馆良文阅读 923评论 0 2
  • 早上给爸爸在微信里发了一个红包,祝福爸爸父亲节快乐。 感谢爸爸,给我一个物质精神都比较丰富的童年。 看到小林的这张...
    独坐静听阅读 104评论 0 1