自定义button 可设置圆角 边框 按下颜色

一、为什么要自定义这样的Button

在Android开发中,按钮是最经常使用的控件,按钮所涉及的样式无非几种

  1. 边框
  2. 圆角
  3. 按下的颜色
example.png

现在有一个按钮的需求如下:

1.背景颜色: 正常: #ff0000  按下: #008888
2.文字颜色:正常: #ffffff  按下: #000000
3.圆角:弧度为5dp
4.边框:宽度:2px  颜色:#000000

1. selector+shape

  • 编写背景selector+shape
//正常shape   normal.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <corners android:radius="5dp" />
    <stroke
        android:width="2px"
        android:color="#000000" />
    <solid android:color="#ff0000" />
</shape>

//按下shape  press.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">

    <corners android:radius="10dp" />
    <stroke
        android:width="2px"
        android:color="#000000" />
    <solid android:color="#008888" />
</shape>


//selector
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@drawable/normal" android:state_pressed="false" />
    <item android:drawable="@drawable/press" android:state_pressed="true" />
</selector>

  • 编写文字颜色selector
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="#ffffff" android:state_pressed="false" />
    <item android:color="#000000" android:state_pressed="true" />

</selector>

  • 在layout文件中使用
   <TextView
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_margin="20dp"
        android:background="@drawable/bgc_btn"
        android:clickable="true"
        android:gravity="center"
        android:text="selector+shape方式"
        android:textColor="@drawable/color_text" />       
  • 效果
ButtonText.gif

2. 自定义Button

 <lu.example.ButtonTextView
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_margin="20dp"
        android:background="#ff0000"
        android:gravity="center"
        android:text="自定义方式"
        android:textColor="@android:color/white"
        app:corner="30%"
        app:pressBgc="#008888"
        app:pressTxtColor="#000"
        app:stroke="10px" />
        
  • 效果
ButtonText2.gif
zongjie.png

从上面两种方式可以看出通过自定义button的方式可以很方便地实现按钮样式的指定,免去了写selector和shape的麻烦,最重要的是每个selector和shape都是固定的值,如果颜色需要改变,不同的样式需要写不同的selector,这是很不方便的,哪怕只是改一个圆角大小或者边框粗细。

一、ButtonTextView的实现思路

在看一下我们上面使用的代码

<lu.example.ButtonTextView
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_margin="20dp"
        android:background="#ff0000"
        android:gravity="center"
        android:text="自定义方式"
        android:textColor="@android:color/white"
        app:corner="30%"
        app:pressBgc="#008888"
        app:pressTxtColor="#000"
        app:stroke="10px" />
        
-----------------------------------------------------

1.background  和 textColor两个是本身就有的属性   用来设置默认情况下背景颜色和字体颜色,这个保持不变

2.  app:corner="30%"//可以是具体数值,也可以是边框的百分比
    app:pressBgc="#008888"//可以是具体颜色,也可以用@color/red的方式
    app:pressTxtColor="#000"
    app:stroke="10px"//边框默认的颜色和正常文字颜色一样
    
    四个自定义属性  从名字即可看出他们的作用
        
        
  1. 自定义属性

在res/values.xml中定义四个属性

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="ButtonTextView">
        <attr name="pressTxtColor" format="color"></attr>
        <attr name="pressBgc" format="color"></attr>
        <attr name="stroke" format="dimension"></attr>
        <attr name="corner" format="dimension|fraction"></attr>
    </declare-styleable>
</resources>

自定义属性值的获取


//首先定义NAME_SPACE和属性用于获取原来就有的background和textColor
  private final String NAME_SPACE = "http://schemas.android.com/apk/res/android";
    private final String ATTR_BGC = "background";
    private final String ATTR_TXTC = "textColor";


