package com.fly.lsn29_switchview;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
/**
* 自定义开关
*
* @author poplar
* <p>
* Android 的界面绘制流程
* 测量 摆放 绘制
* measure -> layout -> draw
* | | |
* onMeasure -> onLayout -> onDraw 重写这些方法, 实现自定义控件
* <p>
* onResume()之后执行
* <p>
* View
* onMeasure() (在这个方法里指定自己的宽高) -> onDraw() (绘制自己的内容)
* <p>
* ViewGroup
* onMeasure() (指定自己的宽高, 所有子View的宽高)-> onLayout() (摆放所有子View) -> onDraw() (绘制内容)
*/
public class SlideSwitchView extends View {
private Bitmap switchBackgroupBitmap; // 背景图片
private Bitmap slideButtonBitmap; // 滑块图片
private Paint paint; // 画笔
private boolean mSwitchState = false; // 开关状态, 默认false
private float currentX;
/**
* 用于代码创建控件
*
* @param context
*/
public SlideSwitchView(Context context) {
super(context);
init();
}
/**
* 用于在xml里使用, 可指定自定义属性
*
* @param context
* @param attrs
*/
public SlideSwitchView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
// 获取配置的自定义属性
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SlideSwitchView);
// int switchBackgroundResource = ta.getResourceId(R.styleable.SlideSwitchView_switch_background, R.drawable.switch_background);//可以设置默认图片
int switchBackgroundResource = ta.getResourceId(R.styleable.SlideSwitchView_switch_background, -1);
int slideButtonResource = ta.getResourceId(R.styleable.SlideSwitchView_slide_button, -1);
mSwitchState = ta.getBoolean(R.styleable.SlideSwitchView_switch_state, false);
setSwitchBackgroundResource(switchBackgroundResource);
setSlideButtonResource(slideButtonResource);
}
/**
* 用于在xml里使用, 可指定自定义属性, 如果指定了样式, 则走此构造函数
*
* @param context
* @param attrs
* @param defStyle
*/
public SlideSwitchView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
paint = new Paint();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 依据背景图片来确定控件大小
setMeasuredDimension(switchBackgroupBitmap.getWidth(), switchBackgroupBitmap.getHeight());
}
// Canvas 画布, 画板. 在上边绘制的内容都会显示到界面上.
@Override
protected void onDraw(Canvas canvas) {
// 1. 绘制背景
canvas.drawBitmap(switchBackgroupBitmap, 0, 0, paint);
// 2. 绘制滑块
if (isTouchMode) {
// 根据当前用户触摸到的位置画滑块
// 让滑块向左移动自身一半大小的位置
float newLeft = currentX - slideButtonBitmap.getWidth() / 2.0f;
int maxLeft = switchBackgroupBitmap.getWidth() - slideButtonBitmap.getWidth();
// 限定滑块范围
if (newLeft < 0) {
newLeft = 0; // 左边范围
} else if (newLeft > maxLeft) {
newLeft = maxLeft; // 右边范围
}
canvas.drawBitmap(slideButtonBitmap, newLeft, 0, paint);
} else {
// 根据开关状态boolean, 直接设置图片位置
if (mSwitchState) {// 开
int newLeft = switchBackgroupBitmap.getWidth() - slideButtonBitmap.getWidth();
canvas.drawBitmap(slideButtonBitmap, newLeft, 0, paint);
} else {// 关
canvas.drawBitmap(slideButtonBitmap, 0, 0, paint);
}
}
}
boolean isTouchMode = false;
private OnSwitchStateUpdateListener onSwitchStateUpdateListener;
// 重写触摸事件, 响应用户的触摸.
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isTouchMode = true;
currentX = event.getX();
break;
case MotionEvent.ACTION_MOVE:
currentX = event.getX();
break;
case MotionEvent.ACTION_UP:
isTouchMode = false;
currentX = event.getX();
float center = switchBackgroupBitmap.getWidth() / 2.0f;
// 根据当前按下的位置, 和控件中心的位置进行比较.
boolean state = currentX > center;
// 如果开关状态变化了, 通知界面. 里边开关状态更新了.
if (state != mSwitchState && onSwitchStateUpdateListener != null) {
// 把最新的boolean, 状态传出去了
onSwitchStateUpdateListener.onStateUpdate(state);
}
mSwitchState = state;
break;
}
// 重绘界面
invalidate(); // 会引发onDraw()被调用, 里边的变量会重新生效.界面会更新
return true; // 消费了用户的触摸事件, 才可以收到其他的事件.
}
/**
* 设置背景图
*
* @param switchBackground
*/
public void setSwitchBackgroundResource(int switchBackground) {
if (switchBackground > 0) {
switchBackgroupBitmap = BitmapFactory.decodeResource(getResources(), switchBackground);
} else {
Log.e("---SlideSwitchView--->", "没有设置开关背景图,应设置app:switch_background=\"@drawable/xxx\"");
}
}
/**
* 设置滑块图片资源
*
* @param slideButton
*/
public void setSlideButtonResource(int slideButton) {
if (slideButton > 0) {
slideButtonBitmap = BitmapFactory.decodeResource(getResources(), slideButton);
} else {
Log.e("---SlideSwitchView--->", "没有设置滑动按钮图,应设置app:slide_button=\"@drawable/xxx\"");
}
}
/**
* 设置开关状态
*/
public void setSwitchState(boolean mSwitchState) {
this.mSwitchState = mSwitchState;
}
public interface OnSwitchStateUpdateListener {
// 状态回调, 把当前状态传出去
void onStateUpdate(boolean state);
}
public void setOnSwitchStateUpdateListener(OnSwitchStateUpdateListener onSwitchStateUpdateListener) {
this.onSwitchStateUpdateListener = onSwitchStateUpdateListener;
}
}
Android自定义控件之实现滑动选择开关代码
4星 · 超过85%的资源 需积分: 22 87 浏览量
2017-05-30
23:37:37
上传
评论 2
收藏 29KB RAR 举报
等待着冬天的风
- 粉丝: 518
- 资源: 24
最新资源
- C#,煎饼排序问题(Pancake Sorting Problem)算法与源代码
- C#,排列组合的堆生成法(Heap’s Algorithm for generating permutations)算法与源代码
- C#,老鼠迷宫问题的回溯法求解(Rat in a Maze)算法与源代码
- 6693eeb8d683458a07938615fba9e68f.apk
- C#,数值计算,解微分方程的龙格-库塔二阶方法与源代码
- C#,数值计算,用割线法(Secant Method)求方程根的算法与源代码
- C#,子集和问题(Subset Sum Problem)的算法与源代码
- mongodb 数据库基本操作
- Linux操作系统基础教程
- Linux操作系统相关习题集
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