国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

自定義滑動按鈕為例圖文剖析Android自定義View繪制

2020-01-02 06:59:35
字體:
來源:轉載
供稿:網友

自定義View一直是橫在Android開發者面前的一道坎。

一、View和ViewGroup的關系

從View和ViewGroup的關系來看,ViewGroup繼承View。

View的子類,多是功能型的控件,提供繪制的樣式,比如imageView,TextView等,而ViewGroup的子類,多用于管理控件的大小,位置,如LinearLayout,RelativeLayout等,從下圖可以看出

從實際應用中看,他們又是組合關系,我們在布局中,常常是一個ViewGroup嵌套多個ViewGroup或View,而被嵌套的ViewGroup又會嵌套多個ViewGroup或View

如下

二、View的繪制流程

從View源碼來看,主要關系三個方法:

1、measure():測量
     一個final方法,控制控件的大小
2、layout():布局
         用來控制自己的布局位置
          有相對性,只相對于自己的父類布局,不關心祖宗布局
3、draw():繪制
          用來控制控件的顯示樣式

流程:  流程 measure --> layout --> draw

對應于我們要實現的方法是

onMeasure()

onLayout()

onDraw()

實際繪制中,我們的思考順序一般是這樣的:

是否需要控制控件的大小-->是-->onMeasure()
(1)如果這個自定義view不是ViewGroup,onMeasure()方法調用setMeasureDeminsion(width,height):用來設置自己的大小
(2)如果是ViewGroup,onMeasure()方法調用 ,child.measure()測量孩子的大小,給出孩子的期望大小值,之后-->setMeasureDeminsion(width,height):用來設置自己的大小

是否需要控制控件的擺放位置-->是 -->onLayout ()

是否需要控制控件的樣子-->是 -->onDraw ()-->canvas的繪制

下面是我繪制的流程圖:

下面以自定義滑動按鈕為例,說明自定義View的繪制流程

我們期待實現這樣的效果:

拖動或點擊按鈕,開關向右滑動,變成


其中開關能隨著手指的觸摸滑動到相應位置,直到最后才固定在開關位置上

新建一個類繼承自View,實現其兩個構造方法

public class SwitchButtonView extends View {       public SwitchButtonView(Context context) {     this(context, null);   }    public SwitchButtonView(Context context, AttributeSet attrs) {     super(context, attrs);   } 

drawable資源中添加這兩張圖片

借此,我們可以用onMeasure()確定這個控件的大小

@Override   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {      mSwitchButton = BitmapFactory.decodeResource(getResources(), R.drawable.switch_background);     mSlideButton = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button_background);     setMeasuredDimension(mSwitchButton.getWidth(), mSwitchButton.getHeight());   } 

這個控件并不需要控制其擺放位置,略過onLayout();

接下來onDraw()確定其形狀。

但我們需要根據我們點擊控件的不同行為來確定形狀,這需要重寫onTouchEvent()

其中的邏輯是:

當按下時,觸發事件MotionEvent.Action_Down,若此時狀態為關:

(1)若手指觸摸點(通過event.getX()得到)在按鈕的 中線右側,按鈕向右滑動一段距離(event.getX()與開關控件一半寬度之差,具體看下文源碼)

(2)若手指觸摸點在按鈕中線左側,按鈕依舊處于最左(即“關”的狀態)。

若此時狀態為開:

(1)若手指觸摸點在按鈕中線左側,按鈕向左滑動一段距離

(2)若手指觸摸點在按鈕中線右側,按鈕依舊處于最右(即“開”的狀態)

當滑動時,觸發時間MotionEvent.Action_MOVE,邏輯與按下時一致

注意,onTouchEvent()需要設置返回值 為 Return true,否則無法響應滑動事件

當手指收起時,若開關中線位于整個控件中線左側,設置狀態為關,反之,設置為開。

具體源碼如下所示:(還對外提供了一個暴露此時開關狀態的接口)

自定義View部分

