背景
不知道大家在開發(fā)md風(fēng)格的項(xiàng)目中斥扛,各種形狀各種顏色的Button是怎么實(shí)現(xiàn)的入问,對(duì)于我這樣的一個(gè)菜鳥來(lái)說(shuō),就傻傻的一種樣式寫一個(gè)<ripple/>,這樣一個(gè)項(xiàng)目下來(lái)包含大量定義按鈕樣式的xml文件芬失,不但看著蛋疼卷仑,后期維護(hù)起來(lái)也非常惱火,于是乎開始自定義按鈕吧麸折。
自定義按鈕
說(shuō)起自定義View其實(shí)并不難锡凝,這里的自定義Button更加簡(jiǎn)單,都不用重寫onDraw垢啼、onLayout窜锯、onMeasure等方法,那就直接上代碼芭析,是在不明白的同學(xué)請(qǐng)學(xué)習(xí)下自定義控件的基礎(chǔ)知識(shí)锚扎。
首先是直接在項(xiàng)目的res/values下新建一個(gè)attrs.xml文件,然后在里面定義一些可能用到的屬性
<!--自定義Button屬性-->
<declare-styleable name="CustomButton">
<attr name="bgColor" format="color" />
<attr name="bgColorPress" format="color" />
<attr name="bgColorDisable" format="color" />
<attr name="textColor" format="color" />
<attr name="textColorPress" format="color" />
<attr name="textColorDisable" format="color" />
<attr name="cornersRadius" format="float" />
<attr name="shape">
<enum name="rectangle" value="0" />
<enum name="oval" value="1" />
<enum name="line" value="2" />
<enum name="ring" value="3" />
</attr>
<attr name="strokeWidth" format="integer" />
<attr name="strokeColor" format="color" />
<attr name="rippleColor" format="color" />
</declare-styleable>
這里根據(jù)需求定義了以上屬性,當(dāng)如如果有更復(fù)雜的需求就定義相應(yīng)的屬性,各位大神自由發(fā)揮瓶盛。
然后就是新建一個(gè)類CustomButton繼承自Button,把所有屬性列舉出來(lái)翠勉,然后獲取相應(yīng)屬性的值即可,由于比較簡(jiǎn)單霉颠,注釋也比較詳細(xì)对碌,就直接貼代碼了
CustomButton.java
package com.liuqiang.customviewlibrary;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.RippleDrawable;
import android.graphics.drawable.StateListDrawable;
import android.util.AttributeSet;
import android.widget.Button;
/**
* Created by liuqiang on 2017/11/3.
* 自定義帶波紋點(diǎn)擊效果的Button,支持圓角矩形蒿偎,圓形按鈕等樣式朽们,可通過(guò)配置文件改變按下后的樣式
*/
public class CustomButton extends Button {
private static String TAG = "CustomButton";
private Context mContext;
/**
* 按鈕的背景色
*/
private int bgColor = 0;
/**
* 按鈕被按下時(shí)的背景色
*/
private int bgColorPress = 0;
/**
* 按鈕不可用的背景色
*/
private int bgColorDisable = 0;
/**
* 按鈕正常時(shí)文字的顏色
*/
private int textColor;
/**
* 按鈕被按下時(shí)文字的顏色
*/
private int textColorPress;
/**
* 按鈕不可點(diǎn)擊時(shí)文字的顏色
*/
private int textColorDisable;
/**
* 按鈕的形狀
*/
private int shapeType;
/**
* 矩形時(shí)有效,4個(gè)角的radius
*/
private float cornersRadius;
/**
* 邊框線寬度
*/
private int strokeWidth = 0;
/**
* 邊框線顏色
*/
private int strokeColor;
private ColorStateList rippleColor;
//shape的樣式
public static final int RECTANGLE = 0;
public static final int OVAL = 1;
public static final int LINE = 2;
public static final int RING = 3;
private GradientDrawable gradientDrawable = null;
public CustomButton(Context context) {
this(context,null);
}
public CustomButton(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public CustomButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
getAttr(attrs);
init();
}
private void getAttr(AttributeSet attrs) {
if (attrs == null) {
return;
}
TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.CustomButton);
if (a != null) {
bgColor = a.getColor(R.styleable.CustomButton_bgColor, 0);
bgColorPress = a.getColor(R.styleable.CustomButton_bgColorPress, 0);
bgColorDisable = a.getColor(R.styleable.CustomButton_bgColorDisable, 0);
textColor = a.getColor(R.styleable.CustomButton_textColor, 0);
textColorPress = a.getColor(R.styleable.CustomButton_textColorPress, 0);
textColorDisable = a.getColor(R.styleable.CustomButton_textColorDisable, 0);
shapeType = a.getInt(R.styleable.CustomButton_shape, GradientDrawable.RECTANGLE);
cornersRadius = a.getFloat(R.styleable.CustomButton_cornersRadius, 0);
strokeWidth = a.getInteger(R.styleable.CustomButton_strokeWidth,0);
strokeColor = a.getColor(R.styleable.CustomButton_strokeColor,0);
rippleColor = a.getColorStateList(R.styleable.CustomButton_rippleColor);
if (rippleColor == null || rippleColor.getDefaultColor() == 0) {
rippleColor = createRippleColorStateList(Color.GRAY);
}
}
}
private void init() {
setClickable(true);
// setBackground(getDrawable(android.R.attr.state_enabled));
setBackground(getSelector());
setTextColor(createColorStateList());
}
/**
* 設(shè)置GradientDrawable
*
* @param state 按鈕狀態(tài)
* @return gradientDrawable
*/
public GradientDrawable getDrawable(int state) {
gradientDrawable = new GradientDrawable();
setShape();
setBorder();
setRadius();
setSelectorColor(state);
return gradientDrawable;
}
/**
* 設(shè)置shape類型
*/
private void setShape() {
switch (shapeType) {
case RECTANGLE:
gradientDrawable.setShape(GradientDrawable.RECTANGLE);
break;
case OVAL:
gradientDrawable.setShape(GradientDrawable.OVAL);
break;
case LINE:
gradientDrawable.setShape(GradientDrawable.LINE);
break;
case RING:
gradientDrawable.setShape(GradientDrawable.RING);
break;
}
}
/**
* 設(shè)置邊框 寬度 顏色 虛線 間隙
*/
private void setBorder() {
gradientDrawable.setStroke(strokeWidth, strokeColor, 0, 0);
}
/**
* 只有類型是矩形的時(shí)候設(shè)置圓角半徑才有效
*/
private void setRadius() {
if (shapeType == GradientDrawable.RECTANGLE) {
if (cornersRadius != 0) {
gradientDrawable.setCornerRadius(cornersRadius);//設(shè)置圓角的半徑
}
}
}
/**
* 設(shè)置Selector的不同狀態(tài)的顏色
*
* @param state 按鈕狀態(tài)
*/
private void setSelectorColor(int state) {
switch (state) {
case android.R.attr.state_pressed:
gradientDrawable.setColor(bgColorPress);
break;
case -android.R.attr.state_enabled:
gradientDrawable.setColor(bgColorDisable);
break;
case android.R.attr.state_enabled:
gradientDrawable.setColor(bgColor);
break;
}
}
/**
* 設(shè)置按鈕的Selector
*
* @return stateListDrawable
*/
public Drawable getSelector() {
StateListDrawable stateListDrawable = new StateListDrawable();
//注意該處的順序诉位,只要有一個(gè)狀態(tài)與之相配骑脱,背景就會(huì)被換掉
//所以不要把大范圍放在前面了,如果sd.addState(new[]{},normal)放在第一個(gè)的話苍糠,就沒有什么效果了
stateListDrawable.addState(new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled}, getDrawable(android.R.attr.state_pressed));
stateListDrawable.addState(new int[]{-android.R.attr.state_enabled}, getDrawable(-android.R.attr.state_enabled));
stateListDrawable.addState(new int[]{}, getDrawable(android.R.attr.state_enabled));
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
return stateListDrawable;
}else{
RippleDrawable rippleDrawable = new RippleDrawable(rippleColor,stateListDrawable,null);
return rippleDrawable;
}
}
/** 設(shè)置不同狀態(tài)時(shí)其文字顏色 */
private ColorStateList createColorStateList() {
int[] colors = new int[] { textColorPress, textColorDisable, textColor};
int[][] states = new int[3][];
states[0] = new int[] { android.R.attr.state_pressed, android.R.attr.state_enabled };
states[1] = new int[] { -android.R.attr.state_enabled};
states[2] = new int[] { android.R.attr.state_enabled };
ColorStateList colorList = new ColorStateList(states, colors);
return colorList;
}
/** 設(shè)置默認(rèn)ripple顏色 */
private ColorStateList createRippleColorStateList(int color) {
int[] colors = new int[] {color};
int[][] states = new int[1][];
states[0] = new int[] { };
ColorStateList colorList = new ColorStateList(states, colors);
return colorList;
}
/////////////////對(duì)外暴露的方法//////////////
/**
* 設(shè)置Shape類型
*
* @param type 類型
* @return 對(duì)象
*/
public CustomButton setShapeType(int type) {
this.shapeType = type;
return this;
}
/**
* 設(shè)置按下的顏色
*
* @param color 顏色
* @return 對(duì)象
*/
public CustomButton setBgPressedColor(int color) {
this.bgColorPress = getResources().getColor(color);
return this;
}
/**
* 設(shè)置正常的顏色
*
* @param color 顏色
* @return 對(duì)象
*/
public CustomButton setBgNormalColor(int color) {
this.bgColor = getResources().getColor(color);
return this;
}
/**
* 設(shè)置不可點(diǎn)擊的顏色
*
* @param color 顏色
* @return 對(duì)象
*/
public CustomButton setBgDisableColor(int color) {
this.bgColorDisable = getResources().getColor(color);
return this;
}
/**
* 設(shè)置按下的顏色
*
* @param color 顏色
* @return 對(duì)象
*/
public CustomButton setTextPressedColor(int color) {
this.textColorPress = getResources().getColor(color);
return this;
}
/**
* 設(shè)置正常的顏色
*
* @param color 顏色
* @return 對(duì)象
*/
public CustomButton setTextNormalColor(int color) {
this.textColor = getResources().getColor(color);
return this;
}
/**
* 設(shè)置不可點(diǎn)擊的顏色
*
* @param color 顏色
* @return 對(duì)象
*/
public CustomButton setTextDisableColor(int color) {
this.textColorDisable = getResources().getColor(color);
return this;
}
/**
* 設(shè)置邊框?qū)挾? *
* @param strokeWidth 邊框?qū)挾戎? * @return 對(duì)象
*/
public CustomButton setStrokeWidth(int strokeWidth) {
this.strokeWidth = strokeWidth;
return this;
}
/**
* 設(shè)置邊框顏色
*
* @param strokeColor 邊框顏色
* @return 對(duì)象
*/
public CustomButton setStrokeColor(int strokeColor) {
this.strokeColor = getResources().getColor(strokeColor);
return this;
}
/**
* 設(shè)置圓角半徑
*
* @param radius 半徑
* @return 對(duì)象
*/
public CustomButton setCornersRadius(float radius) {
this.cornersRadius = radius;
return this;
}
public CustomButton setRippleColor(int color){
this.rippleColor = createRippleColorStateList(getResources().getColor(color));
return this;
}
/**
* 使用shape
* 所有與shape相關(guān)的屬性設(shè)置之后調(diào)用此方法才生效
*/
public void use() {
init();
}
/**
* 單位轉(zhuǎn)換工具類
*
* @param context 上下文對(duì)象
* @param dipValue 值
* @return 返回值
*/
private int dip2px(Context context, float dipValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
}
}
在應(yīng)用主題是md主題的情況下叁丧,默認(rèn)的按鈕是自帶波紋點(diǎn)擊效果的,如果用<ripple/>作為按鈕的background是完全沒問題的椿息,但在自定義的按鈕中設(shè)置了按鈕的背景色歹袁,波紋效果就會(huì)消失,剛開始想不到辦法解決寝优,網(wǎng)上搜索的自定義波紋點(diǎn)擊按鈕都是要自己繪制,稍顯復(fù)雜枫耳,優(yōu)點(diǎn)是可以兼容5.0以下的版本乏矾。于是只能自己摸索,終于找到了一個(gè)簡(jiǎn)單方法。<ripple/>標(biāo)簽最終都會(huì)被解析成RippleDrawable钻心,可不可以直接實(shí)例化一個(gè)RippleDrawable呢凄硼,答案是可以的,直接看上面的代碼捷沸,主要代碼就這一段:
/**
* 設(shè)置按鈕的Selector
*
* @return stateListDrawable
*/
public Drawable getSelector() {
StateListDrawable stateListDrawable = new StateListDrawable();
//注意該處的順序摊沉,只要有一個(gè)狀態(tài)與之相配,背景就會(huì)被換掉
//所以不要把大范圍放在前面了痒给,如果sd.addState(new[]{},normal)放在第一個(gè)的話说墨,就沒有什么效果了
stateListDrawable.addState(new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled}, getDrawable(android.R.attr.state_pressed));
stateListDrawable.addState(new int[]{-android.R.attr.state_enabled}, getDrawable(-android.R.attr.state_enabled));
stateListDrawable.addState(new int[]{}, getDrawable(android.R.attr.state_enabled));
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
return stateListDrawable;
}else{
RippleDrawable rippleDrawable = new RippleDrawable(rippleColor,stateListDrawable,null);
return rippleDrawable;
}
}
在設(shè)置按鈕的背景drawable之前,實(shí)例化一個(gè)RippleDrawable苍柏。
第一參數(shù)就是波紋顏色尼斧;
第二個(gè)參數(shù)是平時(shí)我們?cè)O(shè)置的按鈕背景selector,如果直接用selector作為背景是沒有波紋點(diǎn)擊效果的试吁,這里直接把xml轉(zhuǎn)化成StateListDrawable棺棵,然后再作為RippleDrawable的content;
第三個(gè)參數(shù)是控制波紋范圍的drawable熄捍,這里直接傳null烛恤。
好了,自定義帶波紋效果的Button完成余耽,代碼很簡(jiǎn)單棒动,注釋也比較細(xì),就不多解釋了宾添。代碼傳送門
用法
xml方式
app:shape="rectangle" 設(shè)置按鈕的形狀船惨,矩形(包括帶圓角的矩形)、圓形缕陕、線形粱锐、環(huán)形(環(huán)形一直顯示不對(duì),不知道怎么回事)
app:bgColor="@color/colorPrimary" 按鈕可點(diǎn)擊時(shí)的背景色
app:bgColorPress="@color/colorPrimaryDark" 按鈕按下時(shí)的背景色
app:bgColorDisable="@color/white" 按鈕不可用時(shí)的背景色
app:cornersRadius="20" 當(dāng)shape為矩形時(shí)的圓角角度
app:strokeWidth="10" 邊線寬
app:strokeColor="@color/black" 邊線顏色
app:textColor="@color/white" 按鈕正常狀態(tài)時(shí)的文字顏色
app:textColorPress="@color/black" 按鈕被按下時(shí)的文字顏色
app:textColorDisable="@color/gray" 按鈕不可用時(shí)文字顏色
app:rippleColor="@color/colorAccent" 按鈕被觸摸時(shí)的波紋顏色
注意自定義命名空間 xmlns:app="http://schemas.android.com/apk/res-auto"
<com.liuqiang.customviewlibrary.CustomButton
android:id="@+id/customButton"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="自定義按鈕(xml設(shè)置)"
android:textColor="@color/white"
android:textSize="20sp"
android:gravity="center"
app:shape="rectangle"
app:bgColor="@color/colorPrimary"
app:bgColorPress="@color/colorPrimaryDark"
app:bgColorDisable="@color/white"
app:cornersRadius="20"
app:strokeWidth="10"
app:strokeColor="@color/black"
app:textColor="@color/white"
app:textColorPress="@color/black"
app:textColorDisable="@color/gray"
app:rippleColor="@color/colorAccent"
/>
代碼方式
customButton_code = (CustomButton) findViewById(R.id.customButton1);
customButton_code.setShapeType(CustomButton.RECTANGLE)
.setBgNormalColor(R.color.colorPrimary)
.setBgPressedColor(R.color.colorPrimaryDark)
.setBgDisableColor(R.color.white)
.setCornersRadius(20)
.setStrokeColor(R.color.black)
.setStrokeWidth(10)
.setTextNormalColor(R.color.white)
.setTextPressedColor(R.color.black)
.setTextDisableColor(R.color.gray)
.setRippleColor(R.color.colorAccent)
.use();
注意設(shè)置完屬性后記得調(diào)用use()方法扛邑。
總結(jié)
如果你也在苦惱按鈕的樣式太多怜浅,需要寫大量的<ripple/><shape/>等xml文件,不妨用這種方式蔬崩,擴(kuò)展性也非常強(qiáng)恶座,包括顏色漸變、點(diǎn)擊動(dòng)畫等沥阳。歡迎