Android自定義控件系列(一)—Button七十二變


轉載請注明出處:http://www.cnblogs.com/landptf/p/6290791.html

忙了一段時間,終於有時間整理整理之前所用到的一些知識,分享給大家,希望給同學們有些幫助,同時也是對自己的知識有個鞏固的過程。

在Android的開發中比較常用的控件就是Button了,但是我們平時使用Button時是怎樣來設置按下和抬起顯示不同的效果呢?我想一般的實現方式就是定義一個selector的xml文件,然后在里面根據不同的state來設置不同的圖片,但是當Button控件非常多的時候,就要寫對應數量的xml文件,導致大碼非常臃腫。

今天我們換種方式來改變這個樣式,只需要兩行代碼即可實現按下的效果,同時支持圓角和圓形的按鈕的樣式。先看下效果圖,這是我寫的一個demo

 

接下來講一下主要代碼: 
第一步 自定義屬性 
在res/values/目錄下新建attrs.xml文件

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <resources>
 3     <!--公共屬性-->
 4     <attr name="backColor" format="color" />
 5     <attr name="backColorPress" format="color" />
 6     <attr name="backGroundImage" format="reference" />
 7     <attr name="backGroundImagePress" format="reference" />
 8     <attr name="textColor" format="color" />
 9     <attr name="textColorPress" format="color" />
10 
11     <declare-styleable name="buttonM">
12         <attr name="backColor" />
13         <attr name="backColorPress" />
14         <attr name="backGroundImage"  />
15         <attr name="backGroundImagePress" />
16         <attr name="textColor" />
17         <attr name="textColorPress" />
18         <attr name="fillet" format="boolean" />
19         <attr name="radius" format="float" />
20         <attr name="shape">
21             <enum name="rectangle" value="0" />
22             <enum name="oval" value="1" />
23             <enum name="line" value="2" />
24             <enum name="ring" value="3" />
25         </attr>
26     </declare-styleable>
27 
28 </resources>

