Android自定義控制元件之實現滑動選擇開關
阿新 • • 發佈:2019-01-01
前言:今天我們仿照著Google給我們提供的Switch控制元件來進行一次模仿,自己動手打造一個可以換滑動圖片以及背景的圖片。
-----------------分割線---------------
先看一下google提供的Switc控制元件:
其實用法很簡單就當普通的控制元件使用即可!
<Switch android:id="@+id/switch1" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Switch" />
-----------------分割線---------------
先來看下我們自定義的效果:
-----------------分割線---------------
其實自定義控制元件的套路都差不多,都需要onMeasure -> onLayout -> onDraw,然後在裡面寫一個介面回撥,把狀態傳遞給監聽者。如何配置自定義屬性請看我的另外一篇部落格《Android自定義組合控制元件之實現CheckBox變化》在這裡重複的程式碼就不囉嗦了,後面給出完整程式碼的下載連結!
注意點:
1.我們可以依據背景圖片來確定控制元件大小。
2.繪製滑塊的時候一定要判斷滑塊滑動的範圍。
3.在onTouch裡面呼叫invalidate()可引起onDraw重繪,頁面會更新。
4.在onTouch裡面返回true,表示消費了使用者的點選事件,可收到其他的事件。
其他的可以想看程式碼裡面的註解。
-----------------分割線---------------
程式碼擼去:
-----------------完整程式碼下載以及圖片資源---------------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.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; /** * 自定義滑動選擇開關 * */ public class SlideSwitchView extends View { private Bitmap switchBackgroupBitmap; // 背景圖片 private Bitmap slideButtonBitmap; // 滑塊圖片 private Paint paint; // 畫筆 private boolean mSwitchState = false; // 開關狀態, 預設false private float currentX; /** * 用於程式碼建立控制元件 * * @param context */ public SlideSwitchView(Context context) { super(context); init(); } /** * 用於在xml裡使用, 可指定自定義屬性 * * @param context * @param attrs */ public SlideSwitchView(Context context, AttributeSet attrs) { super(context, attrs); init(); // 獲取配置的自定義屬性 TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SlideSwitchView); // int switchBackgroundResource = ta.getResourceId(R.styleable.SlideSwitchView_switch_background, R.drawable.switch_background);//可以設定預設圖片 int switchBackgroundResource = ta.getResourceId(R.styleable.SlideSwitchView_switch_background, -1); int slideButtonResource = ta.getResourceId(R.styleable.SlideSwitchView_slide_button, -1); mSwitchState = ta.getBoolean(R.styleable.SlideSwitchView_switch_state, false); setSwitchBackgroundResource(switchBackgroundResource); setSlideButtonResource(slideButtonResource); } /** * 用於在xml裡使用, 可指定自定義屬性, 如果指定了樣式, 則走此建構函式 * * @param context * @param attrs * @param defStyle */ public SlideSwitchView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { paint = new Paint(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 依據背景圖片來確定控制元件大小 setMeasuredDimension(switchBackgroupBitmap.getWidth(), switchBackgroupBitmap.getHeight()); } // Canvas 畫布, 畫板. 在上邊繪製的內容都會顯示到介面上. @Override protected void onDraw(Canvas canvas) { // 1. 繪製背景 canvas.drawBitmap(switchBackgroupBitmap, 0, 0, paint); // 2. 繪製滑塊 if (isTouchMode) { // 根據當前使用者觸控到的位置畫滑塊 // 讓滑塊向左移動自身一半大小的位置 float newLeft = currentX - slideButtonBitmap.getWidth() / 2.0f; int maxLeft = switchBackgroupBitmap.getWidth() - slideButtonBitmap.getWidth(); // 限定滑塊範圍 if (newLeft < 0) { newLeft = 0; // 左邊範圍 } else if (newLeft > maxLeft) { newLeft = maxLeft; // 右邊範圍 } canvas.drawBitmap(slideButtonBitmap, newLeft, 0, paint); } else { // 根據開關狀態boolean, 直接設定圖片位置 if (mSwitchState) {// 開 int newLeft = switchBackgroupBitmap.getWidth() - slideButtonBitmap.getWidth(); canvas.drawBitmap(slideButtonBitmap, newLeft, 0, paint); } else {// 關 canvas.drawBitmap(slideButtonBitmap, 0, 0, paint); } } } boolean isTouchMode = false; private OnSwitchStateUpdateListener onSwitchStateUpdateListener; // 重寫觸控事件, 響應使用者的觸控. @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: isTouchMode = true; currentX = event.getX(); break; case MotionEvent.ACTION_MOVE: currentX = event.getX(); break; case MotionEvent.ACTION_UP: isTouchMode = false; currentX = event.getX(); float center = switchBackgroupBitmap.getWidth() / 2.0f; // 根據當前按下的位置, 和控制元件中心的位置進行比較. boolean state = currentX > center; // 如果開關狀態變化了, 通知介面. 裡邊開關狀態更新了. if (state != mSwitchState && onSwitchStateUpdateListener != null) { // 把最新的boolean, 狀態傳出去了 onSwitchStateUpdateListener.onStateUpdate(state); } mSwitchState = state; break; } // 重繪介面 invalidate(); // 會引發onDraw()被呼叫, 裡邊的變數會重新生效.介面會更新 return true; // 消費了使用者的觸控事件, 才可以收到其他的事件. } /** * 設定背景圖 * * @param switchBackground */ public void setSwitchBackgroundResource(int switchBackground) { if (switchBackground > 0) { switchBackgroupBitmap = BitmapFactory.decodeResource(getResources(), switchBackground); } else { Log.e("---SlideSwitchView--->", "沒有設定開關背景圖,應設定app:switch_background=\"@drawable/xxx\""); } } /** * 設定滑塊圖片資源 * * @param slideButton */ public void setSlideButtonResource(int slideButton) { if (slideButton > 0) { slideButtonBitmap = BitmapFactory.decodeResource(getResources(), slideButton); } else { Log.e("---SlideSwitchView--->", "沒有設定滑動按鈕圖,應設定app:slide_button=\"@drawable/xxx\""); } } /** * 設定開關狀態 */ public void setSwitchState(boolean mSwitchState) { this.mSwitchState = mSwitchState; } public interface OnSwitchStateUpdateListener { // 狀態回撥, 把當前狀態傳出去 void onStateUpdate(boolean state); } public void setOnSwitchStateUpdateListener(OnSwitchStateUpdateListener onSwitchStateUpdateListener) { this.onSwitchStateUpdateListener = onSwitchStateUpdateListener; } }