不得不说,在做自定义滑动开关过程中,学习到了很多东西。跟大家分享分享!
1、自定义控件步骤:
测量:onMeasure 设置自己显示在屏幕上的宽高布局:onLayout 设置自己显示在屏幕上的位置(只有在自定义ViewGroup中才用到,需要设置子view的位置)
绘制:onDraw 控制显示在屏幕上的样子(自定义viewgroup时不需要这个,绘制单个view的样子)
所以我们在自定义一个控件之前,要明白自定义的是一个view,还是一个viewgroup,如:本篇中自定义的ToggleButton
只是一个简单view,本人是这样理解的:view相当于button,textview之类的小控件,而viewgroup相当于RelativeLayout
之类的布局。大家可以提出个人的见解让他人可以更好的理解view与viewgroup的差别!
View和ViewGroup的区别
1.他们都需要进行测量操作
2.ViewGroup主要是控制子view如何摆放,所以必须实现onLayout
View没有子view,所以不需要onLayout方法,但是必须实现onDraw
3、废话有点多,先贴出ToggleButton效果图:
4、源码:xml中很简单:
<com.chenshi.mytogglebutton.ToggleButton android:id="@+id/toggleBtn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" />
最后一句是使view居于父控件居中!
5、MainActivity中:
toggleButton.setSildeBackgroundResource(R.drawable.slide_bg); toggleButton.setSwitchOnBackgroundResource(R.drawable.switch_on); toggleButton.setSwitchOffBackgroundResource(R.drawable.switch_off); //设置开关状态,默认为OPEN toggleButton.setToggleState(ToggleButton.ToggleState.CLOSE); toggleButton.setOnStateChangeListeren(new ToggleButton.OnToggleStateChangeListeren() { @Override public void onToggleStateChangeListeren(ToggleButton.ToggleState toggleState) { Toast.makeText(MainActivity.this, toggleState == ToggleButton.ToggleState.CLOSE ? "关" : "开",
Toast.LENGTH_SHORT).show(); } });
为什么要先贴出MainActivity中源码呢,因为我们通过set知道我们自定中需要哪里方法,如:
toggleButton.setSildeBackgroundResource(R.drawable.slide_bg);对应于自定类 ToggleButton 中就要有
/** * 设置滑块的背景图片 * * @param slide_bg */ public void setSildeBackgroundResource(int slide_bg) { slideBg = BitmapFactory.decodeResource(getResources(), slide_bg); }此方法!这是一种思路!并不是盲目的自定义方法!
6、自定类源码:注释的挺详细的,大家耐心点看!
public class ToggleButton extends View {
private Bitmap slideBg;//滑块的背景图
private Bitmap switchOn;//滑动开关开的背景图
private Bitmap switchOff;//滑动开关关的背景图
private ToggleState state = ToggleState.OPEN;//滑动开关的状态,默认为OPEN;
//手指触摸在view上的坐标,这个是相对于view的坐标
private int currentX;
//是否在滑动
private boolean isSliding = true;
/**
* 如果自定的控件需要在Java中实例化,重写该构造方法
*
* @param context
*/
public ToggleButton(Context context) {
super(context);
}
/**
* 如果自定义控件需要在xml文件使用,重写该构造方法
*
* @param context
* @param attrs
*/
public ToggleButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* 设置滑块的背景图片
*
* @param slide_bg
*/
public void setSildeBackgroundResource(int slide_bg) {
slideBg = BitmapFactory.decodeResource(getResources(), slide_bg);
}
/**
* 设置滑动开关开的背景图
*
* @param switch_on
*/
public void setSwitchOnBackgroundResource(int switch_on) {
switchOn = BitmapFactory.decodeResource(getResources(), switch_on);
}
/**
* 设置滑动开关关的背景图
*
* @param switch_off
*/
public void setSwitchOffBackgroundResource(int switch_off) {
switchOff = BitmapFactory.decodeResource(getResources(), switch_off);
}
public void setToggleState(ToggleState toggleState) {
this.state = toggleState;
}
public enum ToggleState {
OPEN, CLOSE
}
/**
* 绘制view的宽与高,以滑动开关的宽高绘制
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(switchOn.getWidth(), switchOn.getHeight());
}
/**
* 画view
*
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas) {
int left = currentX - slideBg.getWidth() / 2;
// (left > (switchOn.getWidth() / 2)) ? ((state = ToggleState.OPEN)) : ((state = ToggleState.CLOSE));
//当触摸点超过整view的一半时,就切换开关背景;
super.onDraw(canvas);
/**
* (@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint)
* left:图片左边的x坐标
* top:图片顶部的y坐标
*/
if (isSliding) {
if (state == ToggleState.OPEN) {
//绘制开关图片
canvas.drawBitmap(switchOn, 0, 0, null);
//绘制滑块图片
if (left > (switchOn.getWidth() - slideBg.getWidth()))
left = switchOn.getWidth() - slideBg.getWidth();
canvas.drawBitmap(slideBg, left, 0, null);
} else {
//绘制开关图片
canvas.drawBitmap(switchOff, 0, 0, null);
//绘制滑块图片
if (left < 0) left = 0;
canvas.drawBitmap(slideBg, left, 0, null);
}
} else {
if (state == ToggleState.OPEN) {
//绘制开关图片
canvas.drawBitmap(switchOn, 0, 0, null);
canvas.drawBitmap(slideBg, switchOn.getWidth() - slideBg.getWidth(), 0, null);
} else {
//绘制开关图片
canvas.drawBitmap(switchOff, 0, 0, null);
canvas.drawBitmap(slideBg, 0, 0, null);
}
}
}
/**
* 定义滑动
*
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
currentX = (int) event.getX();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isSliding = true;
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
isSliding = false;
break;
}
//在滑动的过程中状态改变也进行监听,触摸结束后也监听状态改变,未改变就无需其他动作
if (currentX > switchOn.getWidth() / 2) {
if (state != ToggleState.OPEN) {
state = ToggleState.OPEN;
if (stateChangeListeren != null) {
stateChangeListeren.onToggleStateChangeListeren(state);
}
}
} else {
//CLOSE
if (state != ToggleState.CLOSE) {
state = ToggleState.CLOSE;
if (stateChangeListeren != null) {
stateChangeListeren.onToggleStateChangeListeren(state);
}
}
}
//调用此方法,间接调用OnD
invalidate();
return true;
}
/**
* 开放状态改变接口
*/
private OnToggleStateChangeListeren stateChangeListeren;
public void setOnStateChangeListeren(OnToggleStateChangeListeren stateChangeListeren) {
this.stateChangeListeren = stateChangeListeren;
}
public interface OnToggleStateChangeListeren {
void onToggleStateChangeListeren(ToggleState state);
}
}