首页 > 代码库 > 自定义控件:滑动开关按钮(自定义属性)

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

     

 

【主要步骤】

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 =http://www.mamicode.com/ 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     @Override144     // 测量尺寸时的回调方法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     // @Override156     // 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     @Override162     /**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     @Override180     /**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     @Override197     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             // 将本次的位置 设置给lastX217             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 <= maxLeft265          * slideBtn左边届最大值266          */267         int maxLeft = backgroundBitmap.getWidth() - slideBtn.getWidth();268 269         // 确保 slideBtn_left >= 0270         slideBtn_left = (slideBtn_left > 0) ? slideBtn_left : 0;271 272         // 确保 slideBtn_left <=maxLeft273         slideBtn_left = (slideBtn_left < maxLeft) ? slideBtn_left : maxLeft;274 275         // 刷新当前视图 导致 执行onDraw执行276         invalidate();277     }278 279 }

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

 

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