具體屬性的含義在java代碼中都有描述 
第二步 創建ButtonM類使其繼承Button,代碼如下:

  1 package com.landptf.view;
  2 
  3 import android.content.Context;
  4 import android.content.res.ColorStateList;
  5 import android.content.res.TypedArray;
  6 import android.graphics.drawable.Drawable;
  7 import android.graphics.drawable.GradientDrawable;
  8 import android.util.AttributeSet;
  9 import android.view.MotionEvent;
 10 import android.view.View;
 11 import android.widget.Button;
 12 
 13 import com.landptf.R;
 14 
 15 /**
 16  * Created by landptf on 2016/10/25.
 17  * 自定義Button,支持圓角矩形,圓形按鈕等樣式,可通過配置文件改變按下后的樣式
 18  * 若通過代碼設置圓角或者圓形,需要先調用setFillet方法將fillet設置為true
 19  */
 20 public class ButtonM extends Button {
 21     private static String TAG = "ButtonM";
 22     /**
 23      * 按鈕的背景色
 24      */
 25     private int backColor = 0;
 26     /**
 27      * 按鈕被按下時的背景色
 28      */
 29     private int backColorPress = 0;
 30     /**
 31      * 按鈕的背景圖片
 32      */
 33     private Drawable backGroundDrawable = null;
 34     /**
 35      * 按鈕被按下時顯示的背景圖片
 36      */
 37     private Drawable backGroundDrawablePress = null;
 38     /**
 39      * 按鈕文字的顏色
 40      */
 41     private ColorStateList textColor = null;
 42     /**
 43      * 按鈕被按下時文字的顏色
 44      */
 45     private ColorStateList textColorPress = null;
 46     private GradientDrawable gradientDrawable = null;
 47     /**
 48      * 是否設置圓角或者圓形等樣式
 49      */
 50     private boolean fillet = false;
 51     /**
 52      * 標示onTouch方法的返回值,用來解決onClick和onTouch沖突問題
 53      */
 54     private boolean isCost = true;
 55 
 56     public ButtonM(Context context) {
 57         super(context, null);
 58     }
 59 
 60     public ButtonM(Context context, AttributeSet attrs) {
 61         this(context, attrs, 0);
 62     }
 63 
 64     public ButtonM(Context context, AttributeSet attrs, int defStyle) {
 65         super(context, attrs, defStyle);
 66         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.buttonM, defStyle, 0);
 67         if (a != null) {
 68             //設置背景色
 69             ColorStateList colorList = a.getColorStateList(R.styleable.buttonM_backColor);
 70             if (colorList != null) {
 71                 backColor = colorList.getColorForState(getDrawableState(), 0);
 72                 if (backColor != 0) {
 73                     setBackgroundColor(backColor);
 74                 }
 75             }
 76             //記錄按鈕被按下時的背景色
 77             ColorStateList colorListPress = a.getColorStateList(R.styleable.buttonM_backColorPress);
 78             if (colorListPress != null){
 79                 backColorPress = colorListPress.getColorForState(getDrawableState(), 0);
 80             }
 81             //設置背景圖片,若backColor與backGroundDrawable同時存在,則backGroundDrawable將覆蓋backColor
 82             backGroundDrawable = a.getDrawable(R.styleable.buttonM_backGroundImage);
 83             if (backGroundDrawable != null){
 84                 setBackgroundDrawable(backGroundDrawable);
 85             }
 86             //記錄按鈕被按下時的背景圖片
 87             backGroundDrawablePress = a.getDrawable(R.styleable.buttonM_backGroundImagePress);
 88             //設置文字的顏色
 89             textColor = a.getColorStateList(R.styleable.buttonM_textColor);
 90             if (textColor != null){
 91                 setTextColor(textColor);
 92             }
 93             //記錄按鈕被按下時文字的顏色
 94             textColorPress = a.getColorStateList(R.styleable.buttonM_textColorPress);
 95             //設置圓角或圓形等樣式的背景色
 96             fillet = a.getBoolean(R.styleable.buttonM_fillet, false);
 97             if (fillet){
 98                 getGradientDrawable();
 99                 if (backColor != 0) {
100                     gradientDrawable.setColor(backColor);
101                     setBackgroundDrawable(gradientDrawable);
102                 }
103             }
104             //設置圓角矩形的角度,fillet為true時才生效
105             float radius = a.getFloat(R.styleable.buttonM_radius, 0);
106             if (fillet && radius != 0){
107                 setRadius(radius);
108             }
109             //設置按鈕形狀,fillet為true時才生效
110             int shape = a.getInteger(R.styleable.buttonM_shape, 0);
111             if (fillet && shape != 0) {
112                 setShape(shape);
113             }
114             a.recycle();
115         }
116         setOnTouchListener(new OnTouchListener() {
117             @Override
118             public boolean onTouch(View arg0, MotionEvent event) {
119                 //根據touch事件設置按下抬起的樣式
120                 return setTouchStyle(event.getAction());
121             }
122         });
123     }
124 
125     /**
126      * 根據按下或者抬起來改變背景和文字樣式
127      * @param state
128      * @return isCost
129      *  為解決onTouch和onClick沖突的問題
130      *  根據事件分發機制,如果onTouch返回true,則不響應onClick事件
131      *  因此采用isCost標識位,當用戶設置了onClickListener則onTouch返回false
132      */
133     private boolean setTouchStyle(int state){
134         if (state == MotionEvent.ACTION_DOWN) {
135             if (backColorPress != 0) {
136                 if (fillet){
137                     gradientDrawable.setColor(backColorPress);
138                     setBackgroundDrawable(gradientDrawable);
139                 }else {
140                     setBackgroundColor(backColorPress);
141                 }
142             }
143             if (backGroundDrawablePress != null) {
144                 setBackgroundDrawable(backGroundDrawablePress);
145             }
146             if (textColorPress != null) {
147                 setTextColor(textColorPress);
148             }
149         }
150         if (state == MotionEvent.ACTION_UP) {
151             if (backColor != 0) {
152                 if (fillet){
153                     gradientDrawable.setColor(backColor);
154                     setBackgroundDrawable(gradientDrawable);
155                 }else {
156                     setBackgroundColor(backColor);
157                 }
158             }
159             if (backGroundDrawable != null) {
160                 setBackgroundDrawable(backGroundDrawable);
161             }
162             if (textColor != null) {
163                 setTextColor(textColor);
164             }
165         }
166         return isCost;
167     }
168 
169     /**
170      * 重寫setOnClickListener方法,解決onTouch和onClick沖突問題
171      * @param l
172      */
173     @Override
174     public void setOnClickListener(OnClickListener l) {
175         super.setOnClickListener(l);
176         isCost = false;
177     }
178 
179     /**
180      * 設置按鈕的背景色
181      * @param backColor
182      */
183     public void setBackColor(int backColor) {
184         this.backColor = backColor;
185         if (fillet){
186             gradientDrawable.setColor(backColor);
187             setBackgroundDrawable(gradientDrawable);
188         }else {
189             setBackgroundColor(backColor);
190         }
191     }
192 
193     /**
194      * 設置按鈕被按下時的背景色
195      * @param backColorPress
196      */
197     public void setBackColorPress(int backColorPress) {
198         this.backColorPress = backColorPress;
199     }
200 
201     /**
202      * 設置按鈕的背景圖片
203      * @param backGroundDrawable
204      */
205     public void setBackGroundDrawable(Drawable backGroundDrawable) {
206         this.backGroundDrawable = backGroundDrawable;
207         setBackgroundDrawable(backGroundDrawable);
208     }
209 
210     /**
211      * 設置按鈕被按下時的背景圖片
212      * @param backGroundDrawablePress
213      */
214     public void setBackGroundDrawablePress(Drawable backGroundDrawablePress) {
215         this.backGroundDrawablePress = backGroundDrawablePress;
216     }
217 
218     /**
219      * 設置文字的顏色
220      * @param textColor
221      */
222     public void setTextColor(int textColor) {
223         if (textColor == 0) return;
224         this.textColor = ColorStateList.valueOf(textColor);
225         //此處應加super關鍵字,調用父類的setTextColor方法,否則會造成遞歸導致內存溢出
226         super.setTextColor(this.textColor);
227     }
228 
229     /**
230      * 設置按鈕被按下時文字的顏色
231      * @param textColorPress
232      */
233     public void setTextColorPress(int textColorPress) {
234         if (textColorPress == 0) return;
235         this.textColorPress = ColorStateList.valueOf(textColorPress);
236     }
237 
238     /**
239      * 設置按鈕是否設置圓角或者圓形等樣式
240      * @param fillet
241      */
242     public void setFillet(boolean fillet){
243         this.fillet = fillet;
244         getGradientDrawable();
245     }
246 
247     /**
248      * 設置圓角按鈕的角度
249      * @param radius
250      */
251     public void setRadius(float radius){
252         if (!fillet) return;
253         getGradientDrawable();
254         gradientDrawable.setCornerRadius(radius);
255         setBackgroundDrawable(gradientDrawable);
256     }
257 
258     /**
259      * 設置按鈕的形狀
260      * @param shape
261      */
262     public void setShape(int shape){
263         if (!fillet) return;
264         getGradientDrawable();
265         gradientDrawable.setShape(shape);
266         setBackgroundDrawable(gradientDrawable);
267     }
268 
269     private void getGradientDrawable() {
270         if (gradientDrawable == null){
271             gradientDrawable = new GradientDrawable();
272         }
273     }
274 
275 }