//在构造方法中获取属性

 public ButtonTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();

        if (attrs != null) {
            String bc = attrs.getAttributeValue(NAME_SPACE, ATTR_BGC);
            if (TextUtils.isEmpty(bc)) {
                bgc = Color.WHITE;
            } else if (bc.startsWith("#")) {
                bgc = Color.parseColor(bc);
            } else if (bc.startsWith("@")) {
                bgc = ContextCompat.getColor(context, Integer.valueOf(bc.substring(1)));
            }

            String tc = attrs.getAttributeValue(NAME_SPACE, ATTR_TXTC);
            if (TextUtils.isEmpty(tc)) {
                txtC = DEFAULT_TEXT_COLOR;
            } else if (tc.startsWith("#")) {
                txtC = Color.parseColor(tc);
            } else if (tc.startsWith("@")) {
                txtC = ContextCompat.getColor(context, Integer.valueOf(tc.substring(1)));
            }

        }

        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ButtonTextView);

        pressTxtC = ta.getColor(R.styleable.ButtonTextView_pressTxtColor, txtC);
        pressBgc = ta.getColor(R.styleable.ButtonTextView_pressBgc, bgc);

        //处理圆角度
        final String cornerValue = ta.getString(R.styleable.ButtonTextView_corner);
        if (!TextUtils.isEmpty(cornerValue)) {
            if (cornerValue.contains("%")) {
                corner = -1;
                cornerPercent = ta.getFraction(R.styleable.ButtonTextView_corner, 1, 1, 0f);
            } else {
                corner = ta.getDimensionPixelSize(R.styleable.ButtonTextView_corner, 0);
            }
        }

        //处理边框
        stroke = ta.getDimensionPixelSize(R.styleable.ButtonTextView_stroke, 0);

        ta.recycle();
    }


构建字体颜色ColorStateList的方法

/**
 * 
 * @param txtC 正常情况下的字体颜色
 * @param pressTxtC 按下时的字体颜色
 */
 private void setTxtColor(@NonNull int txtC, @NonNull int pressTxtC) {

        if (txtC == pressTxtC) {
            setTextColor(txtC);
            return;
        }

        int[] colors = new int[]{pressTxtC, txtC};

        int[][] states = new int[2][];
        states[0] = new int[]{android.R.attr.state_pressed};
        states[1] = new int[]{};

        ColorStateList colorStateList = new ColorStateList(states, colors);

        setTextColor(colorStateList);
    }

构建背景色StateListDrawable的方法

 /**
     *
     * @param bgc 正常背景色
     * @param pressBgc 按下背景色
     * @param corner 圆角
     * @param stroke 边框
     */
    private void setBgcDrawable(@NonNull int bgc, @NonNull int pressBgc, float corner, int stroke) {

        GradientDrawable bgcDrawable = new GradientDrawable();
        GradientDrawable pBgcDrawable = new GradientDrawable();

        bgcDrawable.setCornerRadius(corner);
        bgcDrawable.setStroke(stroke, txtC == 0 ? DEFAULT_TEXT_COLOR : txtC);
        bgcDrawable.setColor(bgc);


        if (bgc == pressBgc) {
            setBackgroundDrawable(bgcDrawable);
            return;
        }

        pBgcDrawable.setCornerRadius(corner);
        pBgcDrawable.setStroke(stroke, pressBgc);
        pBgcDrawable.setColor(pressBgc);

        StateListDrawable stateListDrawable = new StateListDrawable();
        stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, pBgcDrawable);
        stateListDrawable.addState(new int[]{}, bgcDrawable);

        setBackgroundDrawable(stateListDrawable);
    }

具体设置字体颜色和背景色的代码这里选择在onSizeChanged()方法中调用

  @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (corner < 0) {
            corner = cornerPercent * h;
        }
        setBgcDrawable(bgc, pressBgc, corner, stroke);
        setTxtColor(txtC, pressTxtC);
    }

附整体代码(使用时需要添加自定义属性)

/**
 * 自定义按下颜色和边框的TextView
 * Created by lqh on 2016/9/25.
 */
public class ButtonTextView extends AppCompatTextView {

    private final String NAME_SPACE = "http://schemas.android.com/apk/res/android";
    private final String ATTR_BGC = "background";
    private final String ATTR_TXTC = "textColor";

    private final int DEFAULT_TEXT_COLOR = 0x8a000000;
    //文字演策
    private int txtC = DEFAULT_TEXT_COLOR;
    private int pressTxtC = DEFAULT_TEXT_COLOR;
    //背景色
    private int bgc;
    private int pressBgc;
    //圆角
    private float corner;
    private float cornerPercent;
    //边框
    private int stroke;

    /* 通过代码创建对象时,不检索自定义属性*/
    public ButtonTextView(Context context) {
        super(context);
        /*默认颜色*/
        setTextColor(DEFAULT_TEXT_COLOR);
    }

    public ButtonTextView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ButtonTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();

