Android View制作13问13答-Android-优质IT资源分享社区

admin
管理员
管理员
  • UID1
  • 粉丝27
  • 关注4
  • 发帖数581
  • 社区居民
  • 忠实会员
  • 原创写手
阅读:172回复:0

  Android View制作13问13答

楼主#
更多 发布于:2016-05-30 22:00


1.view的制作流程分几步,从哪开端?哪个进程结束今后能看到view?
答:从ViewRoot的performTraversals开端,通过measure,layout,draw
三个流程。draw流程结束今后就能够在屏幕上看到view了。
2.view的丈量宽高和实践宽高有差异吗?
答:底子上百分之99的状况下都是能够以为没有差异的。有两种状况,有差异。第一种
即是有的时分会由于某些因素 view会屡次丈量,那首次丈量的宽高 必定和最终实践的宽高 是不必定持平的,可是在这种状况下
最终一次丈量的宽高和实践宽高是一致的。此外,实践宽高是在layout流程里断定的,咱们能够在layout流程里
将实践宽高写死 写成硬编码,这么丈量的宽高和实践宽高就必定不相同了,尽管这么做没有意义 而且也欠好。
3.view的measureSpec
由谁决议?尖端view呢?

答:由view自个的layoutparams和父容器
 一同决议自个的measureSpec。一旦断定了spec,onMeasure中就能够断定view的宽高了。
尖端view就略微格外一点,关于decorView的丈量在ViewRootImpl的源码里。
//desire的这2个参数就代表屏幕的宽高,  childWidthMeasureSpec =
getRootMeasureSpec(desiredWindowWidth, lp.width);  childHeightMeasureSpec =
getRootMeasureSpec(desiredWindowHeight, lp.height);
 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
 //decorView的measureSpec即是在这儿断定的,本来比通常view的measurespec要简略的多  //代码就不剖析了 一望而知的东西
 private static int getRootMeasureSpec(int windowSize, int rootDimension) {    
   int measureSpec;        switch (rootDimension) {        case
ViewGroup.LayoutParams.MATCH_PARENT:            // Window can't resize. Force
root view to be windowSize.            measureSpec =
MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);            break;
       case ViewGroup.LayoutParams.WRAP_CONTENT:            // Window can
resize. Set max size for root view.            measureSpec =
MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);            break;
       default:            // Window wants to be an exact size. Force root view
to be that size.            measureSpec =
MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);          
 break;        }        return measureSpec;}
4.关于通常view来说,他的measure进程中,与父view有关吗?假如有关,这个父view也即是viewgroup扮演了啥人物?
答:看源码:
//关于通常view的measure来说 是由这个view的 父view
,也即是viewgroup来触发的。//也即是下面这个measureChildWithMargins办法protected void
measureChildWithMargins(View child,            int parentWidthMeasureSpec, int
widthUsed,            int parentHeightMeasureSpec, int heightUsed) {        
//第一步 先取得子view的 layoutParams 参数值        final MarginLayoutParams lp =
(MarginLayoutParams) child.getLayoutParams();        //然后开端核算子view的spec的值,留意这儿看到
核算的时分除了要用子view的 layoutparams参数以外        //还用到了父view 也即是viewgroup自个的spec的值      
 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,  
             mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin      
                 + widthUsed, lp.width);        final int childHeightMeasureSpec
