自定义View的绘制流程初探(含实例)

一直想梳理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);
    }

}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值