效果圖:java
定義一個類,取名爲MySwitch.java,此類去繼承View,爲什麼是繼承View而不是去繼承ViewGroup呢,是由於自定義開關沒有子控件,之須要操做自身繪製便可android
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; } }
佈局文件中去引用寫好的自定義開關類canvas
並設置自定義屬性:ide
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>
模擬用戶來使用:this
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(); } });