自定义View实现SwichButton效果
2016-04-04 23:56
363 查看
老习惯先看效果,这种效果其实用自定义的CheckBox也是很好实现的。但是这样实现可以用手指滑动实现和手指点击实现。
需要掌握的技术
自定义属性触摸事件的处理
回调的使用
实现步骤
自定义属性怎么自定义属性我就不详细介绍了网上一大堆,这里需要现在资源文件夹下的values创建attrs.xml文件,主要定义了默认是否是选中和滑动的图片是什么。
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="SwitchView"> <attr name="check" format="boolean" /> <attr name="beforeBg" format="reference"/> </declare-styleable> </resources>
编写SwitchView控件继承View
2.1 获取自定义属性
public SwitchView(Context context) { this(context, null); } public SwitchView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SwitchView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); isCheck = attrs.getAttributeBooleanValue(R.styleable.SwitchView_check, false); slideButton = attrs.getAttributeResourceValue(R.styleable.SwitchView_beforeBg, R.mipmap.slide_button); initView(); }
2.2 将资源文件转换为Bitmap(按钮底部的背景通过Canvas在上面画图)
//将资源图片转换为Bitmap bitmapSlide = BitmapFactory.decodeResource(getResources(), slideButton); bgBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.switch_background);
2.3 计算滑块离左右两边的距离
//计算离左边的最大距离 MAX_LEFT = bgBitmap.getWidth() - bitmapSlide.getWidth(); if (isCheck) { mSlideLeft = MAX_LEFT; } else { mSlideLeft = 0; }
2.4 将背景图和滑块通过画布画出来
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawBitmap(bgBitmap, 0, 0, null); //绘制底部背景 canvas.drawBitmap(bitmapSlide, mSlideLeft, 0, null); //绘制滑块 }
2.5 监听触摸事件
这里需要做的事是根据滑动的x轴的坐标来改变滑块的位置,但是需要注意的是当滑动的x轴的坐标小于0或者滑动的坐标已经大于最大能一定的值就是MAX_LEFT需要自己设置滑块的位置为0或者MAX_LEFT。这里主要监听的是MotionEvent.ACTION_MOVE方法
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //手指按下时获取x的起点坐标 startX = event.getX(); break; case MotionEvent.ACTION_MOVE: //移动时获取手指移动x的最后坐标 endX = event.getX(); float dx = endX - startX; moveX = moveX + (int) Math.abs(dx); mSlideLeft = mSlideLeft + (int) dx; //防止超过边界 if (mSlideLeft < 0) { mSlideLeft = 0; } else if (mSlideLeft > MAX_LEFT) { mSlideLeft = MAX_LEFT; } invalidate(); startX = endX; break; case MotionEvent.ACTION_UP: Log.e("CXX", "moveX" + moveX); if (moveX > 5) { isClick = false; } else { isClick = true; } moveX = 0; if (!isClick) { if (mSlideLeft < MAX_LEFT / 2) { mSlideLeft = 0; isCheck = false; } else { mSlideLeft = MAX_LEFT; isCheck = true; } if (onCheckListener != null) { onCheckListener.onCheck(SwitchView.this, isCheck); } } invalidate(); break; } return super.onTouchEvent(event); }
代码解释下isClick的意识这个是和下面即将写的onClick事件关联在一起的因为我们的代码是可以滑动和点击共同存在的所以需要做个判断。如果不做判断会出现这么一个情况,就是我将滑块从左边滑动到右边不松手在滑动到左边按照道理是应该停留在左边的但是因为写了onClick事件所以还会调onClick中的事件故在这里加了个判断。
onClick中代码如下:
@Override public void onClick(View v) { if (!isClick) { return; } Log.e("CXX", "click" + isCheck); if (isCheck) { mSlideLeft = 0; isCheck = false; } else { mSlideLeft = MAX_LEFT; isCheck = true; } Log.e("CXX", "onCheck" + onCheckListener); //选中的回调 if (onCheckListener != null) { onCheckListener.onCheck(SwitchView.this, isCheck); } invalidate(); }
2.6 回调事件
定义一个接口
public void setOnCheckListener(OnCheckListener onCheckListener) { this.onCheckListener = onCheckListener; } public interface OnCheckListener { public void onCheck(View view, boolean isCheck); }
最后是整个SwitchView的源码:
package com.media;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
/**
* Created by CXX on 2016/4/4.
*/
public class SwitchView extends View implements View.OnClickListener {
private boolean isCheck; //当前滑动开关状态
private int slideButton; //滑动图片资源文件
private Bitmap bitmapSlide; //滑动图片Bitmap
private Bitmap bgBitmap; //背景图片资源文件
private int MAX_LEFT; //滑动最大左边距
private int mSlideLeft; //当前滑块左边距
private float startX;
private float endX;
private int moveX;
private boolean isClick = false; //是否可以点击
public SwitchView(Context context) {
this(context, null);
}
public SwitchView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SwitchView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
isCheck = attrs.getAttributeBooleanValue(R.styleable.SwitchView_check, false);
slideButton = attrs.getAttributeResourceValue(R.styleable.SwitchView_beforeBg, R.mipmap.slide_button);
initView();
}
private void initView() {
Log.e("CXX", "isCheck" + isCheck);
//将资源图片转换为Bitmap
bitmapSlide = BitmapFactory.decodeResource(getResources(), slideButton);
bgBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.switch_background);
//计算离左边的最大距离
MAX_LEFT = bgBitmap.getWidth() - bitmapSlide.getWidth();
if (isCheck) {
mSlideLeft = MAX_LEFT;
} else {
mSlideLeft = 0;
}
//刷新界面
invalidate();
setOnClickListener(this);
}
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //手指按下时获取x的起点坐标 startX = event.getX(); break; case MotionEvent.ACTION_MOVE: //移动时获取手指移动x的最后坐标 endX = event.getX(); float dx = endX - startX; moveX = moveX + (int) Math.abs(dx); mSlideLeft = mSlideLeft + (int) dx; //防止超过边界 if (mSlideLeft < 0) { mSlideLeft = 0; } else if (mSlideLeft > MAX_LEFT) { mSlideLeft = MAX_LEFT; } invalidate(); startX = endX; break; case MotionEvent.ACTION_UP: Log.e("CXX", "moveX" + moveX); if (moveX > 5) { isClick = false; } else { isClick = true; } moveX = 0; if (!isClick) { if (mSlideLeft < MAX_LEFT / 2) { mSlideLeft = 0; isCheck = false; } else { mSlideLeft = MAX_LEFT; isCheck = true; } if (onCheckListener != null) { onCheckListener.onCheck(SwitchView.this, isCheck); } } invalidate(); break; } return super.onTouchEvent(event); }
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(bgBitmap.getWidth(), bgBitmap.getHeight());
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(bgBitmap, 0, 0, null); //绘制底部背景
canvas.drawBitmap(bitmapSlide, mSlideLeft, 0, null); //绘制滑块
}
@Override public void onClick(View v) { if (!isClick) { return; } Log.e("CXX", "click" + isCheck); if (isCheck) { mSlideLeft = 0; isCheck = false; } else { mSlideLeft = MAX_LEFT; isCheck = true; } Log.e("CXX", "onCheck" + onCheckListener); //选中的回调 if (onCheckListener != null) { onCheckListener.onCheck(SwitchView.this, isCheck); } invalidate(); }
private OnCheckListener onCheckListener;
public void setOnCheckListener(OnCheckListener onCheckListener) {
this.onCheckListener = onCheckListener;
}
public interface OnCheckListener {
public void onCheck(View view, boolean isCheck);
}
}
源码地址
源码地址相关文章推荐
- Android——Tomcat+MySQL+Servlet,实现将Client传入的数据写入MySQL
- 怎样理解阻塞非阻塞与同步异步的区别
- 用简单易懂的语言描述过拟合 overfitting?
- HTML毛玻璃背景
- uml 规范
- Cocos2d HttpClient
- 令牌环网
- Leetcode #33. Search in Rotated Sorted Array 旋转数组查询 解题报告
- Web 开发工具类(4): IDUtils
- 全球最受欢迎的十大Linux发行版(图)
- 初学linux相关问题1
- iOS- 响应者链, runtime, runloop
- 机电传动控制与其他课程间的关系
- Storm集群安装部署步骤【详细版】
- char s[]="123456\0";
- gradle相关
- Python 实现Harris 角点检测
- LeetCode之8_String to Integer (atoi)
- 2016年程序员就业形势是什么样的?
- 使用SSH密钥连接Github【图文教程】