= getChildMeasureSpec(parentHeightMeasureSpec,                mPaddingTop +
mPaddingBottom + lp.topMargin + lp.bottomMargin                        +
heightUsed, lp.height);        child.measure(childWidthMeasureSpec,
childHeightMeasureSpec);}//这个算view的spec的办法 看上去一大串 可是真的逻辑十分简略
即是依据爸爸viewgroup//的meaurespec 一起还有view自个的params来断定
view自个的measureSpec。//留意这儿的参数是padding,这个值的意义是 父容器已占用的控件的巨细 所以view的Specsize//的值
你们能够看到 是要减去这个padding的值的。总巨细-现已用的 =可用的。 极好了解。//然后即是下面的switch逻辑
要自个梳理明白。本来也不难,主要是下面几条准则//假如view采用固定宽高,也即是写死的数值那种。那就不论爸爸的spec的值了,view的spec
就必定是exactly 而且巨细遵从layout参数里设置的巨细。//假如view的宽高是match_parent ,那么就要看父容器viewgroup的
spec的值了,假如父view的spec是exactly方式,//那view也必定是exactly,而且巨细即是父容器剩下的空间。假如父容器是at_most方式,那view也是at_most
而且不会超越剩下空间巨细//假如view的宽高是wrap_content, 那就不论父容器的spec了,view的spec必定是at_most
而且不会超越父view 剩下空间的巨细。public static int getChildMeasureSpec(int spec, int padding,
int childDimension) {        int specMode = MeasureSpec.getMode(spec);      
 int specSize = MeasureSpec.getSize(spec);        int size = Math.max(0,
specSize - padding);        int resultSize = 0;        int resultMode = 0;      
 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;        }  
     return MeasureSpec.makeMeasureSpec(resultSize, resultMode);    }
5.view的meaure和onMeasure有啥关系?
答:看源码:
//view的measure是final 办法 咱们子类无法修正的。 public final
void measure(int widthMeasureSpec, int heightMeasureSpec) {        boolean
optical = isLayoutModeOptical(this);        if (optical !=
isLayoutModeOptical(mParent)) {            Insets insets = getOpticalInsets();  
         int oWidth  = insets.left + insets.right;            int oHeight =
insets.top  + insets.bottom;            widthMeasureSpec  =
MeasureSpec.adjust(widthMeasureSpec,  optical ? -oWidth  : oWidth);          
 heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight :
oHeight);        }        // Suppress sign extension for the low bytes      
 long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec &
0xffffffffL;        if (mMeasureCache == null) mMeasureCache = new
LongSparseLongArray(2);        if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) ==
PFLAG_FORCE_LAYOUT ||                widthMeasureSpec != mOldWidthMeasureSpec ||
               heightMeasureSpec != mOldHeightMeasureSpec) {            // first
clears the measured dimension flag            mPrivateFlags &=
~PFLAG_MEASURED_DIMENSION_SET;            resolveRtlPropertiesIfNeeded();      
     int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) ==
PFLAG_FORCE_LAYOUT ? -1 :                    mMeasureCache.indexOfKey(key);    
       if (cacheIndex < 0 || sIgnoreMeasureCache) {                // measure
ourselves, this should set the measured dimension flag back              
 onMeasure(widthMeasureSpec, heightMeasureSpec);                mPrivateFlags3
&= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;            } else {              
 long value = mMeasureCache.valueAt(cacheIndex);                // Casting a
long to int drops the high 32 bits, no mask needed              
 setMeasuredDimensionRaw((int) (value >> 32), (int) value);              
 mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;            }          
 // flag not set, setMeasuredDimension() was not invoked, we raise            //
an exception to warn the developer            if ((mPrivateFlags &
PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {              
 throw new IllegalStateException("View with id " + getId() + ": "              
         + getClass().getName() + "#onMeasure() did not set the"                
       + " measured dimension by calling"                        + "
setMeasuredDimension()");            }            mPrivateFlags |=
PFLAG_LAYOUT_REQUIRED;        }        mOldWidthMeasureSpec = widthMeasureSpec;
       mOldHeightMeasureSpec = heightMeasureSpec;        mMeasureCache.put(key,
((long) mMeasuredWidth) << 32 |                (long) mMeasuredHeight
& 0xffffffffL); // suppress sign extension  
 }//不过能够看到的是在measure办法里调用了onMeasure办法//所以就能知道 咱们在自定义view的时分必定是重写这个办法! protected
void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {      
 setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(),
widthMeasureSpec),                getDefaultSize(getSuggestedMinimumHeight(),
heightMeasureSpec));    }
6.扼要剖析view的measure流程?
答:先回顾疑问4,viewgroup 算出子view的spec今后
会调用子view的measure办法,而子view的measure办法 咱们疑问5也看过了实践上是调用的onMeasure办法
所以咱们只需剖析好onMeasure办法即可,留意onMeasure办法的参数
恰是他的父view算出来的那2个spec的值(这儿view的measure办法会把这个spec里的specSize值做略微的修正 这个有些 不做剖析
由于measure办法修正specSize的有些很简略)。
//能够看出来这个即是setMeasuredDimension办法的调用
这个办法看姓名就知道即是断定view的丈量宽高的//所以咱们剖析的重点即是看这个getDefaultSize 办法 是怎样断定view的丈量宽高的
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {      
 setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(),
widthMeasureSpec),                getDefaultSize(getSuggestedMinimumHeight(),
heightMeasureSpec));    }//这个办法格外简略
底子能够以为即是近似的回来spec中的specSize,除非你的specMode是UNSPECIFIED//UNSPECIFIED
这个通常都是体系内部丈量才用的到,这种时分回来size 也即是getSuggestedMinimumWidth的回来值 public static int
getDefaultSize(int size, int measureSpec) {        int result = size;        int
specMode = MeasureSpec.getMode(measureSpec);        int specSize =
MeasureSpec.getSize(measureSpec);        switch (specMode) {        case
MeasureSpec.UNSPECIFIED:            result = size;            break;        case
MeasureSpec.AT_MOST:        case MeasureSpec.EXACTLY:            result =
specSize;            break;        }        return result;}//跟view的布景有关
这儿不多做剖析了protected int getSuggestedMinimumWidth() {        return (mBackground ==
null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());    }
7.自定义view中 假如onMeasure办法 没有对wrap_content
做处理 会发作啥?为啥?怎样处理?

