自定义控件:滑动开关按钮(自定义属性)

     

 

【主要步骤】

1、自定义类MyToggleButton继承自view。

2、重写onMeasure方法,指定控件大小。

3、重写onDraw方法,绘制控件内容。

4、重写onTouchEvent方法,对touch事件进行解析。

【为新控件添加自定义的属性】

1、在attrs.xml文件中声明属性,有属性名:name和格式:format=如:

<declare-styleable name="MyToggleBtn">

        <attr name="curr_state" format="boolean" />

</declare-styleable>

2、在布局文件中使用新属性,使用之前必须先声明命名空间,如:

xmlns:heihei=http://schemas.android.com/apk/res/com.example.testdemo

说明:xmlns是XML name space的缩写;

         heihei 可以任意写;

         http://schemas.android.com/apk/res/   此为android固定格式;

         com.example.testdemo   此应用的包名,如manifest配置文件中一致。

3、在自定义view的构造方法当中,通过解析AttributeSet对象,获得所需要的属性值。

  

 1 <?xml version="1.0" encoding="utf-8"?>  2 <!-- res/values/attrs.xml -->  3 <resources>  4  5 <!-- 声名属性集的名称 -->  6 <declare-styleable name="MyToggleBtn">  7  8 <!-- 声名一个属性 name是my_background 类型为 引用类型 引用资源ID -->  9 <attr name="my_background" format="reference" /> 10 11 <!-- 声名一个属性 name是my_slide_btn 类型为 引用类型 引用资源ID --> 12 <attr name="my_slide_btn" format="reference" /> 13 14 <!-- 声名一个属性 name是curr_state 类型为 boolean 类型--> 15 <attr name="curr_state" format="boolean" /> 16 17 </declare-styleable> 18 20 </resources>

 

 1 <RelativeLayout  2 xmlns:android="http://schemas.android.com/apk/res/android"  3  xmlns:heihei="http://schemas.android.com/apk/res/com.example.testdemo"  4  xmlns:tools="http://schemas.android.com/tools"  5  android:layout_width="match_parent"  6  android:layout_height="match_parent" >  8 <com.example.testdemo.MyToggleButton  9 android:id="@+id/my_toggle_btn" 10  android:layout_width="wrap_content" 11  android:layout_height="wrap_content" 12  android:layout_centerHorizontal="true" 13  android:layout_centerVertical="true" 14  heihei:my_background="@drawable/switch_background" 开关图片 15  heihei:my_slide_btn="@drawable/slide_button" 灰色图片 16  heihei:curr_state="false" 17  testAttrs="@drawable/ic_launcher" /> 19 </RelativeLayout>

 

  1 package com.example.testdemo;  2  3 import android.content.Context;  4 import android.content.res.TypedArray;  5 import android.graphics.Bitmap;  6 import android.graphics.BitmapFactory;  7 import android.graphics.Canvas;  8 import android.graphics.Paint;  9 import android.util.AttributeSet;  10 import android.util.Log;  11 import android.view.MotionEvent;  12 import android.view.View;  13 import android.view.View.OnClickListener;  14  15 public class MyToggleButton extends View implements OnClickListener {  16 public static final String TAG = "MyToggleButton";  17  18 // 做为背景的图片  19 private Bitmap backgroundBitmap;  21 // 可以滑动的图片  22 private Bitmap slideBtn;  23 private Paint paint;  25 // 滑动按钮的左边届  26 private float slideBtn_left;  28 // 背景图的资源ID  29 private int backgroundId;  31 // 滑动图片的资源ID  32 private int slideBtnId;  34 // 当前开关的状态 true 为开  35 private boolean currState = false;  37 // 判断是否发生拖动, 如果拖动了,就不再响应 onclick 事件  38 private boolean isDrag = false;  40 // down 事件时的x值  41 private int firstX;  43 // touch 事件的上一个x值  44 private int lastX;  45  46 // 在代码里面创建对象的时候,使用此构造方法  47 public MyToggleButton(Context context) {  48 super(context);  49  }  50  51 /**  52  * 在布局文件中声名的view,创建时由系统自动调用。  53  *  54  * @param context  55  * 上下文对象  56  * @param attrs  57  * 属性集  58 */  59 public MyToggleButton(Context context, AttributeSet attrs) {  60 super(context, attrs);  61 //-------------------  62 int count = attrs.getAttributeCount();  63 for (int i = 0; i < count; i++) {  64 String name = attrs.getAttributeName(i);  65 String value = attrs.getAttributeValue(i);  66 Log.i(TAG, "name:" + name+ "value:" + value);  67  }  68 //-------------------  69  70 // 无命名空间测试  71 String testAttrs = attrs.getAttributeValue(null, "testAttrs");  72  73 System.out.println("testAttrs===:" + testAttrs);  74  75 // 获得自定义的属性  76 // (比如)第一个参数:原材料,第二个参数:图纸。  77 // TypedArray相当于是一个加工厂,把原材料加工成图纸上面说的。  78 TypedArray ta = context.obtainStyledAttributes(attrs,  79  R.styleable.MyToggleBtn);  80  81 int N = ta.getIndexCount();  82 for (int i = 0; i < N; i++) {  83 // 获得某个属性的ID值  84 int itemId = ta.getIndex(i);  85 switch (itemId) {  86 case R.styleable.MyToggleBtn_curr_state:  87 currState = ta.getBoolean(itemId, false);  88  89 break;  90 case R.styleable.MyToggleBtn_my_background:  91 backgroundId = ta.getResourceId(itemId, -1);  92 if (backgroundId == -1) {  93 throw new RuntimeException("请设置背景图片");  94  }  95 backgroundBitmap = BitmapFactory.decodeResource(getResources(),  96  backgroundId);  97  98 break;  99 case R.styleable.MyToggleBtn_my_slide_btn: 100 slideBtnId = ta.getResourceId(itemId, -1); 101 slideBtn = BitmapFactory.decodeResource(getResources(), 102  slideBtnId); 103 104 break; 105 106 default: 107 break; 108  } 109 110  } 111  initView(); 112  } 113 114 /** 115  * 初始化 116 */ 117 private void initView() { 118 // 初始化图片 119 // backgroundBitmap = BitmapFactory.decodeResource(getResources(), 120 // R.drawable.switch_background); 121 // slideBtn = BitmapFactory.decodeResource(getResources(), 122 // R.drawable.slide_button); 123 124 // 初始化 画笔 125 paint = new Paint(); 126 // 打开抗矩齿 127 paint.setAntiAlias(true); 128 // 添加onclick事件监听 129 setOnClickListener(this); 130 131  flushState(); 132  } 133 134 /* 135  * view 对象显示的屏幕上,有几个重要步骤: 136  * 1、构造方法 创建 对象。 137  * 2、测量view的大小。 onMeasure(int,int); 138  * 3、确定view的位置 ,view自身有一些建议权,决定权在 父view手中。 onLayout(); 139  * 4、绘制 view 的内容 。 140  * onDraw(Canvas) 141 */ 142 143  @Override 144 // 测量尺寸时的回调方法 145 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 146 // super.onMeasure(widthMeasureSpec, heightMeasureSpec); 147 148 // 设置当前view的大小 width :view的宽度 height :view的高度 (单位:像素) 149  setMeasuredDimension(backgroundBitmap.getWidth(), 150  backgroundBitmap.getHeight()); 151  } 152 153 // 确定位置的时候调用此方法 154 // 自定义view的时候,作用不大 155 // @Override 156 // protected void onLayout(boolean changed, int left, int top, int right, 157 // int bottom) { 158 // super.onLayout(changed, left, top, right, bottom); 159 // } 160 161  @Override 162 /** 163  * 绘制当前view的内容 164 */ 165 protected void onDraw(Canvas canvas) { 166 // super.onDraw(canvas); 167 168 // 绘制 背景 169 /* 170  * backgroundBitmap 要绘制的图片 left 图片的左边届 top 171  * 图片的上边届 paint 绘制图片要使用的画笔 172 */ 173 canvas.drawBitmap(backgroundBitmap, 0, 0, paint); 174 175 // 绘制 可滑动的按钮 176 canvas.drawBitmap(slideBtn, slideBtn_left, 0, paint); 177  } 178 179  @Override 180 /** 181  * onclick 事件在View.onTouchEvent 中被解析。 182  * 系统对onclick 事件的解析,过于简陋,只要有down 事件 up 事件, 183  * 系统即认为 发生了click 事件 184  * 185 */ 186 public void onClick(View v) { 187 /* 188  * 如果没有拖动,才执行改变状态的动作 189 */ 190 if (!isDrag) { 191 currState = !currState; 192  flushState(); 193  } 194  } 195 196  @Override 197 public boolean onTouchEvent(MotionEvent event) { 198 super.onTouchEvent(event); 199 200 switch (event.getAction()) { 201 case MotionEvent.ACTION_DOWN: 202 firstX = lastX = (int) event.getX(); 203 isDrag = false; 204 205 break; 206 case MotionEvent.ACTION_MOVE: 207 208 // 判断是否发生拖动 209 if (Math.abs(event.getX() - firstX) > 5) { 210 isDrag = true; 211  } 212 213 // 计算 手指在屏幕上移动的距离 214 int dis = (int) (event.getX() - lastX); 215 216 // 将本次的位置 设置给lastX 217 lastX = (int) event.getX(); 218 219 // 根据手指移动的距离,改变slideBtn_left 的值 220 slideBtn_left = slideBtn_left + dis; 221 break; 222 case MotionEvent.ACTION_UP: 223 224 // 在发生拖动的情况下,根据最后的位置,判断当前开关的状态 225 if (isDrag) { 226 // slideBtn左边届最大值 227 int maxLeft = backgroundBitmap.getWidth() - slideBtn.getWidth(); 228 // 根据 slideBtn_left 判断,当前应是什么状态 229 // 此时应为 打开的状态 230 if (slideBtn_left > maxLeft / 2) { 231 currState = true; 232 } else { 233 currState = false; 234  } 235 236  flushState(); 237  } 238 break; 239  } 240 241  flushView(); 242 243 return true; 244  } 245 246 /** 247  * 刷新当前状态 248 */ 249 private void flushState() { 250 if (currState) { 251 slideBtn_left = backgroundBitmap.getWidth() - slideBtn.getWidth(); 252 } else { 253 slideBtn_left = 0; 254  } 255 256  flushView(); 257  } 258 259 /** 260  * 刷新当前视力 261 */ 262 private void flushView() { 263 /* 264  * 对 slideBtn_left 的值进行判断 ,确保其在合理的位置 即 0<=slideBtn_left <= maxLeft 265  * slideBtn左边届最大值 266 */ 267 int maxLeft = backgroundBitmap.getWidth() - slideBtn.getWidth(); 268 269 // 确保 slideBtn_left >= 0 270 slideBtn_left = (slideBtn_left > 0) ? slideBtn_left : 0; 271 272 // 确保 slideBtn_left <=maxLeft 273 slideBtn_left = (slideBtn_left < maxLeft) ? slideBtn_left : maxLeft; 274 275 // 刷新当前视图 导致 执行onDraw执行 276  invalidate(); 277  } 278 279 }

DEMO下载地址:http://pan.baidu.com/s/1jGsrvM2

 

你可能感兴趣的:(自定义控件)