自定义控件:开关按钮的实现

来源:互联网 发布:淘宝店雪亮卫生纸 编辑:程序博客网 时间:2024/05/01 06:37

自定义点击开关按钮

一、继承已有View实现自定义View
通过对android原生控件的研究,可以发现android中的控件都是继承view类,如textView、ImageView等,通过重写相关的方法来实现新的果,通过这个我们得到两点:
1.我们可以在已有控件的基础上,通过重写相关方法来实现我们的需求。
2.继承view类或viewgroup类,来创建我们所需要的控件。一般来讲,通过继承已有的控件,来自定义控件要简单一点。

/** * 自定义按钮 * @author afu */public class MyToggleButton extends View {// 增加一个默认显示样式时候使用public MyToggleButton(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}// 在布局文件中声明view的时候,该方法有系统调用public MyToggleButton(Context context, AttributeSet attrs) {super(context, attrs);}// 在代码中new实例化时调用public MyToggleButton(Context context) {super(context);}}
布局文件activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent" >    <com.xbmu.toggle.MyToggleButton        android:layout_marginTop="50dp"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_centerHorizontal="true" /></RelativeLayout>
二、一个View从创建到显示屏幕的步骤
1.执行view构造方法,创建对象
2.测量view大小
    onMeasure(int,int);来完成测量动作
3.指定view的位置,子View只有建议权,父View才有决定权;
onLayout(boolean,int,int,int ,int);
这个方法一般用不着,如果自定义ViewGoup才用到
4.绘制view的内容
onDraw(canvas);
三、画个矩形背景和圆形
package com.xbmu.togglebutton;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.util.AttributeSet;import android.view.View;/** * 自定按钮 * @author afu */public class MyToggleButton extends View {/** * 一个View从创建到显示屏幕上的主要步骤: * 1.执行view构造方法,创建对象 * 2.测量view大小 *  onMeasure(int,int);来完成测量动作 * 3.指定view的位置,子View只有建议权,父View才有决定权; * onLayout(boolean,int,int,int ,int); * 这个方法一般用不着,如果自定义ViewGoup才用到 * 4.绘制view的内容 * onDraw(canvas); *  */private Paint paint;/** * 测量 */@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//super.onMeasure(widthMeasureSpec, heightMeasureSpec);//设置当前view的测量大小setMeasuredDimension(100, 100);}/** * 绘制 */@Overrideprotected void onDraw(Canvas canvas) {//super.onDraw(canvas);//绘制颜色,可以理解成背景颜色canvas.drawColor(Color.RED);//绘制圆形canvas.drawCircle(50, 50, 20, paint);}private void init(Context context) {paint = new Paint();paint.setColor(Color.GREEN);//设置抗锯齿,让边缘圆滑,一般都会设置paint.setAntiAlias(true);}// 增加一个默认显示样式时候使用public MyToggleButton(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init(context);}// 在布局文件中声明view的时候,该方法有系统调用public MyToggleButton(Context context, AttributeSet attrs) {super(context, attrs);init(context);}// 在代码中new实例化时调用public MyToggleButton(Context context) {super(context);init(context);}}
四、画按钮背景
package com.xbmu.togglebutton;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.util.AttributeSet;import android.view.View;import android.widget.ImageView;/** * 自定按钮 * @author afu */public class MyToggleButton extends View {/** * 一个View从创建到显示屏幕上的主要步骤: * 1.执行view构造方法,创建对象 * 2.测量view大小 *  onMeasure(int,int);来完成测量动作 * 3.指定view的位置,子View只有建议权,父View才有决定权; * onLayout(boolean,int,int,int ,int); * 这个方法一般用不着,如果自定义ViewGoup才用到 * 4.绘制view的内容 * onDraw(canvas); *  */private Paint paint;private Bitmap backGroundBitmap;private Bitmap slideBitmap;private Context context;/** * 测量 */@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//super.onMeasure(widthMeasureSpec, heightMeasureSpec);//设置当前view的测量大小setMeasuredDimension(backGroundBitmap.getWidth(), backGroundBitmap.getHeight());}/** * 绘制 */@Overrideprotected void onDraw(Canvas canvas) {//super.onDraw(canvas);//绘制颜色,可以理解成背景颜色//canvas.drawColor(Color.RED);//绘制圆形//canvas.drawCircle(50, 50, 20, paint);canvas.drawBitmap(backGroundBitmap, 0, 0, paint);}private void init(Context context) {this.context = context;paint = new Paint();paint.setColor(Color.GREEN);//设置抗锯齿,让边缘圆滑,一般都会设置paint.setAntiAlias(true);//初始化图片-从资源文件中解析成Bitmap对象slideBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button);backGroundBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.switch_background);}// 增加一个默认显示样式时候使用public MyToggleButton(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init(context);}// 在布局文件中声明view的时候,该方法有系统调用public MyToggleButton(Context context, AttributeSet attrs) {super(context, attrs);init(context);}// 在代码中new实例化时调用public MyToggleButton(Context context) {super(context);init(context);}}
五、画滑动按钮
canvas.drawBitmap(slideBitmap, 45, 0, paint);
分别设置0和30运行看看效果
六、点击时改变按钮状态
package com.itheima.togglebutton;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.util.AttributeSet;import android.view.View;import android.widget.ImageView;/** * 自定按钮 * @author afu */public class MyToggleButton extends View implements  View.OnClickListener {/** * 一个View从创建到显示屏幕上的主要步骤: * 1.执行view构造方法,创建对象 * 2.测量view大小 *  onMeasure(int,int);来完成测量动作 * 3.指定view的位置,子View只有建议权,父View才有决定权; * onLayout(boolean,int,int,int ,int); * 这个方法一般用不着,如果自定义ViewGoup才用到 * 4.绘制view的内容 * onDraw(canvas); *  */private Paint paint;private Bitmap backGroundBitmap;private Bitmap slideBitmap;private Context context;/** * 距离左边的距离 */private float slideLeft;/** * 判断当前开关状态 * true为开 * false为关 */private boolean curStata = false;/** * 测量 */@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//super.onMeasure(widthMeasureSpec, heightMeasureSpec);//设置当前view的测量大小setMeasuredDimension(backGroundBitmap.getWidth(), backGroundBitmap.getHeight());}/** * 绘制 */@Overrideprotected void onDraw(Canvas canvas) {//super.onDraw(canvas);//绘制颜色,可以理解成背景颜色//canvas.drawColor(Color.RED);//绘制圆形//canvas.drawCircle(50, 50, 20, paint);canvas.drawBitmap(backGroundBitmap, 0, 0, paint);//绘制滑动按钮canvas.drawBitmap(slideBitmap, slideLeft, 0, paint);}private void init(Context context) {this.context = context;paint = new Paint();paint.setColor(Color.GREEN);//设置抗锯齿,让边缘圆滑,一般都会设置paint.setAntiAlias(true);//初始化图片-从资源文件中解析成Bitmap对象slideBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button);-backGroundBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.switch_background);setOnClickListener( MyToggleButton.this);}// 增加一个默认显示样式时候使用public MyToggleButton(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init(context);}// 在布局文件中声明view的时候,该方法有系统调用public MyToggleButton(Context context, AttributeSet attrs) {super(context, attrs);init(context);}// 在代码中new实例化时调用public MyToggleButton(Context context) {super(context);init(context);}@Overridepublic void onClick(View v) {curStata = !curStata;flushState();}/** * 刷新状态 */private void flushState() {//设置距离左边的距离if(curStata){slideLeft = backGroundBitmap.getWidth()-slideBitmap.getWidth();}else{slideLeft = 0;}/** * 刷新View,会导致当前View的onDraw方法执行 */invalidate();}}
运行效果图:

自定义滑动开关按钮

一、实现滑动效果:
/** * 第一次按下的x坐标 */int startX = 0;@Overridepublic boolean onTouchEvent(MotionEvent event) {super.onTouchEvent(event);switch (event.getAction()) {case MotionEvent.ACTION_DOWN://按下//1.记录第一次按下坐标startX = (int) event.getRawX();break;case MotionEvent.ACTION_MOVE://滑动//2.来到新的坐标int newX = (int) event.getRawX();//3.计算偏移量int dX = newX - startX;slideLeft += dX;//4.更新UI-onDraw方法即可--invalidate();invalidate();//5.重新记录坐标startX = (int) event.getRawX();break;case MotionEvent.ACTION_UP://离开break;}return true;}
看运行效果图,但是存在bug:

二、取消点击事件,屏蔽非法滑动:
public class MyToggleButton extends View implements OnClickListener {private Paint paint;/** * 一个View从创建到显示到屏幕过程中的步骤 *1.执行View的构造方法,实例化;通常在构造方法里面加载资源  *2.测量view对象 * onMeasure(int,int)  *3.指定View的位置 - 一般的View用不到,自定义包括其他View进来这样的控才用到 * onLayout(boolean,int,int,int,int)  *4.绘制View对象 onDraw(canvas) *  */private Bitmap backgroundBitmap;private Bitmap slideBitmap;/** * 滑动图片,距离左边的距离 */private float slideLeft;/** * 按钮的状态 false为关闭 true为开 */private boolean curState = false;// 测量@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// super.onMeasure(widthMeasureSpec, heightMeasureSpec);// 设置测量值setMeasuredDimension(backgroundBitmap.getWidth(),backgroundBitmap.getHeight());}// 绘制@Overrideprotected void onDraw(Canvas canvas) {// super.onDraw(canvas);// canvas.drawColor(Color.GREEN);// canvas.drawCircle(50, 50, 20, paint);canvas.drawBitmap(backgroundBitmap, 0, 0, paint);canvas.drawBitmap(slideBitmap, slideLeft, 0, paint);}/** * 第一次按下的x坐标 */int startX = 0;int maxLeft;@Overridepublic boolean onTouchEvent(MotionEvent event) {super.onTouchEvent(event);switch (event.getAction()) {case MotionEvent.ACTION_DOWN://按下//1.记录第一次按下坐标startX = (int) event.getRawX();break;case MotionEvent.ACTION_MOVE://滑动//2.来到新的坐标int newX = (int) event.getRawX();//3.计算偏移量int dX = newX - startX;slideLeft += dX;//4.更新UI-onDraw方法即可--invalidate();flushView();//5.重新记录坐标startX = (int) event.getRawX();break;case MotionEvent.ACTION_UP://离开break;default:break;}return true;}  // 刷新View的状态,并且纠正非法滑动private void flushView() {if(slideLeft < 0){slideLeft = 0;}if(slideLeft > maxLeft){slideLeft = maxLeft;}//屏蔽非法滑动invalidate();}private void init(Context context) {paint = new Paint();paint.setColor(Color.RED);// 设置抗锯齿-使其变得光滑paint.setAntiAlias(true);// 加载资源图片backgroundBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.switch_background);slideBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.slide_button);//滑动图片距离左边的距离maxLeft = backgroundBitmap.getWidth()-slideBitmap.getWidth();// 设置点击事件//setOnClickListener(this);}// 一般会在代码中实例化public MyToggleButton(Context context) {super(context);init(context);}// 带有两个参数的构造方法,在布局文件中使用的时候,就会回调public MyToggleButton(Context context, AttributeSet attrs) {super(context, attrs);init(context);}// 我们需要设置默认的样式风格的时候public MyToggleButton(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init(context);}@Overridepublic void onClick(View v) {curState = !curState;flushState();}// 刷新View的状态private void flushState() {if (curState) {slideLeft = maxLeft;} else {slideLeft = 0;}flushView();}}


三、处理滑动到一小半时时不好看的问题
先画图分析:


@Overridepublic boolean onTouchEvent(MotionEvent event) {super.onTouchEvent(event);switch (event.getAction()) {case MotionEvent.ACTION_DOWN://按下//1.记录第一次按下坐标startX = (int) event.getRawX();break;case MotionEvent.ACTION_MOVE://滑动//2.来到新的坐标int newX = (int) event.getRawX();//3.计算偏移量int dX = newX - startX;slideLeft += dX;//4.更新UI-onDraw方法即可--invalidate();flushView();//5.重新记录坐标startX = (int) event.getRawX();break;case MotionEvent.ACTION_UP://离开/** *  当UP事件发生的时候,由按钮的左边距离(btn_left)确定View的状态;当btn_left >= maxLeft/2 设置为开状态当btn_left < maxLeft/2  设置为 关闭状态 */if(slideLeft >= maxLeft/2){curState = true;}else{curState = false;}flushState();break;}return true;}
恢复点击事件
演示会有bug

四、解决点击事件和滑动事件导致的bug
public class MyToggleButton extends View implements OnClickListener {private Paint paint;/** * 一个View从创建到显示到屏幕过程中的步骤: *1.执行View的构造方法,实例化;通常在构造方法里面加载资源  *2.测量view对象 * onMeasure(int,int)  *3.指定View的位置 - 一般的View用不到,自定义包括其他View进来这样的控才用到 * onLayout(boolean,int,int,int,int) *4.绘制View对象 onDraw(canvas) *  */private Bitmap backgroundBitmap;private Bitmap slideBitmap;/** * 滑动图片,距离左边的距离 */private float slideLeft;/** * 按钮的状态 false为关闭 true为开 */private boolean curState = false;// 测量@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// super.onMeasure(widthMeasureSpec, heightMeasureSpec);// 设置测量值setMeasuredDimension(backgroundBitmap.getWidth(),backgroundBitmap.getHeight());}// 绘制@Overrideprotected void onDraw(Canvas canvas) {// super.onDraw(canvas);// canvas.drawColor(Color.GREEN);// canvas.drawCircle(50, 50, 20, paint);canvas.drawBitmap(backgroundBitmap, 0, 0, paint);canvas.drawBitmap(slideBitmap, slideLeft, 0, paint);}/** * 第一次按下的x坐标 */int startX = 0;/** * 最初的历史位置 */int lastX = 0;/** * 滑动按钮距离左边的最大距离 */int maxLeft;/** * 点击事件是否可用 * true 可用 * false 不可用 */boolean isClickEnable = true;@Overridepublic boolean onTouchEvent(MotionEvent event) {super.onTouchEvent(event);switch (event.getAction()) {case MotionEvent.ACTION_DOWN://按下//1.记录第一次按下坐标lastX = startX = (int) event.getRawX();isClickEnable = true;break;case MotionEvent.ACTION_MOVE://滑动//2.来到新的坐标int newX = (int) event.getRawX();//3.计算偏移量int dX = newX - startX;slideLeft += dX;//4.更新UI-onDraw方法即可--invalidate();if(Math.abs(event.getRawX()-lastX)>5){isClickEnable = false;}flushView();//5.重新记录坐标startX = (int) event.getRawX();break;case MotionEvent.ACTION_UP://离开if(!isClickEnable){/** *  当UP事件发生的时候,由按钮的左边距离(btn_left)确定View的状态;当btn_left >= maxLeft/2 设置为开状态当btn_left < maxLeft/2  设置为 关闭状态 */if(slideLeft >= maxLeft/2){curState = true;}else{curState = false;}flushState();}break;default:break;}return true;}  // 刷新View的状态,并且纠正非法滑动private void flushView() {if(slideLeft < 0){slideLeft = 0;}if(slideLeft > maxLeft){slideLeft = maxLeft;}//屏蔽非法滑动invalidate();}private void init(Context context) {paint = new Paint();paint.setColor(Color.RED);// 设置抗锯齿-使其变得光滑paint.setAntiAlias(true);// 加载资源图片backgroundBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.switch_background);slideBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.slide_button);//滑动图片距离左边的距离maxLeft = backgroundBitmap.getWidth()-slideBitmap.getWidth();// 设置点击事件setOnClickListener(this);}// 一般会在代码中实例化public MyToggleButton(Context context) {super(context);init(context);}// 带有两个参数的构造方法,在布局文件中使用的时候,就会回调public MyToggleButton(Context context, AttributeSet attrs) {super(context, attrs);init(context);}// 我们需要设置默认的样式风格的时候public MyToggleButton(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init(context);}@Overridepublic void onClick(View v) {if(isClickEnable){curState = !curState;flushState();}}// 刷新View的状态private void flushState() {if (curState) {slideLeft = maxLeft;} else {slideLeft = 0;}flushView();}}


全部代码:

package com.xbmu.toggle;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;/** * 1.执行view构造方法,创建对象 2.测量view大小 onMeasure(int,int)来完成测量动作 * 3.指定view的位置,子View只有建议权,父View才有决定权; onLayout(boolean,int,int,int,int); * 这个方法一般用不着,如果自定义ViewGroup才用到 4.绘制View的内容 onDraw(canvas) *  */public class MyToggleButton extends View implements View.OnClickListener {// 定义绘制(画笔)对象private Paint paint;private Bitmap slideBitmap;private Bitmap backGroundBitmap;private Context context;/** * 距离左边的距离 */private float slideLeft;/** * 判断当前开关状态 true为开 false为关 */private boolean curState = false;/** * 第一次按下的x坐标 */int startX = 0;int maxLeft;/** * 最初的历史位置 */int lastX = 0;/** * 点击事件是否可用 * true可用 * false不可用 */boolean isClickEnable = true;@Overridepublic boolean onTouchEvent(MotionEvent event) {super.onTouchEvent(event);switch (event.getAction()) {case MotionEvent.ACTION_DOWN://按下//1.记录第一次按下坐标startX = (int) event.getRawX();isClickEnable = true;break;case MotionEvent.ACTION_MOVE://滑动//2.来到新的坐标int newX = (int) event.getRawX();//3.计算偏移量int dx = newX - startX;slideLeft += dx;//4.更新UI-onDraw方法即可--invalidate();if(Math.abs(event.getRawX()-lastX) > 5){isClickEnable = false;}flushView();//5.重新记录坐标startX = (int) event.getRawX();break;case MotionEvent.ACTION_UP://离开if(!isClickEnable){/** * 当UP事件发生的时候,由按钮的左边距离(btn_left)确定View的状态; * 当btn_left >= maxLeft/2设置为开状态 * 当btn_left < maxLeft/2设置为关状态 */if(slideLeft >= maxLeft/2){curState = true;}else{curState = false;}flushState();}break;}return true;}//刷新View的状态,并且纠正非法滑动private void flushView() {if(slideLeft < 0){slideLeft = 0;}if(slideLeft > maxLeft){slideLeft = maxLeft;}//屏蔽非法滑动invalidate();}/** * 测量 */@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// super.onMeasure(widthMeasureSpec, heightMeasureSpec);// 设置当前view的测量带大小setMeasuredDimension(backGroundBitmap.getWidth(),backGroundBitmap.getHeight());}/** * 绘制 */@Overrideprotected void onDraw(Canvas canvas) {// super.onDraw(canvas);// 绘制颜色,可以理解成背景颜色// canvas.drawColor(Color.RED);// 绘制圆形// canvas.drawCircle(50, 50, 20, paint);canvas.drawBitmap(backGroundBitmap, 0, 0, paint);// 绘制滑动按钮canvas.drawBitmap(slideBitmap, slideLeft, 0, paint);}private void initView(Context context2) {paint = new Paint();paint.setColor(Color.GREEN);// 设置抗锯齿,让边缘圆滑,一般都会设置paint.setAntiAlias(true);slideBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.slide_button);backGroundBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.switch_background);//滑动图片距离左边的距离maxLeft = backGroundBitmap.getWidth()-slideBitmap.getWidth();//设置点击事件setOnClickListener(MyToggleButton.this);}@Overridepublic void onClick(View v) {if(isClickEnable){curState = !curState;flushState();}}/** * 刷新状态 */private void flushState() {// 设置距离左边的距离if (curState) {slideLeft = backGroundBitmap.getWidth() - slideBitmap.getWidth();} else {slideLeft = 0;}/** * 刷新View,会导致当前View的onDraw方法执行 */flushView();}public MyToggleButton(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);initView(context);}//带有两个参数的构造方法,在布局文件中使用的时候,就会回调public MyToggleButton(Context context, AttributeSet attrs) {super(context, attrs);initView(context);}//一般会在代码中实例化public MyToggleButton(Context context) {super(context);initView(context);}}
代码下载地址
http://download.csdn.net/detail/btt2013/9352431







0 0