答:假如没有对wrap_content做处理
,那即使你在xml里设置为wrap_content.其效果也和match_parent相同。看疑问4的剖析。咱们能够知道view自个的layout为wrap,那mode即是at_most(不论爸爸view是啥specmode).
这种方式下宽高即是等于specSize(getDefaultSize函数剖析可知),而这儿的specSize明显即是parentSize的巨细。也即是父容器剩下的巨细。那不就和咱们直接设置成match_parent是相同的效果了么?
处理办法即是在onMeasure里 对于wrap 来做格外处理
比方指定一个默许的宽高,当发现是wrap_content 就设置这个默许宽高即可。
8.ViewGroup有onMeasure办法吗?为啥?
答:没有,这个办法是交给子类自个完成的。不相同的viewgroup子类
必定规划都不相同,那onMeasure索性就悉数交给他们自个完成好了。
9.为啥在activity的生命周期里无法取得丈量宽高?有啥办法能够处理这个疑问吗?
答:由于measure的进程和activity的生命周期
 没有任何关系。你无法断定在哪个生命周期履行结束今后 view的measure进程必定走完。能够测验如下几种办法 获取view的丈量宽高。
//重写activity的这个办法public void
onWindowFocusChanged(boolean hasFocus) {      
 super.onWindowFocusChanged(hasFocus);        if (hasFocus) {            int
width = tv.getMeasuredWidth();            int height = tv.getMeasuredHeight();  
         Log.v("burning", "width==" + width);            Log.v("burning",
"height==" + height);        }    }
或许重写这个办法
@Override    protected void onStart() {      
 super.onStart();        tv.post(new Runnable() {            @Override          
 public void run() {                int width = tv.getMeasuredWidth();          
     int height = tv.getMeasuredHeight();            }        });    }
再或许:
@Override    protected void onStart() {      
 super.onStart();        ViewTreeObserver observer = tv.getViewTreeObserver();  
     observer.addOnGlobalLayoutListener(new
ViewTreeObserver.OnGlobalLayoutListener() {            @Override          
 public void onGlobalLayout() {                int width =
tv.getMeasuredWidth();                int height = tv.getMeasuredHeight();      
         tv.getViewTreeObserver().removeOnGlobalLayoutListener(this);          
 }        });    }
