自定義控件(模仿微信ToggleButton控件)


弄過android開發的都知道,系統有一個默認的ToggleButton,但很多人都覺得他很難看,當然也包括我。如果你感覺他不難看,那你就繼續使用系統的吧,這篇文章對你來說是多余的了。

今天來寫一個模仿微信的ToggleButton控件,是啊,模仿都是模仿"大家之作",騰訊、360等等,也確實,他們設計出來的東西確實好看。

下面看效果圖打開狀態,關閉狀態

先奉獻上三張圖片的素材,打開背景圖,關閉背景圖,滑塊背景圖。這里背景圖高度是小於前兩個背景圖2個像素的。這樣才能出現最終的打開和關閉后的滑塊上下出現1個像素背景的效果。

開始我們的控件代碼,這里我們自定義一個ToggleButton控件,當然要繼承自View,下面貼出我的代碼

  1 public class ToggleButton extends View {
  2 
  3     private Bitmap onBackgroundImage;
  4     private Bitmap offBackgroundImage;
  5     private Bitmap blockImage;
  6     private ToggleState state = ToggleState.Open;
  7     private int currentX;
  8     private int mouseDownX = -1;
  9     private boolean isMove = false;
 10     private ToggleState preState;
 11     private boolean isInvalidate = true;
 12      public ToggleButton(Context context) {
 13      super(context);
 14      // TODO Auto-generated constructor stub
 15      }
 16 
 17     
 18     private OnClickListener clickListener = null;
 19 
 20     /*
 21      * 其實應該是stateChange事件,懶得改了
 22      */
 23     public void SetOnClickListener(OnClickListener listener) {
 24         if (listener != null)
 25             clickListener = listener;
 26     }
 27 
 28     @Override
 29     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
 30         // TODO Auto-generated method stub
 31         super.onLayout(changed, left, top, right, bottom);
 32         //layout方法是在ondraw方法之前執行,
 33         //在這個做判斷是防止用戶在設置Image之前setState,
 34         //這樣Image為null,系統無法獲取block寬度,也就無法設置currentX坐標,
 35         //系統將采用默認Open狀態,會出現即使用戶設置狀態為close,而界面顯示仍未open狀態,
 36         //在這里判斷,是因為當前image和state肯定已經設置完畢了
 37         if (state == ToggleState.Open) {
 38             currentX = 2;
 39         } else {
 40             if (blockImage == null || offBackgroundImage == null || onBackgroundImage == null)
 41                 return;
 42             currentX=offBackgroundImage.getWidth()-blockImage.getWidth()-2;
 43         }
 44     }
 45     public enum ToggleState {
 46         Open, Close
 47     }
 48 
 49     public ToggleButton(Context context, AttributeSet attrs) {
 50         super(context, attrs);
 51         // TODO Auto-generated constructor stub
 52     }
 53 
 54     public void setOnBackgroundResource(int id) {
 55 
 56         onBackgroundImage = BitmapFactory.decodeResource(getResources(), id);
 57     }
 58 
 59     public void setOffBackgroundResource(int id) {
 60         offBackgroundImage = BitmapFactory.decodeResource(getResources(), id);
 61 
 62     }
 63 
 64     public void setState(ToggleState state) {
 65         preState = this.state = state;
 66         if (state == ToggleState.Open) {
 67             currentX = 2;
 68         } else {
 69             if (blockImage != null && offBackgroundImage != null)
 70                 currentX = offBackgroundImage.getWidth() - blockImage.getWidth() - 2;
 71         }
 72         invalidate();
 73     }
 74 
 75     public ToggleState getState() {
 76         return state;
 77     }
 78 
 79     public void setBlockResource(int id) {
 80         blockImage = BitmapFactory.decodeResource(getResources(), id);
 81     }
 82 
 83 
 84     @Override
 85     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 86         // TODO Auto-generated method stub
 87         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 88         setMeasuredDimension(onBackgroundImage.getWidth(), onBackgroundImage.getHeight());
 89 
 90     }
 91 
 92     @Override
 93     protected void onDraw(Canvas canvas) {
 94         // TODO Auto-generated method stub
 95         super.onDraw(canvas);
 96         // 有效性判斷,如果其中有一個為空,拒絕繪制,繼續繪制也沒有意義
 97         if (blockImage == null || offBackgroundImage == null || onBackgroundImage == null)
 98             return;
 99         if ((currentX + blockImage.getWidth() / 2) > onBackgroundImage.getWidth() / 2) {
100             canvas.drawBitmap(offBackgroundImage, 0, 0, null);
101         } else {
102             canvas.drawBitmap(onBackgroundImage, 0, 0, null);
103         }
104         canvas.drawBitmap(blockImage, currentX, 1, null);
105     }
106 
107     @Override
108     public boolean onTouchEvent(MotionEvent event) {
109         currentX = (int) event.getX();
110         switch (event.getAction()) {
111         case MotionEvent.ACTION_DOWN:
112             // 記錄鼠標按下時的x
113             mouseDownX = (int) event.getX();
114             // 按下的時候不進行重繪
115             isInvalidate = false;
116             break;
117         case MotionEvent.ACTION_MOVE:
118             // 記錄鼠標發生了移動
119             isMove = true;
120             break;
121         case MotionEvent.ACTION_UP:
122             // 判斷鼠標按下和抬起過程中是否發生了移動
123             // 鼠標抬起時,判斷當前x坐標位置
124             if (isMove) {
125                 // 發生了移動,判斷當前位置
126                 if ((currentX + blockImage.getWidth() / 2) > onBackgroundImage.getWidth() / 2) {
127                     // 背景圖的后半段
128                     currentX = onBackgroundImage.getWidth() - blockImage.getWidth();
129                     state = ToggleState.Close;
130                 } else {
131                     // 背景圖的前半段
132                     currentX = 2;
133                     state = ToggleState.Open;
134                 }
135             } else {
136                 // 沒有發生移動,即為點擊事件,更改狀態,同時改變滑塊位置
137                 if (state == ToggleState.Open) {
138                     state = ToggleState.Close;
139                     currentX = onBackgroundImage.getWidth() - blockImage.getWidth() - 2;
140                 } else {
141                     state = ToggleState.Open;
142                     currentX = 2;
143                 }
144             }
145             // 復位,以免影響下一次的觸摸事件
146             isMove = false;
147             if (preState != state && clickListener != null) {
148                 clickListener.onClick(this);
149                 preState = state;
150             }
151             break;
152         }
153         if (currentX < 2)
154             currentX = 2;
155         if (currentX + blockImage.getWidth() >= onBackgroundImage.getWidth())
156             currentX = onBackgroundImage.getWidth() - blockImage.getWidth() - 2;
157         // 通知控件繪制
158         if (isInvalidate)
159             invalidate();
160         else
161             isInvalidate = true;
162         return true;
163     }
164 
165 }
View Code