package com.lian.switchtogglebutton;  import android.content.Context; 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;  /**  * Created by lian on 2016/3/20.  */ public class SwitchButtonView extends View {    private static final int STATE_NULL = 0;//默認狀態   private static final int STATE_DOWN = 1;   private static final int STATE_MOVE = 2;   private static final int STATE_UP = 3;    private Bitmap mSlideButton;   private Bitmap mSwitchButton;   private Paint mPaint = new Paint();   private int buttonState = STATE_NULL;   private float mDistance;   private boolean isOpened = false;   private onSwitchListener mListener;    public SwitchButtonView(Context context) {     this(context, null);   }    public SwitchButtonView(Context context, AttributeSet attrs) {     super(context, attrs);   }    @Override   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {      mSwitchButton = BitmapFactory.decodeResource(getResources(), R.drawable.switch_background);     mSlideButton = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button_background);     setMeasuredDimension(mSwitchButton.getWidth(), mSwitchButton.getHeight());   }    @Override   protected void onDraw(Canvas canvas) {     super.onDraw(canvas);     if (mSwitchButton!= null){       canvas.drawBitmap(mSwitchButton, 0, 0, mPaint);     }     //buttonState的值在onTouchEvent()中確定     switch (buttonState){       case STATE_DOWN:       case STATE_MOVE:         if (!isOpened){           float middle = mSlideButton.getWidth() / 2f;           if (mDistance > middle) {             float max = mSwitchButton.getWidth() - mSlideButton.getWidth();             float left = mDistance - middle;             if (left >= max) {               left = max;             }             canvas.drawBitmap(mSlideButton,left,0,mPaint);           }            else {              canvas.drawBitmap(mSlideButton,0,0,mPaint);           }         }else{           float middle = mSwitchButton.getWidth() - mSlideButton.getWidth() / 2f;           if (mDistance < middle){             float left = mDistance-mSlideButton.getWidth()/2f;             float min = 0;             if (left < 0){               left = min;             }             canvas.drawBitmap(mSlideButton,left,0,mPaint);           }else{             canvas.drawBitmap(mSlideButton,mSwitchButton.getWidth()-mSlideButton.getWidth(),0,mPaint);           }         }            break;        case STATE_NULL:       case STATE_UP:         if (isOpened){           Log.d("開關","開著的");           canvas.drawBitmap(mSlideButton,mSwitchButton.getWidth()-mSlideButton.getWidth(),0,mPaint);         }else{           Log.d("開關","關著的");           canvas.drawBitmap(mSlideButton,0,0,mPaint);         }         break;        default:         break;     }    }    @Override   public boolean onTouchEvent(MotionEvent event) {      switch (event.getAction()){       case MotionEvent.ACTION_DOWN:         mDistance = event.getX();         Log.d("DOWN","按下");         buttonState = STATE_DOWN;         invalidate();         break;        case MotionEvent.ACTION_MOVE:         buttonState = STATE_MOVE;         mDistance = event.getX();         Log.d("MOVE","移動");         invalidate();         break;        case MotionEvent.ACTION_UP:         mDistance = event.getX();         buttonState = STATE_UP;         Log.d("UP","起開");         if (mDistance >= mSwitchButton.getWidth() / 2f){           isOpened = true;         }else {           isOpened = false;         }         if (mListener != null){           mListener.onSwitchChanged(isOpened);         }         invalidate();         break;       default:         break;     }      return true;   }    public void setOnSwitchListener(onSwitchListener listener){     this.mListener = listener;   }    public interface onSwitchListener{     void onSwitchChanged(boolean isOpened);   } } 

DemoActivity:

package com.lian.switchtogglebutton;  import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.widget.Toast;  public class MainActivity extends AppCompatActivity {    @Override   protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.activity_main);      SwitchButtonView switchButtonView = (SwitchButtonView) findViewById(R.id.switchbutton);     switchButtonView.setOnSwitchListener(new SwitchButtonView.onSwitchListener() {       @Override       public void onSwitchChanged(boolean isOpened) {         if (isOpened) {           Toast.makeText(MainActivity.this, "打開", Toast.LENGTH_SHORT).show();         }else {           Toast.makeText(MainActivity.this, "關閉", Toast.LENGTH_SHORT).show();         }       }     });   } } 

布局:

<?xml version="1.0" encoding="utf-8"?> <RelativeLayout   xmlns:android="http://schemas.android.com/apk/res/android"   xmlns:tools="http://schemas.android.com/tools"   android:layout_width="match_parent"   android:layout_height="match_parent"   android:paddingBottom="@dimen/activity_vertical_margin"   android:paddingLeft="@dimen/activity_horizontal_margin"   android:paddingRight="@dimen/activity_horizontal_margin"   android:paddingTop="@dimen/activity_vertical_margin"   tools:context="com.lian.switchtogglebutton.MainActivity">    <com.lian.switchtogglebutton.SwitchButtonView     android:id="@+id/switchbutton"     android:layout_width="wrap_content"     android:layout_height="wrap_content"     /> </RelativeLayout> 

以上就是本文的全部內容,希望對大家的學習有所幫助。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 文昌市| 四会市| 岳阳县| 霍山县| 通辽市| 黑龙江省| 新龙县| 义马市| 香港 | 当雄县| 道真| 芦山县| 南皮县| 象州县| 庆云县| 房产| 天镇县| 太仓市| 东源县| 翁牛特旗| 宁国市| 沭阳县| 扶风县| 视频| 平舆县| 武夷山市| 汉川市| 绥芬河市| 昌吉市| 田林县| 阿拉善右旗| 修武县| 淮北市| 南乐县| 郴州市| 平顶山市| 九江县| 岱山县| 邳州市| 内江市| 柘城县|