注釋基本上寫的比較詳細,下面主要說一下這里面涉及的一些知識點 
1 關於onTouch返回值問題,如果返回true表示要消費該點擊事件,后續的所有事件都交給他處理,同時onTouchEvent將不會執行,因此onClick也得不到執行,在這里通過重寫setOnClickListener設置變量來改變返回值。具體關於View的事件分發機制可以查閱有關文檔,網上很多這方面的教程。

2 如果想要通過java代碼來設置圓角或者圓形時,必須先設置setFillet(true),然后再設置背景色,形狀或者角度等參數。通過xml文件則無限制

最后講一下怎么使用,這里以設置圓角矩形為例,分別通過xml和java代碼實現,其他的可參考源碼。

1 xml

 1 <com.landptf.view.ButtonM
 2     android:id="@+id/btm_radius_color_xml"
 3     android:layout_width="0dp"
 4     android:layout_height="50dp"
 5     android:layout_weight="1"
 6     android:gravity="center"
 7     android:text="點擊改變背景色"
 8     landptf:backColor="#ff3300"
 9     landptf:backColorPress="#ff33ff"
10     landptf:fillet="true"
11     landptf:radius="30"
12     landptf:textColor="@android:color/white" />

2 java

 1 ButtonM btmRadiusColorJava = (ButtonM) findViewById(R.id.btm_radius_color_java);
 2 if (btmRadiusColorJava != null) {
 3     btmRadiusColorJava.setFillet(true);
 4     btmRadiusColorJava.setRadius(30);
 5     btmRadiusColorJava.setTextColor(Color.parseColor("#ffffff"));
 6     btmRadiusColorJava.setBackColor(Color.parseColor("#ff3300"));
 7     btmRadiusColorJava.setBackColorPress(Color.parseColor("#ff33ff"));
 8     btmRadiusColorJava.setOnClickListener(new View.OnClickListener() {
 9         @Override
10         public void onClick(View v) {
11             Toast.makeText(ButtonMTestActivity.this, "java代碼實現", Toast.LENGTH_SHORT).show();
12         }
13     });
14 }

代碼已托管到開源中國的碼雲上,歡迎下載,地址:https://git.oschina.net/landptf/landptf.git


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM