自定义开关滑动按钮控件
package com.loaderman.switch;
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;
/**
* 控件绘制流程:
* <p>
* 1. measure方法来确定控件尺寸
* 2. layout方法确定控件位置
* 3. draw方法绘制控件内容
* <p>
* measure->layout->draw
* onMeasure->onLayout->onDraw
* <p>
* 1280x720 xhdpi
* 480x800 hdpi
*/
public class MySwitch extends View {
private Paint paint;
private Bitmap mBitmapBg;
private Bitmap mBitmapSlide;
private int MAX_LEFT;//最大左边距
private int mSlideLeft;//当前左边距
private boolean isOpen = false;//当前开关状态
private static final String NAMESPACE = "http://schemas.android.com/apk/res-auto";
public MySwitch(Context context) {
this(context, null);
}
public MySwitch(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public MySwitch(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
//设置开关初始状态
isOpen = attrs.getAttributeBooleanValue(NAMESPACE, "isOpen", false);
if(isOpen) {
mSlideLeft = MAX_LEFT;
}else {
mSlideLeft = 0;
}
//设置滑块图片
int slideDrawable = attrs.getAttributeResourceValue(NAMESPACE, "slideDrawable", -1);
mBitmapSlide = BitmapFactory.decodeResource(getResources(), slideDrawable);
invalidate();
}
private void init() {
paint = new Paint();
paint.setColor(Color.RED);
//初始化背景图片
mBitmapBg = BitmapFactory.decodeResource(getResources(), R.drawable.switch_background);
//滑块图片
mBitmapSlide = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button);
MAX_LEFT = mBitmapBg.getWidth() - mBitmapSlide.getWidth();
//设置点击事件
this.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (isClick) {
if (isOpen) {
mSlideLeft = 0;
isOpen = false;
} else {
mSlideLeft = MAX_LEFT;
isOpen = true;
}
//刷新当前控件
invalidate();//此方法会导致onDraw重新走一遍
//调用回调对象的方法, 来回传数据
if (listener != null) {
listener.onCheckedChanged(isOpen);
}
}
}
});
//this.setOnTouchListener();
}
private int startX;
private int moveX;//记录移动总距离
private boolean isClick;//标记当前是否是点击事件
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//1.记录起点坐标
startX = (int) event.getX();
break;
case MotionEvent.ACTION_MOVE:
//2. 记录移动后坐标
int endX = (int) event.getX();
//3. 计算偏移量
int dx = endX - startX;
moveX += Math.abs(dx);
//4. 根据偏移量更新控件位置
mSlideLeft += dx;
//避免越界
if (mSlideLeft < 0) {
mSlideLeft = 0;
}
if (mSlideLeft > MAX_LEFT) {
mSlideLeft = MAX_LEFT;
}
invalidate();
//5. 重新初始化起点坐标
startX = endX;
break;
case MotionEvent.ACTION_UP:
//根据移动总距离确定是否是点击事件
if (moveX > 5) {//允许5个像素的误差
//移动
isClick = false;
} else {
//点击
isClick = true;
}
moveX = 0;//移动总距离一定要归0;
if (!isClick) {
//确定最终开关状态
if (mSlideLeft < MAX_LEFT / 2) {
mSlideLeft = 0;
isOpen = false;
} else {
mSlideLeft = MAX_LEFT;
isOpen = true;
}
invalidate();
//调用回调对象的方法, 来回传数据
if (listener != null) {
listener.onCheckedChanged(isOpen);
}
}
break;
default:
break;
}
//return true;//消费了此事件,导致onClick无法响应 ACTION_DOWN + ACTION_MOVE..... + ACTION_UP
//return false;//不消费事件,只会走ACTION_DOWN, 后续所有事件不都不处理了
return super.onTouchEvent(event);//由于onClick的回调是在super.onTouchEvent完成的,
// 所以必须返回父类的onTouchEvent才能保证onClick也能够响应事件
}
//重写此方法可以修改控件尺寸
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//setMeasuredDimension(100, 100);
setMeasuredDimension(mBitmapBg.getWidth(), mBitmapBg.getHeight());
System.out.println("onMeasure....");
}
//设置控件位置
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
System.out.println("onLayout....");
}
//绘制控件
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//canvas.drawRect(0, 0, 200, 200, paint);
System.out.println("onDraw......");
//在左上角绘制图片
canvas.drawBitmap(mBitmapBg, 0, 0, null);
canvas.drawBitmap(mBitmapSlide, mSlideLeft, 0, null);
}
private OnCheckedChangeListener listener;
public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
this.listener = listener;
}
//定义回调接口
public interface OnCheckedChangeListener {
void onCheckedChanged(boolean isChecked);
}
// public void smsBackup(OnCheckedChangeListener callback) {
//
// callback.onCheckedChanged(true);
// }
}
package com.loaderman.switch;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private MySwitch mSwitch;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSwitch = (MySwitch) findViewById(R.id.my_switch);
mSwitch.setOnCheckedChangeListener(new MySwitch.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(boolean isChecked) {
Toast.makeText(MainActivity.this, "开关状态:" + isChecked, Toast.LENGTH_SHORT).show();
}
});
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.loaderman.switch.MainActivity">
<com.loaderman.switch.MySwitch
android:id="@+id/my_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
app:isOpen="true"
app:slideDrawable="@drawable/slide_button"
/>
</RelativeLayout>
自定义属性atts_my_switch.xml
<resources>
<declare-styleable name="MySwitch">
<!--开关状态-->
<attr name="isOpen" format="boolean"/>
<!--滑块图片-->
<attr name="slideDrawable" format="reference"/>
</declare-styleable>
</resources>
效果图:

最后,关注【码上加油站】微信公众号后,有疑惑有问题想加油的小伙伴可以码上加入社群,让我们一起码上加油吧!!!
浙公网安备 33010602011771号