先来张效果图
banner.gif
步骤思路
- 继承View
1、测量自己的宽高
2、绘制自己
3、处理触摸事件
1、继承View
public class MyToggleButton extends View {
public MyToggleButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
// 在布局文件中使用控件时 调用
public MyToggleButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
// 在代码中new对象时 调用
public MyToggleButton(Context context) {
super(context);
}
}
2、在布局中使用MyToggleButton
<com.itheima.toggle94.MyToggleButton
android:id="@+id/toggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
3、测量宽高
// 测量自己的宽高,把背景图片的宽高设置为自己的宽高
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 把背景图片的宽高设置为自己的宽高
setMeasuredDimension(backgroundBitmap.getWidth(), backgroundBitmap.getHeight());
}
// 外界设置开关背景图片资源
public void setBackgroundRes(int backgroundRes){
// 把传进来的图片资源 转换成Bitmap对象
backgroundBitmap = BitmapFactory.decodeResource(getResources(), backgroundRes);
}
4、绘制
// 外界设置滑动块图片资源
public void setSlideButtonRes(int slideButtonRes){
// 把传进来的图片资源 转换成Bitmap对象
slideButtonBitmap = BitmapFactory.decodeResource(getResources(), slideButtonRes);
}
// 绘制自己
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 绘制背景
canvas.drawBitmap(backgroundBitmap, 0, 0, null);
// 绘制滑动块
canvas.drawBitmap(slideButtonBitmap, 0, 0, null);
}
5、根据状态绘制滑动块
// 外界设置开关状态
public void setState(boolean state){
this.currentState = state;
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 绘制背景
canvas.drawBitmap(backgroundBitmap, 0, 0, null);
// 绘制滑动块 根据外界的设置,展示开或关
if(!currentState){// 关状态
canvas.drawBitmap(slideButtonBitmap, 0, 0, null);
}else{// 开状态
canvas.drawBitmap(slideButtonBitmap, backgroundBitmap.getWidth()-slideButtonBitmap.getWidth(), 0, null);
}
}
6、处理事件,让滑动块随手指移动
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isTouching = true;
// 获取手指触摸时相对于自己的x位置
currentX = (int) event.getX();
break;
case MotionEvent.ACTION_MOVE:
currentX = (int) event.getX();
break;
case MotionEvent.ACTION_UP:
isTouching = false;
currentX = (int) event.getX();
break;
default:
break;
}
// 重新绘制控件,重新调用onDraw
invalidate();
return true;// 消费掉事件
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 绘制背景
canvas.drawBitmap(backgroundBitmap, 0, 0, null);
// 根据手指触摸的位置绘制滑动块图片
if(isTouching){
canvas.drawBitmap(slideButtonBitmap, currentX, 0, null);
}else{
// 绘制滑动块 根据外界的设置,展示开或关
if(!currentState){// 关状态
canvas.drawBitmap(slideButtonBitmap, 0, 0, null);
}else{// 开状态
canvas.drawBitmap(slideButtonBitmap, backgroundBitmap.getWidth()-slideButtonBitmap.getWidth(), 0, null);
}
}
}
7、让用户感觉手指触摸到了滑动块中间
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 绘制背景
canvas.drawBitmap(backgroundBitmap, 0, 0, null);
// 根据手指触摸的位置绘制滑动块图片
if(isTouching){
int left = currentX-slideButtonBitmap.getWidth()/2;// 让滑动块画的中心画到手指触摸的位置
// 限制滑动块的滑动范围
if(left<0){// 如果超出左边,直接绘制到0位置
left = 0;
}else if(left>backgroundBitmap.getWidth()-slideButtonBitmap.getWidth()){
left = backgroundBitmap.getWidth()-slideButtonBitmap.getWidth();
}
canvas.drawBitmap(slideButtonBitmap, left, 0, null);
}else{
......
}
}
8、手指抬起时,通过改变当前状态,让滑动块根据状态绘制
case MotionEvent.ACTION_UP:
isTouching = false;
currentX = (int) event.getX();
// 手指抬起时,根据手指触摸的位置与背景图片一半比较,离哪边近,滑动块就滑动到哪
currentState = currentX>backgroundBitmap.getWidth()/2;
break;
default:
break;
}
9、对外暴露监听,让外界监听到开关的状态变化
* 1、暴露接口
// 对外暴露开关状态发生变化的接口
public interface OnToggleButtonStateChangeListener{
// 当开关状态发生变化时 调用
void onStateChanged(boolean state);
}
* 2、对外提供监听器
// 让外界设置状态变化的监听器
public void setOnToggleButtonStateChangeListener(OnToggleButtonStateChangeListener listener){
this.mListener = listener;
}
* 3、当状态发生变化时调用监听器的回调方法
case MotionEvent.ACTION_UP:
isTouching = false;
currentX = (int) event.getX();
// 手指抬起时,根据手指触摸的位置与背景图片一半比较,离哪边近,滑动块就滑动到哪
// 当状态发生变化时,调用外界传递进来的监听器的onStateChanged
boolean tempState = currentX>backgroundBitmap.getWidth()/2;
if(tempState!=currentState){// 状态发生变化
currentState = tempState;
if(mListener!=null){
mListener.onStateChanged(currentState);
}
}
break;
* 4、外界监听开关状态
OnToggleButtonStateChangeListener listener = new OnToggleButtonStateChangeListener() {
@Override
public void onStateChanged(boolean state) {
// 处理业务
Toast.makeText(getApplicationContext(), ""+state, 0).show();
}
};
toggle.setOnToggleButtonStateChangeListener(listener);
10、自定义属性
* 1、定义属性
/toggle94/res/values/attrs.xml
<declare-styleable name="MytoggleButton">
<!-- 背景图片属性 -->
<attr name="backgroundRes" format="reference" />
<!-- 滑动块图片属性 -->
<attr name="slideButtonRes" format="reference" />
<!-- 开关状态属性 -->
<attr name="state" format="boolean" />
</declare-styleable>
* 2、在布局中使用属性,要先声明命名空间
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:itheima="http://schemas.android.com/apk/res/com.itheima.toggle94"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<com.itheima.toggle94.MyToggleButton
android:id="@+id/toggle"
itheima:backgroundRes="@drawable/switch_background"
itheima:slideButtonRes="@drawable/slide_button_background"
itheima:state="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
* 3、代码中获取自定义属性值,设置给控件
// 在布局文件中使用控件时 调用
public MyToggleButton(Context context, AttributeSet attrs) {
super(context, attrs);
// 从布局文件中获取控件的自定义属性值
String namespace = "http://schemas.android.com/apk/res/com.itheima.toggle94";
int backgroundResValue = attrs.getAttributeResourceValue(namespace, "backgroundRes", 0);
setBackgroundRes(backgroundResValue);
int slideButtonResValue = attrs.getAttributeResourceValue(namespace, "slideButtonRes", 0);
setSlideButtonRes(slideButtonResValue);
currentState = attrs.getAttributeBooleanValue(namespace, "state", false);
}
网友评论