标签:
1. 开关按钮点击效果,如下:
2. 继承已有View实现自定义View
3. 下面通过一个案例实现滑动开关的案例:
(1)新建一个新的Android工程,命名为" 开关按钮",接下来我们按照上面的步骤来:自定义类MyToggleButton继承自View。
(2)编写设计activity_main.xml布局文件,如下:
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 tools:context="com.himi.togglebtn.MainActivity" > 6 7 <com.himi.togglebtn.MyToggleButton 8 android:layout_width="wrap_content" 9 android:layout_height="wrap_content" 10 android:id="@+id/my_toggle_btn" 11 android:layout_centerHorizontal="true" 12 android:layout_centerVertical="true"/> 13 14 </RelativeLayout>
注意这里使用的自定的MyToggleButton(View),要使用全路径。
(3)编写自定义的MyToggleButton。
• 重写onMeasure()方法,指定控件大小;
• 重写onDraw()方法,绘制控件内容。
1 package com.himi.togglebtn; 2 3 import android.content.Context; 4 import android.graphics.Bitmap; 5 import android.graphics.BitmapFactory; 6 import android.graphics.Canvas; 7 import android.graphics.Paint; 8 import android.util.AttributeSet; 9 import android.view.View; 10 import android.view.View.OnClickListener; 11 12 public class MyToggleButton extends View implements OnClickListener { 13 14 //作为背景的图片 15 private Bitmap backgroundBitmap; 16 //滑动的开关图片 17 private Bitmap slidebtn; 18 private Paint paint; 19 20 //滑动按钮的左边界 21 private float slidebtn_left; 22 23 /** 24 * 当前开关的状态 25 * true :为开 26 * false:为关 27 */ 28 private boolean currState = false; 29 30 31 /** 32 * 我们在代码里面创建对象的时候,使用此构造方法 33 * @param context 34 */ 35 public MyToggleButton(Context context) { 36 super(context); 37 // TODO 自动生成的构造函数存根 38 } 39 40 /** 41 * 在布局文件xml中声明的View,创建时候由系统自动调用此构造方法。 42 * 倘若我们(使用全路径)在xml布局文件中,声明使用这个自定义的View,但是我们没有这个构造方法,就会报错(系统不能找到这个构造) 43 * @param context 44 * @param attrs 45 */ 46 public MyToggleButton(Context context, AttributeSet attrs) { 47 super(context, attrs); 48 initView(); 49 } 50 51 52 /** 53 * 这个构造方法比上面的构造方法都了一个参数 defStyle,这个参数View默认的样式,这里可以重新这个构造,设置defStyle 54 * 改变生成自定义的View的样式style 55 * @param context 56 * @param attrs 57 * @param defStyle 58 */ 59 public MyToggleButton(Context context, AttributeSet attrs, int defStyle) { 60 super(context, attrs, defStyle); 61 // TODO 自动生成的构造函数存根 62 } 63 64 65 //初始化 66 private void initView() { 67 //初始化图片 68 backgroundBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.switch_background); 69 slidebtn = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button); 70 //初始化画笔 71 paint = new Paint(); 72 paint.setAntiAlias(true);//打开抗锯齿 73 //添加Onclick事件监听 74 setOnClickListener(this); 75 } 76 77 /* 78 * View对象显示在屏幕上,有几个重要步骤: 79 * 1. 构造方法 创建 对象. 80 * 2. 测量View的大小. onMeasure(int, int):系统调用的方法,获知View的大小 81 * 3. 确定View的位置,View自身有一些建议权,View位置决定权在父View手中. onLayout(): ViewGroup调用 82 * 4. 绘制View的内容 onDraw(canvas) 83 * 84 */ 85 86 87 88 /** 89 * 90 * 测量尺寸时候的回调方法 91 */ 92 @Override 93 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 94 95 //super.onMeasure(widthMeasureSpec, heightMeasureSpec); 96 /** 97 * 设置当前View的大小 98 * width :当前View的宽度 99 * height:当前view的高度(单位:像素) 100 */ 101 setMeasuredDimension(backgroundBitmap.getWidth(), backgroundBitmap.getHeight()); 102 } 103 104 105 /** 106 * 自定义的View,作用不大 107 * 确定位置的时候,系统调用的方法(我们不用关心),这里我们就不改写这个方法 108 */ 109 @Override 110 protected void onLayout(boolean changed, int left, int top, int right, 111 int bottom) { 112 // TODO 自动生成的方法存根 113 super.onLayout(changed, left, top, right, bottom); 114 } 115 116 /** 117 * 绘制当前View的内容 118 */ 119 @Override 120 protected void onDraw(Canvas canvas) { 121 // TODO 自动生成的方法存根 122 //super.onDraw(canvas); 123 //绘制背景图 124 /* 125 * backgroundBitmap:要绘制的图片 126 * left 图片的左边界 127 * top 图片的上边界 128 * paint 绘制图片要使用的画笔 129 */ 130 canvas.drawBitmap(backgroundBitmap, 0, 0, paint); 131 //绘制可滑动的按钮 132 canvas.drawBitmap(slidebtn, slidebtn_left, 0, paint); 133 } 134 135 public void onClick(View v) { 136 currState = ! currState; 137 flushState();//刷新当前开关状态 138 139 } 140 141 /** 142 * 刷新当前开关视图 143 */ 144 private void flushState() { 145 if(currState) { 146 slidebtn_left = backgroundBitmap.getWidth()-slidebtn.getWidth(); 147 }else { 148 slidebtn_left =0; 149 } 150 151 /* 152 * invalidate():刷新当前View,会导致onDraw方法的执行 153 * 上面只是设置参数设置,下面必须将上面的参数传递到onDraw方法中,利用onDraw方法重新绘制,才能实现刷新的效果 154 * 155 */ 156 invalidate(); 157 158 } 159 160 161 162 163 }
备注:
初始状态slideBtn 左边为0
开的时候slideBtn left值为background.width-slidebtn.width
与此同时,MainActivity.java,如下:
1 package com.himi.togglebtn; 2 3 import android.app.Activity; 4 import android.os.Bundle; 5 6 public class MainActivity extends Activity { 7 8 @Override 9 protected void onCreate(Bundle savedInstanceState) { 10 super.onCreate(savedInstanceState); 11 setContentView(R.layout.activity_main); 12 } 13 14 }
(4)布署程序到模拟器上如下:
(5)上面的只能让开关左右切换,不能手机拖动滑动开关,用户体验不好,我们要优化。
MainActivity.java,修改为:
1 package com.himi.togglebtn; 2 3 import android.content.Context; 4 import android.graphics.Bitmap; 5 import android.graphics.BitmapFactory; 6 import android.graphics.Canvas; 7 import android.graphics.Paint; 8 import android.util.AttributeSet; 9 import android.view.MotionEvent; 10 import android.view.View; 11 import android.view.View.OnClickListener; 12 13 public class MyToggleButton extends View implements OnClickListener { 14 15 //作为背景的图片 16 private Bitmap backgroundBitmap; 17 //滑动的开关图片 18 private Bitmap slidebtn; 19 private Paint paint; 20 21 //滑动按钮的左边界 22 private float slidebtn_left; 23 24 /** 25 * 当前开关的状态 26 * true :为开 27 * false:为关 28 */ 29 private boolean currState = false; 30 31 32 /** 33 * 我们在代码里面创建对象的时候,使用此构造方法 34 * @param context 35 */ 36 public MyToggleButton(Context context) { 37 super(context); 38 // TODO 自动生成的构造函数存根 39 } 40 41 /** 42 * 在布局文件xml中声明的View,创建时候由系统自动调用此构造方法。 43 * 倘若我们(使用全路径)在xml布局文件中,声明使用这个自定义的View,但是我们没有这个构造方法,就会报错(系统不能找到这个构造) 44 * @param context 45 * @param attrs 46 */ 47 public MyToggleButton(Context context, AttributeSet attrs) { 48 super(context, attrs); 49 initView(); 50 } 51 52 53 /** 54 * 这个构造方法比上面的构造方法都了一个参数 defStyle,这个参数View默认的样式,这里可以重新这个构造,设置defStyle 55 * 改变生成自定义的View的样式style 56 * @param context 57 * @param attrs 58 * @param defStyle 59 */ 60 public MyToggleButton(Context context, AttributeSet attrs, int defStyle) { 61 super(context, attrs, defStyle); 62 // TODO 自动生成的构造函数存根 63 } 64 65 66 //初始化 67 private void initView() { 68 //初始化图片 69 backgroundBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.switch_background); 70 slidebtn = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button); 71 //初始化画笔 72 paint = new Paint(); 73 paint.setAntiAlias(true);//打开抗锯齿 74 //添加Onclick事件监听 75 setOnClickListener(this); 76 } 77 78 /* 79 * View对象显示在屏幕上,有几个重要步骤: 80 * 1. 构造方法 创建 对象. 81 * 2. 测量View的大小. onMeasure(int, int):系统调用的方法,获知View的大小 82 * 3. 确定View的位置,View自身有一些建议权,View位置决定权在父View手中. onLayout(): ViewGroup调用 83 * 4. 绘制View的内容 onDraw(canvas) 84 * 85 */ 86 87 88 89 /** 90 * 91 * 测量尺寸时候的回调方法 92 */ 93 @Override 94 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 95 96 //super.onMeasure(widthMeasureSpec, heightMeasureSpec); 97 /** 98 * 设置当前View的大小 99 * width :当前View的宽度 100 * height:当前view的高度(单位:像素) 101 */ 102 setMeasuredDimension(backgroundBitmap.getWidth(), backgroundBitmap.getHeight()); 103 } 104 105 106 /** 107 * 自定义的View,作用不大 108 * 确定位置的时候,系统调用的方法(我们不用关心),这里我们就不改写这个方法 109 */ 110 @Override 111 protected void onLayout(boolean changed, int left, int top, int right, 112 int bottom) { 113 // TODO 自动生成的方法存根 114 super.onLayout(changed, left, top, right, bottom); 115 } 116 117 /** 118 * 绘制当前View的内容 119 */ 120 @Override 121 protected void onDraw(Canvas canvas) { 122 // TODO 自动生成的方法存根 123 //super.onDraw(canvas); 124 //绘制背景图 125 /* 126 * backgroundBitmap:要绘制的图片 127 * left 图片的左边界 128 * top 图片的上边界 129 * paint 绘制图片要使用的画笔 130 */ 131 canvas.drawBitmap(backgroundBitmap, 0, 0, paint); 132 //绘制可滑动的按钮 133 canvas.drawBitmap(slidebtn, slidebtn_left, 0, paint); 134 } 135 136 /** 137 * 判断是否发生拖到 138 * 如果拖动了,就不再响应Onclick事件 139 * true:发生拖动 140 * false:没有发生拖动 141 */ 142 private boolean isDrag = false; 143 144 /** 145 * onClick事件在view.onTouchEvent中被解析 146 * 系统对Onclick事件的解析,过于简陋,只要有down事件和up事件,系统即认为发生了click事件 147 */ 148 public void onClick(View v) { 149 /* 150 * 如果没有拖动,才执行改变状态的动作 151 */ 152 if(!isDrag) { 153 currState = ! currState; 154 flushState();//刷新当前开关状态 155 } 156 } 157 158 /** 159 * 刷新当前开关视图 160 */ 161 private void flushState() { 162 if(currState) { 163 slidebtn_left = backgroundBitmap.getWidth()-slidebtn.getWidth(); 164 }else { 165 slidebtn_left =0; 166 } 167 168 flushView(); 169 } 170 171 public void flushView() { 172 /** 173 * 对slidebtn_left的值进行判断 174 * 0 <= slidebtn_left <= backgroundwidth-slidebtnwidth(这样才能保证滑动的开关不会滑动越界) 175 * 176 */ 177 int maxLeft = backgroundBitmap.getWidth()-slidebtn.getWidth();//slidebtn左边界最大值 178 //确保slidebtn_left >= 0 179 slidebtn_left =(slidebtn_left>0)?slidebtn_left:0; 180 //确保slidebtn_left <=maxLeft 181 slidebtn_left = (slidebtn_left<maxLeft)?slidebtn_left:maxLeft; 182 183 //告诉系统我需要刷新当前视图,只要当前视图可见状态,就会调用onDraw方法重新绘制,达到刷新视图的效果 184 invalidate(); 185 } 186 187 /** 188 * down事件时的x值 189 */ 190 private int firstX; 191 /** 192 * touch事件的上一个x值 193 */ 194 private int lastX; 195 196 @Override 197 public boolean onTouchEvent(MotionEvent event) { 198 super.onTouchEvent(event); 199 switch(event.getAction()) { 200 case MotionEvent.ACTION_DOWN: 201 firstX = lastX = (int) event.getX(); 202 isDrag = false; 203 break; 204 case MotionEvent.ACTION_MOVE: 205 //判断是否发生拖动 206 if(Math.abs(event.getX()-lastX)>5) { 207 isDrag = true; 208 } 209 210 //计算手指在屏幕上移动的距离 211 int dis = (int) (event.getX()-lastX); 212 //将本次的位置设置给lastX 213 lastX = (int) event.getX(); 214 //根据手指移动的距离,改变slidebtn_left的值 215 slidebtn_left = slidebtn_left+dis; 216 break; 217 case MotionEvent.ACTION_UP: 218 219 //在发生拖动的情况下,根据最后的位置,判断当前的开关的状态 220 if(isDrag){ 221 int maxLeft = backgroundBitmap.getWidth()-slidebtn.getWidth();//slidebtn左边界最大值 222 /** 223 * 根据slidebtn_left判断,当前应该是什么状态 224 * 225 */ 226 if(slidebtn_left>maxLeft/2) {//应为打开状态 227 currState = true; 228 }else { 229 currState = false; 230 } 231 flushState(); 232 } 233 234 break; 235 236 } 237 flushView(); 238 return true; 239 } 240 241 242 243 244 245 246 }
布署程序到模拟器上,如下:
自定义控件(视图)28期笔记05:自定义控件之使用系统控件(开关按钮点击效果)
标签:
原文地址:http://www.cnblogs.com/hebao0514/p/4842166.html