一直想梳理view的绘制原理,老是借口没有时间而一次一次浪费珍贵的自学时间,今天终于狠下心耐着性子写点小结。
Activity获得焦点时,就开始绘制自己的布局,androidframework 将会处理绘制过程,activity只需提供他的布局根节点。
上述流程主要调用了VIEW的measure,layout和draw三个函数。
绘制过程从布局的根节点开始策略和绘制整个layout tree。
Viewgroup负责监控它的子节点被绘制,每个view负责绘制自己。
绘制过程是按照树顺序遍历的,所有根节点先绘制,其他节点按照顺序进行绘制。
绘制有两个流程:测量measure过程和layout布局过程.
1. 测量:
measure()从根节点到叶子节点依次测量,这样每个view都会存有各自的dimension。它是由measure(int, int)函数唤起的。
在自定义view时,可以重写onMeasure来设置更为精确的content大小,重写完成后必须调用setMeasureDimension来存储view的宽和高。
2. 布局
布局发生在layout(int, int, int, int)中,从上到下进行。在这个过程中国,每一个parent都会用测量过程中得到的尺寸,把子节点放在正确的位置。
onMeasure方法
onMeasure方法是测量view和它的内容,决定measured width 和measured height的,这个方法由measure(int,int)方法唤起,子类可以覆写onMeasure提供更加准确和有效的测量。
在覆写onMeasue方法的时候,必须调用setMeasuredDimension(int, int)来存储这个view测量得到的measurewidth and height.否则将会由measure(int,int)方法抛出一个IlldgalStateException。
Protected void onMeasure(int widthMeasureSpec, intheightMeasureSpec), 其中有两个参数:
WidthMeasureSpec和heightMeasureSpec
每一个measureSpec由两部分组成<size, mode>,大小和模式。
有三种模式:
UNSPECIFIED
父视图来决定子视图的合适尺寸.
EXACTLY
父视图来给其子视图强加一个准确的尺寸。子视图必须使用这个大小,并确保其所有的子节点将适合这个尺寸。
AT_MOST
父视图给子视图强加一个最大尺寸。子视图必须确保它自己以及它的子视图能够适合这个尺寸。
下面展示实例
首先看效果图
代码如下:
activity的布局文件activity_demo
<!--
~ Copyright (C) 2016 Baidu, Inc. All Rights Reserved.
-->
<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">
<Button
android:id="@+id/button_annotation_next"
android:text="点击"
android:layout_centerInParent="true"
android:onClick="onDemoclick"
android:layout_width="90dp"
android:layout_height="90dp"/>
<com.baidu.annotation.DemoCardView
android:id="@+id/annotation_show"
android:layout_width="400dp"
android:layout_height="400dp"
android:visibility="gone"
/>
</RelativeLayout>
自定义view的布局文件layout_demo
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2016 Baidu, Inc. All Rights Reserved.
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="@+id/annotaion_card_root"
android:background="#0F82AB"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:id="@+id/tv_card_title_content"
android:textSize="22sp"
android:text="自定义view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<RelativeLayout
android:id="@+id/demo_card_mm_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/tv_card_title_content"
>
<ScrollView
android:id="@+id/annotation_card_mm"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="2dp"
android:layout_marginBottom="2dp"
android:scrollbarStyle="insideOverlay"
android:scrollbarSize="4px"
android:scrollbarThumbVertical="@drawable/annotation_card_scroll_bar">
<TextView android:id="@+id/annotation_card_content"
android:textColor="#FF82AB"
android:textSize="22sp"
android:text="设说说的宽哥"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</ScrollView>
</RelativeLayout>
</RelativeLayout>
</merge>
activity代码DemoActivity
/*
* Copyright (C) 2016 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.annotation;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class DemoActivity extends Activity {
private DemoCardView mDemoView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo);
Button button = (Button) findViewById(R.id.button_annotation);
mDemoView = (DemoCardView) findViewById(R.id.annotation_show);
}
public void onDemoclick(View view) {
int cardH = 720;
int cardW = 440;
mDemoView.showOrHide( cardW, cardH,
"这是一个红柱石这是一个红柱石这是一个红柱石这是一个红柱石这是一个红柱石这是一个红柱石这是一个红柱石这是一个红柱石这是一个红柱石这是一个红柱石这是一个红柱石这是一个红柱石", 12, "123");
}
}
自定义view的实现DemoCardView代码
/*
* Copyright (C) 2016 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.annotation;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.OvershootInterpolator;
import android.view.animation.ScaleAnimation;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.TextView;
public class DemoCardView extends FrameLayout {
private static final String TAG = DemoCardView.class.getSimpleName();
private static final int DURATION = 300;
private TextView mContentView;
private ScrollView mScrollView;
private int mCardWidth;
private int mCardHeight;
private boolean mAnimationshow = false;
private RelativeLayout mNotiveCardView;
boolean showState = false;
public DemoCardView(Context context) {
super(context);
initView(context);
}
public DemoCardView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public DemoCardView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView(context);
}
public void initView(Context context) {
LayoutInflater.from(context).inflate(R.layout.layout_democardview, this);
mNotiveCardView = (RelativeLayout) findViewById(R.id.annotaion_card_root);
mContentView = (TextView) findViewById(R.id.annotation_card_content);
mScrollView = (ScrollView) findViewById(R.id.annotation_card_mm);
mScrollView.setVerticalFadingEdgeEnabled(true);
showState = false;
mAnimationshow = false;
if (Build.VERSION.SDK_INT >= 16) {
mScrollView.setScrollBarFadeDuration(1);
}
if (Build.VERSION.SDK_INT >= 9) {
mScrollView.setOverScrollMode(OVER_SCROLL_NEVER);
}
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (showState) {
hide();
}
}
});
}
public void showOrHide(int w, int h, CharSequence charSequence, int fontsize, String fontname) {
if (showState) {
hide();
} else {
show( w, h, charSequence, fontsize, fontname);
}
}
private void show(int w, int h, CharSequence charSequence, int fontsize, String fontname) {
showState = true;
mCardWidth = w;
mCardHeight = h;
setText(charSequence, fontsize, fontname);
setVisibility(INVISIBLE);
invalidate();
}
@Override
public boolean isShown() {
return showState;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureCard(widthMeasureSpec, heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private void measureCard(int widthMeasureSpec, int heightMeasureSpec) {
int[] parentLocation = new int[2];
getLocationInWindow(parentLocation);
RelativeLayout.LayoutParams scrollViewLayoutParams = (RelativeLayout.LayoutParams) mScrollView
.getLayoutParams();
int scrollViewSpecWidth = MeasureSpec.getSize(widthMeasureSpec);
int scrollViewSpecHeight = MeasureSpec.getSize(heightMeasureSpec) / 2;
setSize(mNotiveCardView, mCardWidth, mCardHeight);
setSize(mScrollView, mCardWidth, mCardWidth);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
int cardl = 40;
int cardt = 40;
int cardr = cardl + mCardWidth;
int cardb = cardt + mCardHeight;
// 控制view绘制的起点
mNotiveCardView.layout(cardl, cardt, cardr, cardb);
setVisibility(VISIBLE);
startShowAnimation();
}
private void setText(CharSequence charSequence, int fonsize, String fontName) {
mContentView.setText(charSequence);
}
public void hide() {
showState = false;
startHideAnimation();
}
private void startShowAnimation() {
if (mAnimationshow) {
return;
}
mAnimationshow = true;
mScrollView.scrollTo(0, 0);
clearAnimation();
ScaleAnimation scaleAnimation;
scaleAnimation = new ScaleAnimation(0, 1.0f, 0, 1.0f, Animation.RELATIVE_TO_SELF, (float) 0 /
mCardWidth, Animation.RELATIVE_TO_SELF, 0f);
scaleAnimation.setInterpolator(new OvershootInterpolator(1.2f));
scaleAnimation.setFillBefore(true);
scaleAnimation.setFillAfter(true);
scaleAnimation.setDuration(DURATION);
mNotiveCardView.startAnimation(scaleAnimation);
}
private void startHideAnimation() {
if (!mAnimationshow) {
return;
}
mAnimationshow = false;
clearAnimation();
ScaleAnimation scaleAnimation;
scaleAnimation = new ScaleAnimation(1.0f, 0, 1.0f, 0, Animation.RELATIVE_TO_SELF, (float) 0 /
mCardWidth, Animation.RELATIVE_TO_SELF, 1.0f);
scaleAnimation.setInterpolator(new AccelerateInterpolator(2));
scaleAnimation.setFillBefore(true);
scaleAnimation.setFillAfter(true);
scaleAnimation.setDuration(DURATION);
scaleAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
setVisibility(GONE);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
mNotiveCardView.startAnimation(scaleAnimation);
}
private void setSize(View view, int w, int h) {
ViewGroup.LayoutParams params = view.getLayoutParams();
params.width = w;
params.height = h;
view.setLayoutParams(params);
}
}