10.layout和onLayout办法有啥差异?
答:layout是断定自身view的方位 而onLayout是断定一切子元素的方位。layout里边
即是通过serFrame办法设设定自身view的 四个极点的方位。这4个方位以断定 自个view的方位就固定了
然后就调用onLayout来断定子元素的方位。view和viewgroup的onlayout办法都没有写。都留给咱们自个给子元素规划
11.draw办法 大概有几个过程?
答: 一共是4个过程, 制作布景———制作自个——–制作chrildren—-制作装修。
12.setWillNotDraw办法有啥用?
答:这个办法在view里。
/**     * If this view doesn't do any drawing on
its own, set this flag to     * allow further optimizations. By default, this
flag is not set on     * View, but could be set on some View subclasses such as
ViewGroup.     *     * Typically, if you override {@link
#onDraw(android.graphics.Canvas)}     * you should clear this flag.     *     *
@param willNotDraw whether or not this View draw on its own     */    public
void setWillNotDraw(boolean willNotDraw) {        setFlags(willNotDraw ?
WILL_NOT_DRAW : 0, DRAW_MASK);    }
用于设置象征位的 也即是说 假如你的自定义view
不需要draw的话,就能够设置这个办法为true。这么体系知道你这个view 不需要draw 能够优化履行速度。viewgroup
通常都默许设置这个为true,由于viewgroup大都都是只担任规划
不担任draw的。而view 这个象征位 默许通常都是封闭的。
13.自定义view 有哪些需要留意的点?
答:主要是要处理wrap_content 和padding。不然xml
那儿设置这2个特点就底子没用了。还有不要在view中运用handler
由于人家现已供给了post办法。假如是承继自viewGroup,那在onMeasure和onLayout里边 也要思考
padding和layout的影响。也即是说specSize 要算一下
。最终即是假如view的动画或许线程需要停止,能够思考在onDetachedFromWindow里边来做。
对于上述的几点,给出几个简略的自定义view 供咱们了解。
给出一个圆形的view 典范:
package
com.example.administrator.motioneventtest;import android.content.Context;import
android.graphics.Canvas;import android.graphics.Color;import
android.graphics.Paint;import android.util.AttributeSet;import
android.view.View;/** * Created by Administrator on 2016/2/4. */public class
CircleView extends View {    private int mColor = Color.RED;    private Paint
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);    private void init() {      
 mPaint.setColor(mColor);    }    @Override    protected void onMeasure(int
widthMeasureSpec, int heightMeasureSpec) {      
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int widthSpecMode
= MeasureSpec.getMode(widthMeasureSpec);        int widthSpecSize =
MeasureSpec.getSize(widthMeasureSpec);        int heightSpecMode =
MeasureSpec.getMode(heightMeasureSpec);        int heightSpecSize =
MeasureSpec.getSize(heightMeasureSpec);        //处理为wrap_content时的状况        if
(widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode ==
MeasureSpec.AT_MOST) {            setMeasuredDimension(200, 200);        } else
if (widthSpecMode == MeasureSpec.AT_MOST) {            setMeasuredDimension(200,
heightSpecSize);        } else if (heightSpecMode == MeasureSpec.AT_MOST) {    
       setMeasuredDimension(widthSpecSize, 200);        }    }    @Override  
 protected void onDraw(Canvas canvas) {        super.onDraw(canvas);      
 //处理padding的状况        final int paddingLeft = getPaddingLeft();        final
int paddingRight = getPaddingRight();        final int paddingTop =
getPaddingTop();        final int paddingBottom = getPaddingBottom();        int
width = getWidth() - paddingLeft - paddingRight;        int height = getHeight()
- paddingTop - paddingBottom;        int radius = Math.min(width, height) / 2;  
     canvas.drawCircle(paddingLeft + width / 2, paddingTop + height / 2, radius,
mPaint);    }    public CircleView(Context context, AttributeSet attrs, int
defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }
   public CircleView(Context context) {        super(context);        init();  
 }    public CircleView(Context context, AttributeSet attrs) {      
 super(context, attrs);        init();    }}
