Android自訂控制項之開關按鈕學習筆記分享_Android

來源:互聯網
上載者:User

今天來講講自訂單個控制項,就拿開關按鈕來講講,相信大家見了非常多這樣的了,先看看效果:

我們可以看到一個很常見的開關按鈕,那就來分析分析。

首先:

這是由兩張圖片構成:

①一張為有開和關的背景圖片

②一張為控制開和關的滑動按鈕

第一步:

寫個類繼承View,並重寫幾個方法:

第一個為建構函式,重寫一個參數的函數和兩個參數的函數就夠了,因為兩個參數的函數能夠使用自訂屬性

第二個為控制控制項的大小–>protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {}

第三個為繪製控制項的方法–>protected void onDraw(Canvas canvas) {}

第二步:

將使用者指定的兩張圖片載入進來,這裡使用自訂屬性載入, 在values目錄下建立attrs.xml,在xml檔案中指定自訂屬性名和自訂屬性的欄位及實值型別(即背景圖和滑塊圖)即可:

<?xml version="1.0" encoding="utf-8"?><resources>  <declare-styleable name="switchView_attrs">    <attr name="background" format="reference"></attr>    <attr name="slide" format="reference"></attr>  </declare-styleable>  </resources>

各個欄位的含義我在這就不講了,不懂的就去看看前幾篇《Android開發筆記之自訂群組合控制項》有講過,寫好就只需在建構函式中載入進來

