效果图:
定义一个类,取名为MySwitch.java,此类去继承View,为何是继承View而不是去继承ViewGroup呢,是因为自定义开关没有子控件,之需要操作自身绘制即可
package custom.view.upgrade.my_switch; 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.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import custom.view.R; public class MySwitch extends View implements View.OnClickListener { private static String TAG = MySwitch.class.getSimpleName(); private Paint mPaint; /** * 让布局中来指定实例化,得到属性集合AttributeSet * @param context * @param attrs */ public MySwitch(Context context, @Nullable AttributeSet attrs) { super(context, attrs); initView(context, attrs); initListener(); } // 定义按钮背景图片 private Bitmap bmSwitchBackground; // 定义按钮拖动的图片 private Bitmap bmSwitchDrag; // 定义开关的状态 true || false , 默认是关闭状态 private boolean switchStatus; // 定义开关的临时记录状态 private boolean tempSwitchStatus; // 定义按钮拖动距离左边的距离 private int dragLife = -1; /** * 初始化工作 */ private void initView(Context context, AttributeSet attrs) { mPaint = new Paint(); mPaint.setAntiAlias(true); // 抗锯齿 // 获取属性 TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MySwitch); bmSwitchBackground = ((BitmapDrawable) typedArray.getDrawable(R.styleable.MySwitch_switch_background)).getBitmap(); bmSwitchDrag = ((BitmapDrawable) typedArray.getDrawable(R.styleable.MySwitch_switch_drag)).getBitmap(); switchStatus = typedArray.getBoolean(R.styleable.MySwitch_switch_status, false); } public void setBmSwitchBackground(int switchBackground) { this.bmSwitchBackground = BitmapFactory.decodeResource(getResources() ,switchBackground); } public void setBmSwitchDrag(int switchDrag) { this.bmSwitchDrag = BitmapFactory.decodeResource(getResources(), switchDrag); } public void setSwitChStatus(boolean switchStatus) { this.switchStatus = switchStatus; dragLife = -1; } /** * 初始化事件 */ private void initListener() { setOnClickListener(this); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 宽度是:按钮背景的宽度 // 高度是:按钮背景的高度 // 测量自身View setMeasuredDimension(bmSwitchBackground.getWidth(), bmSwitchBackground.getHeight()); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 绘制按钮背景 canvas.drawBitmap(bmSwitchBackground, 0, 0, mPaint); if (dragLife != -1) { canvas.drawBitmap(bmSwitchDrag, dragLife, 0 , mPaint); } else if (dragLife == -1) { if (switchStatus) { // 打开状态 // 滑动点向右就是开启状态 int openDragLife = getLifeDragMaxValue(); canvas.drawBitmap(bmSwitchDrag, openDragLife, 0, mPaint); moveEndX = openDragLife; } else { // 关闭状态 canvas.drawBitmap(bmSwitchDrag, 0, 0, mPaint); moveEndX = 0; } // 当开关的状态发生变化后,回调方法告诉用户,开关改变了 if (null != onSwitchChangeListener && switchStatusChange) { if (tempSwitchStatus != switchStatus) { onSwitchChangeListener.onSwitchChange(switchStatus); } } } } private float downX; private int moveEndX; private float clickDown; private float clickMove; // 开关状态是否发送了改变 private boolean switchStatusChange; @Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); // 必须要调用此方法,onClick点击事件方法才会生效 switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downX = event.getX(); isClick = true; clickDown = event.getX(); switchStatusChange = false; tempSwitchStatus = switchStatus; break; case MotionEvent.ACTION_MOVE: // Log.d(TAG, ">>>>>不加等于:" + (int) (event.getX() - downX)); moveEndX += (int) (event.getX() - downX); Log.d(TAG,">>>>>>加等于:" + moveEndX); if (moveEndX > getLifeDragMaxValue()) { moveEndX = getLifeDragMaxValue(); } else if (moveEndX < 0){ moveEndX = 0; } dragLife = moveEndX; invalidate(); downX = event.getX(); clickMove = downX; if (Math.abs(clickMove - clickDown) > 5) { isClick = false; } break; case MotionEvent.ACTION_UP: if (dragLife > (getLifeDragMaxValue() / 2)) { dragLife = -1; switchStatus = true; switchStatusChange = true; } else if (dragLife >= 0){ dragLife = -1; switchStatus = false; switchStatusChange = true; } else { switchStatusChange = false; } invalidate(); // upX = (int) event.getX(); break; default: break; } return true; } private int getLifeDragMaxValue() { return bmSwitchBackground.getWidth() - bmSwitchDrag.getWidth(); } /*@Override protected void onFinishInflate() { super.onFinishInflate(); if (switchStatus) { moveEndX = getLifeDragMaxValue(); Log.d(TAG, ">>>>>>>>>>>>>>>>>onFinishInflate()............ getLifeDragMaxValue():" + getLifeDragMaxValue()); } }*/ /** * 定义点击事件状态 */ private boolean isClick = true; @Override public void onClick(View v) { Log.d(TAG, "onClick() isClick:" + isClick); if (isClick) { if (switchStatus) { switchStatus = false; switchStatusChange = true; } else { switchStatus = true; switchStatusChange = true; } // switchStatus = (switchStatus==true?false:true); dragLife = -1; invalidate(); } } private OnSwitchChangeListener onSwitchChangeListener; /** * 用户设置的 状态监听 * @param onSwitchChangeListener */ public void setOnSwitchChangeListener(OnSwitchChangeListener onSwitchChangeListener) { this.onSwitchChangeListener = onSwitchChangeListener; } }
布局文件中去引用写好的自定义开关类
并设置自定义属性:
myswitch:switch_background="@mipmap/switch_background" myswitch:switch_drag="@mipmap/switch_drag" myswitch:switch_status="true"
<!-- 自定义开关升级版 --> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:myswitch="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".upgrade.MainActivity"> <!-- 使用wrap_content,是因为不知道按钮的背景有多大,更加按钮图片的改变而变化 --> <custom.view.upgrade.my_switch.MySwitch android:id="@+id/custom_myswitch" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" myswitch:switch_background="@mipmap/switch_background" myswitch:switch_drag="@mipmap/switch_drag" myswitch:switch_status="true" /> </RelativeLayout>
自定义规则arrts.xml文件声明:
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="MySwitch"> <attr name="switch_status" format="boolean" /> <attr name="switch_background" format="reference" /> <attr name="switch_drag" format="reference" /> </declare-styleable> </resources>
模拟用户来使用:
MySwitch mySwitch = findViewById(R.id.custom_myswitch); // 设置开关的背景图片 mySwitch.setBmSwitchBackground(R.mipmap.switch_background); // 设置开关拖动的图片 mySwitch.setBmSwitchDrag(R.mipmap.switch_drag); // 设置开关的状态,打开、关闭 mySwitch.setSwitChStatus(false); mySwitch.setOnSwitchChangeListener(new OnSwitchChangeListener() { @Override public void onSwitchChange(boolean switchChangeStatus) { String result; if (switchChangeStatus) { result = "打开"; } else { result = "关闭"; } Toast.makeText(MainActivity.this, "开关已" + result, Toast.LENGTH_SHORT).show(); } });