- 自定义属性的声明和获取
- 测量onMeasure
- 布局onLayout(ViewGroup)
- 绘制onDrow
- onTouchEvent
- onInterceptTouchEvent(ViewGroup)
1. 自定义属性声明和获取
- 分析需要的自定义属性
- 在res/values/attrs.xml定义声明
- 在layout.xml文件中使用
- 在View的构造方法中获取
<declare-styleable name="MyActionbar">
<attr name="ma_title" format="string|reference"/>
<attr name="ma_title_text_color" format="color|reference"/>
<attr name="ma_right_img_visible" format="boolean"/>
<attr name="ma_back_img_visible" format="boolean"/>
<attr name="ma_right_img" format="reference"/>
<attr name="ma_back_img" format="reference"/>
<attr name="ma_second_img" format="reference"/>
<attr name="ma_center_img" format="reference"/>
<attr name="ma_right_title" format="boolean"/>
<attr name="ma_center_img_visible" format="boolean"/>
<attr name="ma_bg" format="color|reference"/>
</declare-styleable>
public MyActionbar(final Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
LayoutInflater.from(context).inflate(R.layout.custom_view_actionbar, this);
mBackImg = (ImageView) findViewById(R.id.bar_back_img);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.MyActionbar);
boolean rightTitleVisible = a.getBoolean(R.styleable.MyActionbar_ma_right_title, false);
int backImgId = a.getResourceId(R.styleable.MyActionbar_ma_back_img, 0);
if (backImgId != 0) {
mBackImg.setVisibility(VISIBLE);
mBackImg.setImageResource(backImgId);
}
mBackImg.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
});
a.recycle();
}
2. 测量onMeasure
主要由测量模式和测量数值决定
ViewGroup就是测量子view的宽和高
- EXACTLY,AT_MOST,UNSPECIFIED
- MeasureSpec
- setMeasuredDimension
- requestLayout
说明:
- EXACTLY明确的值,例如100dp的值,match_parent
- AT_MOST至多不能超过某个值,例如wrap_content,说明本身控件是由自身内容决定,最大不能超过父控件
- UNSPECIFIED没有限制,例如scrollview,listview
- 测量模式和测量数值由辅助MeasureSpec决定
- 测量是由requestLayout重新设置
例如TextView.setText(内容),在方法里面调用requesLayout
private int measureHeight(int heightMeasureSpec) {
int result = 0;
int mode = MeasureSpec.getMode(heightMeasureSpec);
int size = MeasureSpec.getSize(heightMeasureSpec);
if (mode == MeasureSpec.EXACTLY) {
result = size;
} else {
result = getHeight() + getPaddingBottom() + getPaddingTop();//计算本身高度
if (mode == MeasureSpec.AT_MOST) {
result = Math.min(result, size);
}
}
return result;
}
3. 布局onLayout(只有viewGroup才有)
- 决定子view在viewgroup中的位置(如果是view不用考虑)
- 尽可能将onMeasure中的一些操作放到这个方法(耗时操作可以放这里)
- requestLayout()
4. 绘制onDrow
- 绘制内容区域,背景不算在内
- invalidate(UI线程中调用),postInvalidate(子线程调用);界面变化
- Canvas.drawXXX
- translate,rotate,scale,skew
- save,restore
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//使用Canvas相关api绘制
}
5. onTouchEvent
- ACTION_DOWN,ACTION_MOVE,ACTION_UP
- ACTION_POINTER_DOWN,ACTION_POINTER_UP
- parent.requestDisallow
——InterceptTouchEvent(true) - VelocityTracker
6. onInterceptTouchEvent(ViewGroup的方法)
返回true,表示拦截子控件的事件
7. 保存状态
例如progressBar保存进度
- onSaveInstanceState,onRestoreInstanceState
@Override
protected Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putParcelable("instance", super.onSaveInstanceState());
bundle.putFloat("alpha", mAlpha);
return bundle;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
mAlpha = bundle.getFloat("alpha");
super.onRestoreInstanceState(bundle.getParcelable("instance"));
return;
}
super.onRestoreInstanceState(state);
}
案例
- generateLayoutParams
父容器生成子 view 的布局LayoutParams;
如果一个View想要被添加到这个容器中,这个view可以调用此方法生成和容器类匹配的布局
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
View childView = getChildAt(0);
//对应 generateLayoutParams() 返回的Layout
MarginLayoutParams params = (MarginLayoutParams) childView.getLayoutParams();
//注意 宽度不是获取 params.width
int width = childView.getMeasuredWidth() + params.leftMargin + params.rightMargin;
int height = childView.getMeasuredHeight() + params.topMargin + params.bottomMargin;
childView.layout(0, 0, width, height);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int resultWidth = 0;
int resultHeight = 0;
//获取长度
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
//获取模式
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY) {
resultWidth = widthSize;
} else {
//测量所有子view的宽度
}
//重新设置高度
setMeasuredDimension(resultWidth, resultHeight);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
网友评论