雖然實現自定義開關,一般狀況下都是繼承View,比較合理方便快捷一些android
可是我今天想去繼承ViewGroup來實現自定義開關來玩玩canvas
效果圖:ide
佈局代碼:佈局
<!-- 自定義開關ViewGroup版 --> <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"> <custom.view.upgrade.my_switch_viewgroup.MySwitch2 android:layout_width="200dp" android:layout_height="200dp" android:layout_centerInParent="true" android:background="#33F00000"> </custom.view.upgrade.my_switch_viewgroup.MySwitch2> </RelativeLayout>
效果:this
佈局代碼:spa
<!-- 自定義開關ViewGroup版 --> <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"> <custom.view.upgrade.my_switch_viewgroup.MySwitch2 android:layout_width="200dp" android:layout_height="200dp" android:layout_centerInParent="true" android:background="#33F00000"> <!-- 第一個子控件 --> <ImageView android:id="@+id/iv_switch_bg" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/switch_background"/> <!-- 第二個子控件 --> <ImageView android:id="@+id/iv_switch_drag" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/switch_drag"/> </custom.view.upgrade.my_switch_viewgroup.MySwitch2> </RelativeLayout>
自定義開關繼承ViewGroup代碼:code
package custom.view.upgrade.my_switch_viewgroup; import android.content.Context; import android.graphics.Canvas; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; public class MySwitch2 extends ViewGroup implements View.OnClickListener , View.OnTouchListener { private static final String TAG = MySwitch2.class.getSimpleName(); private View switchBackground; private View switchGrag; private int switchBackgroundWidth; private int switchBackgroundHeight; private int switchGragWidth; private int switchGragHeight; private boolean onCreate = false; // 開關的狀態 private boolean switchStatus; public MySwitch2(Context context, AttributeSet attrs) { super(context, attrs); // 若是是這樣寫的話,是給整個粉紅色正方形區域都設置了點擊事件 // setOnClickListener(this); } @Override protected void onFinishInflate() { super.onFinishInflate(); switchBackground = getChildAt(0); switchGrag = getChildAt(1); onCreate = true; // 給子控件設置點擊事件 switchBackground.setOnClickListener(this); switchGrag.setOnClickListener(this); // 給子控件設置Touch事件 switchBackground.setOnTouchListener(this); switchGrag.setOnTouchListener(this); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 測量第一個子控件 LayoutParams switchBgParams = switchBackground.getLayoutParams(); switchBackground.measure(switchBgParams.width, switchBgParams.height); // 測量第二個子控件 LayoutParams switchGragParams = switchGrag.getLayoutParams(); switchGrag.measure(switchGragParams.width, switchGragParams.height); // -2 表明是wrap_content // Log.d(TAG, "測量方法>>> switchGragParams.width" + switchGragParams.width); // 獲得測量後的高和寬 if (onCreate) { onCreate = false; switchBackgroundWidth = switchBackground.getMeasuredWidth(); switchBackgroundHeight = switchBackground.getMeasuredHeight(); switchGragWidth = switchGrag.getMeasuredWidth(); switchGragHeight = switchGrag.getMeasuredHeight(); } // 在拖動開關的時候,測量會執行不少次,由於在拖動的過程當中,開關Grag會的測量寬度會不斷變化 // Log.d(TAG, "測量方法>>> switchBackgroundWidth:" + switchBackgroundWidth + " switchGragWidth:" + switchGragWidth); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // 給子控件排版指定位置 switchBackground.layout( getMeasuredWidth() / 2 - switchBackground.getWidth() / 2, getMeasuredHeight() / 2 - switchBackground.getHeight() / 2, switchBackground.getMeasuredWidth() + (getMeasuredWidth() / 2 - switchBackground.getWidth() / 2), switchBackground.getMeasuredHeight() + (getMeasuredHeight() / 2 - switchBackground.getHeight() / 2)); /*switchGrag.layout( getMeasuredWidth() / 2 - (switchGrag.getWidth() / 2), getMeasuredHeight() /2 - (switchGrag.getHeight() / 2), switchGrag.getMeasuredWidth() + getMeasuredWidth() / 2 - (switchGrag.getWidth() / 2), switchGrag.getMeasuredHeight() + getMeasuredHeight() /2 - (switchGrag.getHeight() / 2));*/ int gragL = getMeasuredWidth() / 2 - (switchBackground.getWidth() / 2); int gragT = getMeasuredHeight() / 2 - (switchGrag.getHeight() / 2); switchGrag.layout( gragL, gragT, switchGrag.getMeasuredWidth() + gragL, switchGrag.getMeasuredHeight() + gragT); // 動的是兩個子控件 // scrollTo(90, 0); } /** * 打開開關 * @return */ /*private int getOpenSwitch() { return getMeasuredWidth() / 2; }*/ /** * 關閉開關 * @return */ /*private int getCloseSwitch() { return getMeasuredWidth() / 2 - (switchBackground.getWidth() / 2); }*/ /** * 開關滑動的按鈕 * @param move */ private void setScrollMove(int move) { int countValue = (switchBackground.getMeasuredWidth() - switchGrag.getMeasuredWidth()) / 2; int countValue2 = (switchBackgroundWidth - switchGragWidth); // Log.d(TAG, "switchBackgroundWidth:" + switchBackgroundWidth + " switchGragWidth:" + switchGragWidth); Log.d(TAG, "move:" + move + " countValue:" + countValue + " getMeasuredWidth():" + switchGrag.getMeasuredWidth() + " getWidth():" + switchGrag.getWidth() + " countValue2:" +countValue2); if (move < 0) { move = 0; } else if (move > countValue2) { Log.d(TAG, "移動到最右邊...."); move = countValue2; } // switchGrag.setPadding(move, 0, 0, 0); setPaddingAction(move); } /** * 注意!這個是當前MySwitch2 ViewGroup的Touch事件 * @param event * @return */ @Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); } /** * 此方法並不去繪製任何東西,Android自動回去繪製的 * @param canvas */ @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); } /** * 是否能夠點擊 */ private boolean isClick = true; @Override public void onClick(View v) { Log.d(TAG, "onClick() >>>>>>>>>>>>>>>> run"); Log.d(TAG, "onClick() isClick:" + isClick); if (isClick) { if (!switchStatus) { // 說明是關閉狀態,我要打開 moveResult = (switchBackgroundWidth - switchGragWidth); } else { moveResult = 0; } switchGrag.setPadding(moveResult, 0, 0, 0); } } private float downX; private int moveResult; private float downTempX; @Override public boolean onTouch(View v, MotionEvent event) { boolean touchResult = false; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downX = event.getX(); touchResult = false; downTempX = downX; isClick = true; break; case MotionEvent.ACTION_MOVE: moveResult += (int) (event.getX() - downX); setScrollMove(moveResult); downX = event.getX(); if (Math.abs(event.getX() - downTempX) > 5) { // 說明在滑動,就onTouch來消費掉了,不給onClick點擊事件了 touchResult = true; Log.d(TAG, "說明在滑動,就onTouch來消費掉了,不給onClick點擊事件了"); isClick = false; } break; case MotionEvent.ACTION_UP: // int upLeft = 0; if (moveResult > (switchBackgroundWidth - switchGragWidth) / 2) { moveResult = switchBackgroundWidth - switchGragWidth; } else if (moveResult < (switchBackgroundWidth - switchGragWidth) / 2) { moveResult = 0; } else if (moveResult <= 0){ moveResult = 0; } setPaddingAction(moveResult); break; default: break; } return touchResult; } private void setPaddingAction(int value) { if (value > (switchBackgroundWidth - switchGragWidth) / 2) { switchStatus = true; } else if (value < (switchBackgroundWidth - switchGragWidth) / 2) { switchStatus = false; } else if (value <= 0){ switchStatus = false; } switchGrag.setPadding(value, 0,0,0); } }