一、为什么要自定义这样的Button
在Android开发中,按钮是最经常使用的控件,按钮所涉及的样式无非几种
- 边框
- 圆角
- 按下的颜色
现在有一个按钮的需求如下:
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" />
- 效果
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" />
- 效果
从上面两种方式可以看出通过自定义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"//边框默认的颜色和正常文字颜色一样
四个自定义属性 从名字即可看出他们的作用
- 自定义属性
在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;
}