
从实际应用中看,他们又是组合关系,我们在布局中,常常是一个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();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>以上就是本文的全部内容,希望对大家的学习有所帮助。