1. 程式人生 > >Android完全自定義控制元件並且實現監聽事件

Android完全自定義控制元件並且實現監聽事件

本篇文章帶來Android的完全自定義控制元件。載體是自定義一個開關的控制元件,並且能夠響應事件,首先我們先創一個專案,名字就叫ToggleView,修改MainActivity

public class MainActivity extends Activity {

    private ToggleView toggleView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        toggleView = (ToggleView) findViewById(R.id.toggleView);
//        toggleView.setSwitchBackgroundResource(R.drawable.switch_background);
// toggleView.setSlideButtonResource(R.drawable.slide_button); // toggleView.setSwitchState(true); // // 設定開關更新監聽 toggleView.setOnSwitchStateUpdateListener(new ToggleView.OnSwitchStateUpdateListener(){ @Override public void onStateUpdate(boolean
state) { Toast.makeText(getApplicationContext(), "state: " + state, Toast.LENGTH_SHORT).show(); } }); } // @Override // protected void onResume() { // super.onResume(); // } // }

寫一個繼承View的自定義控制元件的類ToggleView

/**
 * 自定義開關
 * @author poplar
 * 
 * Android 的介面繪製流程
 * 測量            擺放     繪製
 * measure  ->  layout  ->  draw
 *    |           |          |
 * onMeasure -> onLayout -> onDraw 重寫這些方法, 實現自定義控制元件
 * 
 * onResume()之後執行
 * 
 * View
 * onMeasure() (在這個方法裡指定自己的寬高) -> onDraw() (繪製自己的內容)
 * 
 * ViewGroup
 * onMeasure() (指定自己的寬高, 所有子View的寬高)-> onLayout() (擺放所有子View) -> onDraw() (繪製內容)
 */
public class ToggleView extends View { private Bitmap switchBackgroupBitmap; // 背景圖片 private Bitmap slideButtonBitmap; // 滑塊圖片 private Paint paint; // 畫筆 private boolean mSwitchState = false; // 開關狀態, 預設false private float currentX; /** * 用於程式碼建立控制元件 * @param context */ public ToggleView(Context context) { super(context); init(); } /** * 用於在xml裡使用, 可指定自定義屬性 * @param context * @param attrs */ public ToggleView(Context context, AttributeSet attrs) { super(context, attrs); init(); // 獲取配置的自定義屬性 String namespace = "http://schemas.android.com/apk/res-auto"; int switchBackgroundResource = attrs.getAttributeResourceValue(namespace , "switch_background", -1); int slideButtonResource = attrs.getAttributeResourceValue(namespace , "slide_button", -1); mSwitchState = attrs.getAttributeBooleanValue(namespace, "switch_state", false); setSwitchBackgroundResource(switchBackgroundResource); setSlideButtonResource(slideButtonResource); } /** * 用於在xml裡使用, 可指定自定義屬性, 如果指定了樣式, 則走此建構函式 * @param context * @param attrs * @param defStyle */ public ToggleView(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; System.out.println("event: ACTION_DOWN: " + event.getX()); currentX = event.getX(); break; case MotionEvent.ACTION_MOVE: System.out.println("event: ACTION_MOVE: " + event.getX()); currentX = event.getX(); break; case MotionEvent.ACTION_UP: isTouchMode = false; System.out.println("event: ACTION_UP: " + event.getX()); 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; default: break; } // 重繪介面 invalidate(); // 會引發onDraw()被呼叫, 裡邊的變數會重新生效.介面會更新 return true; // 消費了使用者的觸控事件, 才可以收到其他的事件. } /** * 設定背景圖 * @param switchBackground */ public void setSwitchBackgroundResource(int switchBackground) { switchBackgroupBitmap = BitmapFactory.decodeResource(getResources(), switchBackground); } /** * 設定滑塊圖片資源 * @param slideButton */ public void setSlideButtonResource(int slideButton) { slideButtonBitmap = BitmapFactory.decodeResource(getResources(), slideButton); } /** * 設定開關狀態 * @param */ public void setSwitchState(boolean mSwitchState) { this.mSwitchState = mSwitchState; } public interface OnSwitchStateUpdateListener{ // 狀態回撥, 把當前狀態傳出去 void onStateUpdate(boolean state); } public void setOnSwitchStateUpdateListener( OnSwitchStateUpdateListener onSwitchStateUpdateListener) { this.onSwitchStateUpdateListener = onSwitchStateUpdateListener; } }

修改activity_main.xml

<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:my="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=".MainActivity" >

    <com.mengxin.toggleview.ui.ToggleView
        android:id="@+id/toggleView"
        android:layout_centerInParent="true"
        my:switch_background="@drawable/switch_background"
        my:slide_button="@drawable/slide_button"
        my:switch_state="false"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>

在value資料夾下面建一個attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="ToggleView">
        <attr name="switch_background" format="reference"/>
        <attr name="slide_button" format="reference" />
        <attr name="switch_state" format="boolean" />
    </declare-styleable>

</resources>