        if (attrs != null) {
            String bc = attrs.getAttributeValue(NAME_SPACE, ATTR_BGC);
            if (TextUtils.isEmpty(bc)) {
                bgc = Color.WHITE;
            } else if (bc.startsWith("#")) {
                bgc = Color.parseColor(bc);
            } else if (bc.startsWith("@")) {
                bgc = ContextCompat.getColor(context, Integer.valueOf(bc.substring(1)));
            }

            String tc = attrs.getAttributeValue(NAME_SPACE, ATTR_TXTC);
            if (TextUtils.isEmpty(tc)) {
                txtC = DEFAULT_TEXT_COLOR;
            } else if (tc.startsWith("#")) {
                txtC = Color.parseColor(tc);
            } else if (tc.startsWith("@")) {
                txtC = ContextCompat.getColor(context, Integer.valueOf(tc.substring(1)));
            }

        }

        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ButtonTextView);

        pressTxtC = ta.getColor(R.styleable.ButtonTextView_pressTxtColor, txtC);
        pressBgc = ta.getColor(R.styleable.ButtonTextView_pressBgc, bgc);

        //处理圆角度
        final String cornerValue = ta.getString(R.styleable.ButtonTextView_corner);
        if (!TextUtils.isEmpty(cornerValue)) {
            if (cornerValue.contains("%")) {
                corner = -1;
                cornerPercent = ta.getFraction(R.styleable.ButtonTextView_corner, 1, 1, 0f);
            } else {
                corner = ta.getDimensionPixelSize(R.styleable.ButtonTextView_corner, 0);
            }
        }

        //处理边框
        stroke = ta.getDimensionPixelSize(R.styleable.ButtonTextView_stroke, 0);

        ta.recycle();
    }

    private void init() {
        setClickable(true);
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (corner < 0) {
            corner = cornerPercent * h;
        }
        setBgcDrawable(bgc, pressBgc, corner, stroke);
        setTxtColor(txtC, pressTxtC);
    }

    /**
     *
     * @param txtC 正常情况下的字体颜色
     * @param pressTxtC 按下时的字体颜色
     */
    private void setTxtColor(@NonNull int txtC, @NonNull int pressTxtC) {

        if (txtC == pressTxtC) {
            setTextColor(txtC);
            return;
        }

        int[] colors = new int[]{pressTxtC, txtC};

        int[][] states = new int[2][];
        states[0] = new int[]{android.R.attr.state_pressed};
        states[1] = new int[]{};

        ColorStateList colorStateList = new ColorStateList(states, colors);

        setTextColor(colorStateList);
    }


    /**
     *
     * @param bgc 正常背景色
     * @param pressBgc 按下背景色
     * @param corner 圆角
     * @param stroke 边框
     */
    private void setBgcDrawable(@NonNull int bgc, @NonNull int pressBgc, float corner, int stroke) {

        GradientDrawable bgcDrawable = new GradientDrawable();
        GradientDrawable pBgcDrawable = new GradientDrawable();

        bgcDrawable.setCornerRadius(corner);
        bgcDrawable.setStroke(stroke, txtC == 0 ? DEFAULT_TEXT_COLOR : txtC);
        bgcDrawable.setColor(bgc);


        if (bgc == pressBgc) {
            setBackgroundDrawable(bgcDrawable);
            return;
        }

        pBgcDrawable.setCornerRadius(corner);
        pBgcDrawable.setStroke(stroke, pressBgc);
        pBgcDrawable.setColor(pressBgc);

        StateListDrawable stateListDrawable = new StateListDrawable();
        stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, pBgcDrawable);
        stateListDrawable.addState(new int[]{}, bgcDrawable);

        setBackgroundDrawable(stateListDrawable);
    }

    /**
     * 设置背景色
     *
     * @param bgc
     * @param pressBgc
     */
    public void setBgcDrawable(@NonNull int bgc, @NonNull int pressBgc) {
        this.bgc = bgc;
        this.pressBgc = pressBgc;
    }

    /**
     * 设置文字颜色
     *
     * @param txtC
     * @param pressTxtC
     */
    public void setTextColor(@NonNull int txtC, @NonNull int pressTxtC) {
        this.txtC = txtC;
        this.pressTxtC = pressTxtC;
    }

    public void setCorner(float corner) {
        this.corner = corner;
    }

    public void setStroke(int stroke) {
        this.stroke = stroke;
    }

    public void setTxtC(int txtC, int pressTxtC) {
        this.txtC = txtC;
        this.pressTxtC = pressTxtC;
    }

如果错误欢迎指正,感谢!

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

推荐阅读更多精彩内容