自定义滑动按钮为例图文剖析Android自定义View绘制

自定义滑动按钮为例图文剖析Android自定义View绘制,第1张

概述自定义View一直是横在Android开发者面前的一道坎。一、View和ViewGroup的关系从View和ViewGroup的关系来看,ViewGroup继承View。

自定义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,attrs);   }    @OverrIDe   protected voID onMeasure(int wIDthMeasureSpec,mSwitchbutton.getHeight());   }    @OverrIDe   protected voID onDraw(Canvas canvas) {     super.onDraw(canvas);     if (mSwitchbutton!= null){       canvas.drawBitmap(mSwitchbutton,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,mPaint);           }            else {              canvas.drawBitmap(mSlIDebutton,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,mPaint);           }else{             canvas.drawBitmap(mSlIDebutton,mSwitchbutton.getWIDth()-mSlIDebutton.getWIDth(),mPaint);           }         }            break;        case STATE_NulL:       case STATE_UP:         if (isOpened){           Log.d("开关","开着的");           canvas.drawBitmap(mSlIDebutton,mPaint);         }else{           Log.d("开关","关着的");           canvas.drawBitmap(mSlIDebutton,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> 

以上就是本文的全部内容,希望对大家的学习有所帮助。

总结

以上是内存溢出为你收集整理的自定义滑动按钮为例图文剖析Android自定义View绘制全部内容,希望文章能够帮你解决自定义滑动按钮为例图文剖析Android自定义View绘制所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/1141645.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-31
下一篇2022-05-31

发表评论

登录后才能评论

评论列表(0条)

    保存