Android个人日记app开发,[安卓开发日记] 自定义View

一、自定义View三个步骤:onMeasure,onLayout,onDraw

onMeasure 一定要在onLayout之前,之后再onLayout

ViewGroup一般不用处理onDraw

二、onMeasure方法,测量布局

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

}

MeasureSpec

MeasureSpec 是View的一个内部类,他可以用一个int同时表示一个宽度或者高度的mode和size

一个int的32位,高两位表示mode,剩余30位表示size

mode 包含 UNSPECIFIED, EXACTLY, AT_MOST,其值的大小如下

private static final int MODE_SHIFT = 30;

public static final int UNSPECIFIED = 0 << MODE_SHIFT;

public static final int EXACTLY = 1 << MODE_SHIFT;

public static final int AT_MOST = 2 << MODE_SHIFT;

调用MeasureSpec.makeMeasureSpec(int size,int mode) 可以计算出合并后MeasureSpec值

三种模式的含义:UNSPECIFIED一般是类似ScrollView的高这种不确定值,EXACTLY一般是MatchParent和传入具体值的情况,AT_MOST一般是WRAP_CONTENT的情况,只有一个父布局限制他的最大值

实际情况会比上面复杂这里贴出关键代码

/**

* specMode是自定义容器的mode

* size是自定义容器的size

* childDimension 是子ViewLayoutParams的宽或者高

**/

switch (specMode) {

// Parent has imposed an exact size on us

case MeasureSpec.EXACTLY:

if (childDimension >= 0) {

resultSize = childDimension;

resultMode = MeasureSpec.EXACTLY;

} else if (childDimension == LayoutParams.MATCH_PARENT) {

// Child wants to be our size. So be it.

resultSize = size;

resultMode = MeasureSpec.EXACTLY;

} else if (childDimension == LayoutParams.WRAP_CONTENT) {

// Child wants to determine its own size. It can't be

// bigger than us.

resultSize = size;

resultMode = MeasureSpec.AT_MOST;

}

break;

// Parent has imposed a maximum size on us

case MeasureSpec.AT_MOST:

if (childDimension >= 0) {

// Child wants a specific size... so be it

resultSize = childDimension;

resultMode = MeasureSpec.EXACTLY;

} else if (childDimension == LayoutParams.MATCH_PARENT) {

// Child wants to be our size, but our size is not fixed.

// Constrain child to not be bigger than us.

resultSize = size;

resultMode = MeasureSpec.AT_MOST;

} else if (childDimension == LayoutParams.WRAP_CONTENT) {

// Child wants to determine its own size. It can't be

// bigger than us.

resultSize = size;

resultMode = MeasureSpec.AT_MOST;

}

break;

// Parent asked to see how big we want to be

case MeasureSpec.UNSPECIFIED:

if (childDimension >= 0) {

// Child wants a specific size... let him have it

resultSize = childDimension;

resultMode = MeasureSpec.EXACTLY;

} else if (childDimension == LayoutParams.MATCH_PARENT) {

// Child wants to be our size... find out how big it should

// be

resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;

resultMode = MeasureSpec.UNSPECIFIED;

} else if (childDimension == LayoutParams.WRAP_CONTENT) {

// Child wants to determine its own size.... find out how

// big it should be

resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;

resultMode = MeasureSpec.UNSPECIFIED;

}

break;

}

Measure流程如下

DecorView 调用measure(int widthMeasureSpec, int heightMeasureSpec)方法

measure方法中,这里会处理measure缓存等问题,一般不需要覆写,默认会在这里调用onMeasure(int widthMeasureSpec, int heightMeasureSpec)

如果是ViewGroup容器,则会先剔除Padding和Margin,然后计算出child的MeasureSpec并调用子View的measure(int widthMeasureSpec, int heightMeasureSpec)方法。子view又会走相同的流程形成递归

计算测量结果后,在onMeasure中调用setMeasuredDimension(int measuredWidth, int measuredHeight),来设置测量后的宽高。

三、onLayout方法,定位View

非容器的View一般不用实现onLayout方法

@Override

protected void onLayout(int left, int top, int right, int bottom) {

super.onLayout(left, top, right, bottom);

}

这里从getPaddingLeft和getPaddingTop开始,遍历子View逐一计算它应该放置的left,right,top,bottom

调用子View的onLayout,子View逐级调用onLayout。

getWidth和getHeight要在onLayout结束后才能获取到值

四、onDraw方法,绘制具体的显示逻辑

一般容器View不需要实现onDraw方法

@Override

protected void onDraw(Canvas canvas) {

}

为了避免在动效类自定义View频繁调用onDraw方法,导致频繁GC,要提前在构造函数中初始化Paint,Path等对象,path可以利用reset方法使用, 不能在onDraw方法重复创建

canvas.restore可以回到上次canvas.save的状态

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值