首页 > 代码库 > 自定义控件:滑动开关按钮(自定义属性)
自定义控件:滑动开关按钮(自定义属性)
【主要步骤】
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
自定义控件:滑动开关按钮(自定义属性)
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。