public SwitchView(Context context, AttributeSet attrs) {    super(context, attrs);    //拿到自訂屬性    TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.switchView_attrs);    //拿到自訂欄位的值    Drawable switchBackground = ta.getDrawable(R.styleable.switchView_attrs_background);    Drawable switchView_slide = ta.getDrawable(R.styleable.switchView_attrs_slide);    //把值設定到相應組件上    backgroundBitmap = convertDrawable2BitmapSimple(switchBackground);    switchSlide = convertDrawable2BitmapSimple(switchView_slide);  }

不過要注意的是:

因為從自訂屬性中取到的是Drawable對象,而我們要的是一個Bitmap對象,所以我們先得把Drawable對象轉成Bitmap對象,其實有很多種方法來轉,這裡就介紹種最簡單的方法,藉助與BitmapDrawable類:

//將Drawable轉成Bitmap  public Bitmap convertDrawable2BitmapSimple(Drawable drawable) {    BitmapDrawable bd= (BitmapDrawable)drawable;    return bd.getBitmap();  }

第三步:

onMeasure方法來控制控制項的大小,我們可以看到這個開關按鈕的大小就跟背景的大小一樣大,只需要設定為背景的大小:

//控制控制項的大小  @Override  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    if (backgroundBitmap != null) {      //控制項大小設定為背景的大小      setMeasuredDimension(backgroundBitmap.getWidth(), backgroundBitmap.getHeight());    }else {      super.onMeasure(widthMeasureSpec, heightMeasureSpec);    }  }

第四步:

既然這個開關按鈕需要通過點擊或移動來控制控制項的開和關,所以就需要實現onTouchEvent方法,當然應該有三個事件會觸發:按下、移動和抬起的時候,每次點擊、移動或抬起都需要重繪,我們先來分析下滑塊的狀態有哪些(應該有四種狀態)一開始預設狀態為空白:

1.點擊的時候
2.移動的時候
3.抬起的時候
4.空的時候(即什麼都沒乾的時候)

先分析下點擊的時候的情況:

①當按下或移動的座標大於滑塊寬度一半時將滑塊右移

②當按下或移動的座標小於滑塊寬度一半時滑塊不動

註:防止滑塊移至背景外面,最大是滑塊右邊和背景右邊對齊(即最大離左邊為背景寬度-滑塊寬度)

再來看看移動的時候的情況應該是和點擊的時候是一樣的。

再來分析抬起的時候的情況:

①如果開關狀態是開啟的就將滑塊移動至右邊

②如果開關狀態是關閉的就將滑塊移動至左邊

那怎麼判斷什麼時候是開啟狀態和關閉狀態呢??

①抬起的座標大於背景寬度一半的時候設為開啟狀態

②抬起的座標小於背景寬度座標一 半的時候設為關閉狀態

再來分析下空的時候,可以發現它和抬起的時候的情況是一樣的。

第五步:

在onDraw方法中將背景和滑塊繪製出來。剛才分析了onTouchEvent方法,這次是一樣的,滑塊的四個狀態分別處理,前面onTouchEvent方法中滑塊的狀態改變,然後通過invalidate()方法來通知系統重繪。

第六步:

我們做這個自訂控制項是為了讓使用者使用的,現在這個是沒有什麼用的,使用者用不了,所以可以通過設定監聽器來對外提供介面。

/**    * switchView開關監聽介面   *    * */  interface OnSwitchChangedListener {    public void onSwitchChange(boolean isOpen);  }  /**    * 設定 switchView狀態監聽    * */  public void setOnChangeListener(OnSwitchChangedListener listener) {    switchListener = listener;  }

這個監聽器中的boolean值需要賦值,那在什麼時候賦值呢,應該是在抬起或空的狀態的時候給它賦值,因為那個時候才真正確定開關按鈕是開啟的還是關閉的。

第七步:

到這一步就是來使用了,在布局檔案中把自訂的這個控制項定義出來

<com.example.custom.SwitchView    minguo:background="@drawable/switch_background"    minguo:slide="@drawable/slide_button_background"    android:id="@+id/switchView"    android:layout_width="wrap_content"    android:layout_height="wrap_content"/>

使用定義好的View應該都會用了,不會的去看看《android開發筆記之自訂群組合控制項》。

核心代碼:

布局檔案

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  xmlns:minguo="http://schemas.android.com/apk/res/com.example.custom"  xmlns:tools="http://schemas.android.com/tools"  android:layout_width="match_parent"  android:layout_height="match_parent"  tools:context="com.example.custom.MainActivity" >  <com.example.custom.SwitchView    minguo:background="@drawable/switch_background"    minguo:slide="@drawable/slide_button_background"    android:id="@+id/switchView"    android:layout_width="wrap_content"    android:layout_height="wrap_content"/></LinearLayout>

自訂View: SwitchView.java

/** * 自訂開關按鈕 * @author Administrator * */public class SwitchView extends View {  //背景圖片和滑塊圖片  private Bitmap backgroundBitmap,switchSlide;  //畫筆  private Paint paint;  //得到的x座標(點擊、移動、抬起)  private float currentX;  //判斷開關是否開啟的標記位  private boolean isOpen = false;  //開關開啟與關閉的監聽器  private OnSwitchChangedListener switchListener;  //滑塊的四種狀態  public static final int STATE_DOWN = 1; //按下的時候  public static final int STATE_MOVE = 2; //移動的時候  public static final int STATE_UP = 3;  //抬起的時候  public static final int STATE_NONE = 0; //空的時候(即什麼都沒乾的時候)  //標記狀態(預設為空白狀態)  private int state = STATE_NONE;  public SwitchView(Context context) {    super(context,null);  }  public SwitchView(Context context, AttributeSet attrs) {    super(context, attrs);    //拿到自訂屬性    TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.switchView_attrs);    //拿到自訂欄位的值    Drawable switchBackground = ta.getDrawable(R.styleable.switchView_attrs_background);    Drawable switchView_slide = ta.getDrawable(R.styleable.switchView_attrs_slide);    //把值設定到相應組件上    backgroundBitmap = convertDrawable2BitmapSimple(switchBackground);    switchSlide = convertDrawable2BitmapSimple(switchView_slide);  }  //將Drawable轉成Bitmap  public Bitmap convertDrawable2BitmapSimple(Drawable drawable) {    BitmapDrawable bd = (BitmapDrawable)drawable;    return bd.getBitmap();  }  //控制控制項的大小  @Override  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    if (backgroundBitmap != null) {      //控制項大小設定為背景的大小      setMeasuredDimension(backgroundBitmap.getWidth(), backgroundBitmap.getHeight());    }else {      super.onMeasure(widthMeasureSpec, heightMeasureSpec);    }  }  //繪製控制項的樣式  @Override  protected void onDraw(Canvas canvas) {    //背景為空白的時候才將背景繪製    if (backgroundBitmap != null) {      paint = new Paint();      //第一個參數表示需要畫的Bitmap      //第二個參數表示Bitmap左邊離控制項的左邊距      //第三個參數表示Bitmap上邊離控制項的上邊距      //第四個參數表示畫筆      canvas.drawBitmap(backgroundBitmap, 0, 0, paint);    }    switch (state) {    case STATE_DOWN:    //按下和移動的觸發事件都一樣,都是將滑塊移動    case STATE_MOVE:      //當按下或移動的座標大於滑塊寬度一半時將滑塊右移      if (currentX > switchSlide.getWidth()/2f) {        //讓滑塊向右滑動(重新繪製滑塊的位置)        float left = currentX - switchSlide.getWidth()/2f;        //防止滑塊移至背景外面,最大是滑塊右邊和背景右邊對齊(即最大離左邊為背景寬度-滑塊寬度)        float maxLeft = backgroundBitmap.getWidth() - switchSlide.getWidth();        if (left > maxLeft) {          left = maxLeft;        }        canvas.drawBitmap(switchSlide, left, 0, paint);      //當按下或移動的座標小於滑塊寬度一半時滑塊不動      }else if (currentX < switchSlide.getWidth()/2f) {        //讓滑塊不動就可以了        canvas.drawBitmap(switchSlide, 0, 0, paint);      }      break;    case STATE_NONE:    //空或抬起的時候將滑塊至於左邊或右邊    case STATE_UP:      //如果是開啟的將滑塊移動至右邊,並將開啟狀態傳至監聽器      if (isOpen) {        if (switchListener != null) {          switchListener.onSwitchChange(true);        }        canvas.drawBitmap(switchSlide,             backgroundBitmap.getWidth() - switchSlide.getWidth(), 0, paint);      //如果是關閉的將滑塊至於左邊,並將關閉狀態傳至監聽器      }else {        if (switchListener != null) {          switchListener.onSwitchChange(false);        }        canvas.drawBitmap(switchSlide, 0, 0, paint);      }      break;    default:      break;    }  }  @Override  public boolean onTouchEvent(MotionEvent event) {    switch (event.getAction()) {    case MotionEvent.ACTION_DOWN:      currentX = event.getX();      //將標記位修改成按下的狀態      state = STATE_DOWN;      //通知系統重新繪製介面      invalidate();//在主線程//     postInvalidate();//在子線程      break;    case MotionEvent.ACTION_MOVE:      currentX = event.getX();      //將標記位修改為移動狀態      state = STATE_MOVE;      invalidate();      break;    case MotionEvent.ACTION_UP:      currentX = event.getX();      //將標記為修改為抬起狀態      state = STATE_UP;      //抬起的座標大於背景寬度一半的時候設為開啟狀態      if (currentX > backgroundBitmap.getWidth()/2f) {        //滑塊在右邊,開啟        isOpen = true;      //抬起的座標小於背景寬度座標一 半的時候設為關閉狀態      }else if (currentX < backgroundBitmap.getWidth()) {        //滑塊在左邊,關閉        isOpen = false;      }      invalidate();      break;    }    return true;  }  /**    * switchView開關監聽介面   *    * */  interface OnSwitchChangedListener {    public void onSwitchChange(boolean isOpen);  }  /**    * 設定 switchView狀態監聽    * */  public void setOnChangeListener(OnSwitchChangedListener listener) {    switchListener = listener;  }}

MainActivity.java

public class MainActivity extends Activity {  private SwitchView switchView;  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    switchView = (SwitchView) findViewById(R.id.switchView);    switchView.setOnChangeListener(new OnSwitchChangedListener() {      @Override      public void onSwitchChange(boolean isOpen) {        if (isOpen) {          //開啟開關的時候的邏輯          Toast.makeText(MainActivity.this, "開關開啟了", Toast.LENGTH_LONG).show();        }else {          //關閉開關的時候的邏輯          Toast.makeText(MainActivity.this, "開關關閉了", Toast.LENGTH_LONG).show();        }      }    });  }}

大家看起來這麼簡單的一個寫了這麼多,其實我們學習這個不是為了寫這個,比這個好的開源多的是,而是為了學習這種思路與思維,大家趕緊試試吧!

謝謝大家的閱讀,也希望大家可以繼續關注云棲社區的更多精彩內容。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.