代碼里的注釋夠多吧,哈哈,所以就不進行講解了。

接下來我們在activity布局文件里面使用這個ToggleButton 控件

1 <包名.ToggleButton 
2         android:layout_width="wrap_content"
3         android:layout_height="wrap_content"
4         android:id="@+id/testToggleButton"
5         />
View Code

我們在activity里面使用吧

 1 final ToggleButton toggle=(ToggleButton) findViewById(R.id.testToggleButton);
 2         toggle.setState(ToggleState.Close);
 3         toggle.setOnBackgroundResource(R.drawable.on);
 4         toggle.setOffBackgroundResource(R.drawable.off);
 5         toggle.setBlockResource(R.drawable.block);
 6         
 7         toggle.SetOnClickListener(new OnClickListener() {
 8             @Override
 9             public void onClick(View v) {
10                 // TODO Auto-generated method stub
11                 String state=(toggle.getState()==ToggleState.Open?"打開":"關閉");
12                 Toast.makeText(MainActivity.this, "當前狀態:"+state, Toast.LENGTH_SHORT).show();
13             }
14         });
View Code

注意啊,我上面的一行代碼的位置

toggle.setState(ToggleState.Close);

設置狀態這行代碼放在了設置圖片之前,但效果仍然是關閉狀態。

但ToggleButton里面如果不重寫OnLayout方法,顯示出來的狀態就只能是打開狀態。

 


免責聲明!

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



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