忙了一段時間,終於有時間整理整理之前所用到的一些知識,分享給大家,希望給同學們有些幫助,同時也是對自己的知識有個鞏固的過程。
在Android的開發中比較常用的控件就是Button了,但是我們平時使用Button時是怎樣來設置按下和擡起顯示不同的效果呢?我想一般的實現方式就是定義一個selector的xml文件,然後在裏面根據不同的state來設置不同的圖片,但是當Button控件非常多的時候,就要寫對應數量的xml文件,導致大碼非常臃腫。
今天我們換種方式來改變這個樣式,只需要兩行代碼即可實現按下的效果,同時支持圓角和圓形的按鈕的樣式。先看下效果圖,這是我寫的一個demo
接下來講一下主要代碼:
第一步 自定義屬性
在res/values/目錄下新建attrs.xml文件,
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--公共屬性-->
<attr name="backColor" format="color" />
<attr name="backColorPress" format="color" />
<attr name="backGroundImage" format="reference" />
<attr name="backGroundImagePress" format="reference" />
<attr name="textColor" format="color" />
<attr name="textColorPress" format="color" />
<declare-styleable name="buttonM">
<attr name="backColor" />
<attr name="backColorPress" />
<attr name="backGroundImage" />
<attr name="backGroundImagePress" />
<attr name="textColor" />
<attr name="textColorPress" />
<attr name="fillet" format="boolean" />
<attr name="radius" 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>
</declare-styleable>
</resources>
具體屬性的含義在java代碼中都有描述
第二步 創建ButtonM類使其繼承Button,代碼如下:
package com.landptf.view;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import com.landptf.R;
/**
* Created by landptf on 2016/10/25.
* 自定義Button,支持圓角矩形,圓形按鈕等樣式,可通過配置文件改變按下後的樣式
* 若通過代碼設置圓角或者圓形,需要先調用setFillet方法將fillet設置爲true
*/
public class ButtonM extends Button {
private static String TAG = "ButtonM";
/**
* 按鈕的背景色
*/
private int backColor = 0;
/**
* 按鈕被按下時的背景色
*/
private int backColorPress = 0;
/**
* 按鈕的背景圖片
*/
private Drawable backGroundDrawable = null;
/**
* 按鈕被按下時顯示的背景圖片
*/
private Drawable backGroundDrawablePress = null;
/**
* 按鈕文字的顏色
*/
private ColorStateList textColor = null;
/**
* 按鈕被按下時文字的顏色
*/
private ColorStateList textColorPress = null;
private GradientDrawable gradientDrawable = null;
/**
* 是否設置圓角或者圓形等樣式
*/
private boolean fillet = false;
/**
* 標示onTouch方法的返回值,用來解決onClick和onTouch衝突問題
*/
private boolean isCost = true;
public ButtonM(Context context) {
super(context, null);
}
public ButtonM(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ButtonM(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.buttonM, defStyle, 0);
if (a != null) {
//設置背景色
ColorStateList colorList = a.getColorStateList(R.styleable.buttonM_backColor);
if (colorList != null) {
backColor = colorList.getColorForState(getDrawableState(), 0);
if (backColor != 0) {
setBackgroundColor(backColor);
}
}
//記錄按鈕被按下時的背景色
ColorStateList colorListPress = a.getColorStateList(R.styleable.buttonM_backColorPress);
if (colorListPress != null){
backColorPress = colorListPress.getColorForState(getDrawableState(), 0);
}
//設置背景圖片,若backColor與backGroundDrawable同時存在,則backGroundDrawable將覆蓋backColor
backGroundDrawable = a.getDrawable(R.styleable.buttonM_backGroundImage);
if (backGroundDrawable != null){
setBackgroundDrawable(backGroundDrawable);
}
//記錄按鈕被按下時的背景圖片
backGroundDrawablePress = a.getDrawable(R.styleable.buttonM_backGroundImagePress);
//設置文字的顏色
textColor = a.getColorStateList(R.styleable.buttonM_textColor);
if (textColor != null){
setTextColor(textColor);
}
//記錄按鈕被按下時文字的顏色
textColorPress = a.getColorStateList(R.styleable.buttonM_textColorPress);
//設置圓角或圓形等樣式的背景色
fillet = a.getBoolean(R.styleable.buttonM_fillet, false);
if (fillet){
getGradientDrawable();
if (backColor != 0) {
gradientDrawable.setColor(backColor);
setBackgroundDrawable(gradientDrawable);
}
}
//設置圓角矩形的角度,fillet爲true時才生效
float radius = a.getFloat(R.styleable.buttonM_radius, 0);
if (fillet && radius != 0){
setRadius(radius);
}
//設置按鈕形狀,fillet爲true時才生效
int shape = a.getInteger(R.styleable.buttonM_shape, 0);
if (fillet && shape != 0) {
setShape(shape);
}
a.recycle();
}
setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View arg0, MotionEvent event) {
//根據touch事件設置按下擡起的樣式
return setTouchStyle(event.getAction());
}
});
}
/**
* 根據按下或者擡起來改變背景和文字樣式
* @param state
* @return isCost
* 爲解決onTouch和onClick衝突的問題
* 根據事件分發機制,如果onTouch返回true,則不響應onClick事件
* 因此採用isCost標識位,當用戶設置了onClickListener則onTouch返回false
*/
private boolean setTouchStyle(int state){
if (state == MotionEvent.ACTION_DOWN) {
if (backColorPress != 0) {
if (fillet){
gradientDrawable.setColor(backColorPress);
setBackgroundDrawable(gradientDrawable);
}else {
setBackgroundColor(backColorPress);
}
}
if (backGroundDrawablePress != null) {
setBackgroundDrawable(backGroundDrawablePress);
}
if (textColorPress != null) {
setTextColor(textColorPress);
}
}
if (state == MotionEvent.ACTION_UP) {
if (backColor != 0) {
if (fillet){
gradientDrawable.setColor(backColor);
setBackgroundDrawable(gradientDrawable);
}else {
setBackgroundColor(backColor);
}
}
if (backGroundDrawable != null) {
setBackgroundDrawable(backGroundDrawable);
}
if (textColor != null) {
setTextColor(textColor);
}
}
return isCost;
}
/**
* 重寫setOnClickListener方法,解決onTouch和onClick衝突問題
* @param l
*/
@Override
public void setOnClickListener(OnClickListener l) {
super.setOnClickListener(l);
isCost = false;
}
/**
* 設置按鈕的背景色
* @param backColor
*/
public void setBackColor(int backColor) {
this.backColor = backColor;
if (fillet){
gradientDrawable.setColor(backColor);
setBackgroundDrawable(gradientDrawable);
}else {
setBackgroundColor(backColor);
}
}
/**
* 設置按鈕被按下時的背景色
* @param backColorPress
*/
public void setBackColorPress(int backColorPress) {
this.backColorPress = backColorPress;
}
/**
* 設置按鈕的背景圖片
* @param backGroundDrawable
*/
public void setBackGroundDrawable(Drawable backGroundDrawable) {
this.backGroundDrawable = backGroundDrawable;
setBackgroundDrawable(backGroundDrawable);
}
/**
* 設置按鈕被按下時的背景圖片
* @param backGroundDrawablePress
*/
public void setBackGroundDrawablePress(Drawable backGroundDrawablePress) {
this.backGroundDrawablePress = backGroundDrawablePress;
}
/**
* 設置文字的顏色
* @param textColor
*/
public void setTextColor(int textColor) {
if (textColor == 0) return;
this.textColor = ColorStateList.valueOf(textColor);
//此處應加super關鍵字,調用父類的setTextColor方法,否則會造成遞歸導致內存溢出
super.setTextColor(this.textColor);
}
/**
* 設置按鈕被按下時文字的顏色
* @param textColorPress
*/
public void setTextColorPress(int textColorPress) {
if (textColorPress == 0) return;
this.textColorPress = ColorStateList.valueOf(textColorPress);
}
/**
* 設置按鈕是否設置圓角或者圓形等樣式
* @param fillet
*/
public void setFillet(boolean fillet){
this.fillet = fillet;
getGradientDrawable();
}
/**
* 設置圓角按鈕的角度
* @param radius
*/
public void setRadius(float radius){
if (!fillet) return;
getGradientDrawable();
gradientDrawable.setCornerRadius(radius);
setBackgroundDrawable(gradientDrawable);
}
/**
* 設置按鈕的形狀
* @param shape
*/
public void setShape(int shape){
if (!fillet) return;
getGradientDrawable();
gradientDrawable.setShape(shape);
setBackgroundDrawable(gradientDrawable);
}
private void getGradientDrawable() {
if (gradientDrawable == null){
gradientDrawable = new GradientDrawable();
}
}
}
註釋基本上寫的比較詳細,下面主要說一下這裏面涉及的一些知識點
1 關於onTouch返回值問題,如果返回true表示要消費該點擊事件,後續的所有事件都交給他處理,同時onTouchEvent將不會執行,因此onClick也得不到執行,在這裏通過重寫setOnClickListener設置變量來改變返回值。具體關於View的事件分發機制可以查閱有關文檔,網上很多這方面的教程。
2 如果想要通過java代碼來設置圓角或者圓形時,必須先設置setFillet(true),然後再設置背景色,形狀或者角度等參數。通過xml文件則無限制
最後講一下怎麼使用,這裏以設置圓角矩形爲例,分別通過xml和java代碼實現,其他的可參考源碼。
1 xml
<com.landptf.view.ButtonM
android:id="@+id/btm_radius_color_xml"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"
android:gravity="center"
android:text="點擊改變背景色"
landptf:backColor="#ff3300"
landptf:backColorPress="#ff33ff"
landptf:fillet="true"
landptf:radius="30"
landptf:textColor="@android:color/white" />
2 java
ButtonM btmRadiusColorJava = (ButtonM) findViewById(R.id.btm_radius_color_java);
if (btmRadiusColorJava != null) {
btmRadiusColorJava.setFillet(true);
btmRadiusColorJava.setRadius(30);
btmRadiusColorJava.setTextColor(Color.parseColor("#ffffff"));
btmRadiusColorJava.setBackColor(Color.parseColor("#ff3300"));
btmRadiusColorJava.setBackColorPress(Color.parseColor("#ff33ff"));
btmRadiusColorJava.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(ButtonMTestActivity.this, "java代碼實現", Toast.LENGTH_SHORT).show();
}
});
}
代碼已託管到開源中國的碼雲上,歡迎下載,地址:https://git.oschina.net/landptf/landptf.git