然后下面再给出一个典范,略微杂乱一点是自定义viewgroup了(主要是加强对onMeasure和onLayout的了解),
需要如下:
一个水平的viewgroup,内部的子元素 为了简略
咱们假定他们的宽高都是相同的。来写一个这么的简略的viewgroup。
package
com.example.administrator.motioneventtest;import android.content.Context;import
android.util.AttributeSet;import android.util.Log;import
android.view.View;import android.view.ViewGroup;/** * Created by Administrator
on 2016/2/4. *///这儿咱们只处理了padding的状况 没有处理margin的状况,子view的margin
对measure和layout的影响//就留给读者自个完成了public class CustomHorizontalLayout extends
ViewGroup {    //设置默许的控件最小是多少 这儿不供给自定义特点了 写死在代码里 你们能够自行拓展    final int minHeight
= 0;    final int minWidth = 0;    public CustomHorizontalLayout(Context
context) {        super(context);    }    public CustomHorizontalLayout(Context
context, AttributeSet attrs) {        super(context, attrs);    }    public
CustomHorizontalLayout(Context context, AttributeSet attrs, int defStyleAttr) {
       super(context, attrs, defStyleAttr);    }    @Override    protected void
onMeasure(int widthMeasureSpec, int heightMeasureSpec) {      
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int measureWidth =
0;        int measureHeight = 0;        final int childCount = getChildCount();
       measureChildren(widthMeasureSpec, heightMeasureSpec);        int
widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);        int widthSpecSize
= MeasureSpec.getSize(widthMeasureSpec);        int heightSpecMode =
MeasureSpec.getMode(heightMeasureSpec);        int heightSpecSize =
MeasureSpec.getSize(heightMeasureSpec);        final View childView =
getChildAt(0);        final int paddingLeft = getPaddingLeft();        final int
paddingRight = getPaddingRight();        final int paddingTop = getPaddingTop();
       final int paddingBottom = getPaddingBottom();        //没有子控件 时
咱们的宽高要作格外处理        if (childCount == 0) {            //当没有子控件时,假如长宽有一个为wrap
那么就让这个控件以最小的方式展现            //这儿咱们最小设置为0            if (widthSpecMode ==
MeasureSpec.AT_MOST || heightSpecMode == MeasureSpec.AT_MOST) {              
 setMeasuredDimension(minWidth, minHeight);            } else {              
 //不然依据咱们的layout特点来                setMeasuredDimension(getLayoutParams().width,
getLayoutParams().height);            }        } else if (widthSpecMode ==
MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {        
   measureWidth = childView.getMeasuredWidth() * childCount;          
 measureHeight = childView.getMeasuredHeight();          
 setMeasuredDimension(paddingLeft + measureWidth + paddingRight, paddingTop +
measureHeight + paddingBottom);        } else if (heightSpecMode ==
MeasureSpec.AT_MOST) {            measureHeight = childView.getMeasuredHeight();
           setMeasuredDimension(paddingLeft + paddingRight + widthSpecSize,
paddingTop + paddingBottom + measureHeight);        } else if (widthSpecMode ==
MeasureSpec.AT_MOST) {            measureWidth = childView.getMeasuredWidth() *
childCount;            setMeasuredDimension(paddingLeft + paddingRight +
measureWidth, paddingTop + paddingBottom + heightSpecSize);        }    }  
 @Override    protected void onLayout(boolean changed, int l, int t, int r, int
b) {        final int paddingLeft = getPaddingLeft();        final int
paddingRight = getPaddingRight();        final int paddingTop = getPaddingTop();
       final int paddingBottom = getPaddingBottom();        //左面初始方位为0      
 int childLeft = 0 + paddingLeft;        final int childCount = getChildCount();
       for (int i = 0; i < childCount; i++) {            final View childView
= getChildAt(i);            if (childView.getVisibility() != View.GONE) {      
         final int childWidth = childView.getMeasuredWidth();              
 childView.layout(childLeft, 0 + paddingTop, childLeft + childWidth, paddingTop
+ childView.getMeasuredHeight());                childLeft += childWidth;      
     }        }    }}








优质IT资源分享社区为你提供此文。
站有大量优质android教程视频,资料等资源,包含android基础教程,高级进阶教程等等,教程视频资源涵盖传智播客,极客学院,达内,北大青鸟,猎豹网校等等IT职业培训机构的培训教学视频,价值巨大。欢迎点击下方链接查看。

android教程视频
优质IT资源分享社区(www.itziyuan.top)
一个免费,自由,开放,共享,平等,互助的优质IT资源分享网站。
专注免费分享各大IT培训机构最新培训教学视频,为你的IT学习助力!

!!!回帖受限制请看点击这里!!!
!!!资源失效请在此版块发帖说明!!!

[PS:按 CTRL+D收藏本站网址~]

——“优质IT资源分享社区”管理员专用签名~

本版相似帖子

游客