
自定义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绘制所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)