`
qinweiping
  • 浏览: 128325 次
  • 性别: Icon_minigender_1
  • 来自: 嘉兴
社区版块
存档分类
最新评论

深入理解Android View(1)

阅读更多
    做android其实也有一段时间了,我们每个人都会碰到一些这样或那样的问题,碰到问题了就拼命百度,可是发现,我们解决问题的能力并没有提升很多,所以我才有想总结一下我项目中所用过的相关知识,并了解一下Android源代码中是如何定义这些属性的,如何去实现的。以后再碰到类似的问题,我该如何实现。本人也不常写博客,希望各位博友能指点,分享,并提出博客中不正确的地方,共勉!
   首先我发一份我做的关于Android View深入实现的的XMind的思维导图,可以帮助我一起整理思路,若是博友有什么想到的地方可以帮我一起补充。



    今天首先来介绍下View,View字面意思理解就是视图,什么是视图,说白了UI,你在屏幕上看到的,呈现在你眼前的东西都是属于视图。但是有个问题:我们在做刷新视图的时候,要用Handler机制来对UI组件进行相应的刷新,为什么不能直接在UI线程中做相关的刷新呢?若是在做一些比较费时的操作,比如读取数据库,网络访问,这个时候我们基本都会在UI线程中开一个子线程,然后利用Handler机制去子线程中取相应的数据,利用消息通知机制来UI主线程中刷新数据。我的理解是:其实UI,View,这些可以看得到的组件在运行的过程中,首先他要创建相关的UI的属性,比如要显示的图形的坐标点,大小,长宽,然后有一个画笔和画布,画上去的。就是说,电脑屏幕上你所有看到的东西都是画上去的。但是你发现,一旦断电,屏幕上的东西立马就不见了,是的!屏幕其实一直在不断的重绘,主要是利用了人眼的视觉暂留效果,两次重绘在0.05秒-0.2秒的时候,人不会发现发现屏幕在闪烁!计算机里有一个叫UI线程,其实他只做绘画,只管不停地在屏幕上不断画画,而且要再那么短的时间不断画,他是一个死循环,他很繁忙,还有一点,比如你操作数据库的数据,通过网络访问数据这些都是比较慢的操作,内存取数据>文件取数据>网络访问请求取数据。这个时候若是在UI主线程中去做非时操作,UI就阻塞了。那这个时候需要开辟一个子线程,他来做数据相关的操作,Handler的作用就好像是UI线程和子线程之间的信使,UI主线程这个时候发出一个通知:郑打理朝政繁忙,哪个子民可以为我平息边疆的战争,这个时候,主线程不是直接把这个话通知到下面的所有臣子,而是用了个中间的对象,就好比皇帝身边的人,先告诉他:最近要平息边疆战争,你给我告诉传达到每一个下面的臣子,务必传达到!好了,于是Handler就忙起来了,他忙着通知,接受大臣的反馈,大臣接受到这个间接的通知之后,带兵大战,驰骋沙场。那老皇帝等的着急吧,战争胜利了还是失败了呢?还是通过Handler把子线程运行的结果告诉Handler,Handler把结果拿给UI线程,一做刷新就完美呈现了!这个过程有什么好处呢,就是保证每个事物,每个人都分工很明确,各干各的事情。UI线程只做UI组件的创建,渲染,重绘,而Handler只管理UI线程和子线程之间的交互和反馈,子线程只管做自己擅长的具体事情就好了,他唯一要做的是把运行的结果告诉Handler,Handler再拿着结果反馈给UI线程,UI再拿着这个结果去刷新屏幕。
      其实不光光是UI绘制这个过程是这样的,比如:我们在点击一个Button的时候,会给他注册一个setOnClickListener(),但是UI组件自己是不知道自己干什么的,UI是不知道自己是被点击了,还是被滑动了,他只管自己绘画,而真正的处理这个事情的过程是由另一个接口来处理的,相应的接口做相应的处理,处理的结果再由回调来响应。
      说了那么多,都只是刚开始,其实这是一种很好的思想,叫做委托,比如你因为各种原因,没时间,没有资源,我委托一个中间的代理来帮我找能帮我做相应事情的人,然后这个代理再把找到的结果反馈给你。你要租房子,但是找不到很多的房源,那就要通过第三方比如中介来做这个事情了。
    在说View之前,我提出了很多问题:我的学习方法是带着问题从源代码中去找答案。这样才能知其然而知其所以然,然后用理论再在结合实际的一些小例子
   1)首先一个View要绘制之前,肯定要定义相应的属性和方法,比如坐标点,长,宽,高,绘制的方式,绘制的风格,这些是哪里定义的呢?
(2)View是如何被创建出来的呢,绘图绘制的机制是如何的?是如何把相应的属性结合起来呈现视图效果的?
(3)一个View他要占地方,他的位置如何确定的。他的大小如何确定的?
(4)一个View画出来之后,其实就像水氢原子+氧原子+电产生的,可是产生了之后用什么来存放呢?
(5)就一个图形放在那里,我们不知道他如何和外部交互啊?UI叫做UserInterface,用户接口,既然是给用户用的,那我怎么和计算机交互呢?怎么样赋予它生命呢,和外界感知?
(6)如何用这些知识在项目中实战
总的来说,分成三个方面,(1)UI的外部形态?(2)谁来产生UI,谁来去创造它?(3)事件机制,能被外界感知到
  先来说第一个问题,在说道UI的外部形态的时候,有三个决定他外部形态的函数
-> onDraw()方法
-> onMeasure()方法
-> onLayout()方法
从字面上来 onDraw()和onLayout()很好理解 一个是用来绘制的,一个是用来确定他的位置的,但是这个onMeasure()我查了下英文字典,measure是测量,尺寸的意思,就是测量UI组件的大小的。
还是先来看onDraw()的源代码
咋一看
 
  /**
     * Implement this to do your drawing.
     *
     * @param canvas the canvas on which the background will be drawn
     */
    protected void onDraw(Canvas canvas) {
 
    }

其中什么代码也没有?是不是很奇怪,这个时候就应该想是哪个地方调用这个函数的。可以在AndroidStudio下按下ctrl+g
你可以看到有一个方法里调用了onDraw()方法,这个方法就是draw()方法。
首先来看一下英文的解释:注意其中的2点,draw()是通过Canvas来实现的(2)当这个方法draw()方法被调用的时候,这个View其实已经生成了一个完整的布局了,然后后面说的,在编程的时候如果你要在一个子类中需要调用重写该方法,要使用onDraw()方法来替代这个draw()方法
这里要注意2点,第一,View的相关绘制追踪到再底层是由Canvas绘制的,Canvas是画布,可以在画布上绘制各种雏形,然后可以通过跟踪代码的方式按F3,其实在绘制View的时候会首先绘制一个矩形这样的雏形。第二,我有问我网友,我为什么不能重写draw(),若是重写父类的draw()方法我要重写这个onDraw()方法呢,他给我的解释是这样的:
draw()方法是系统的方法,是一个framework层的方法,onDraw()方法是用户的方法,是给用户间接调用实现特定用户需求的方法的
如果要重写draw()方法的话就变成了
 public void draw(){
   //framework code
   //custom code
   //framework code
}

但是若是采用重写onDraw()方法的话就变成了
public void draw(){
   //framework code
   onDraw(); 
   //framework code

   其实后面要说到的layout()方法,官方中也是这么解释的,要你使用onLayout()方法来重写,而不是调用layout()方法。这个就是所谓的钩子函数!不知道说的准不准确,欢迎博友一起纠正!继续往下读代码,你可以知道要绘制一个View 需要6个步骤:我简要总结一下,
1。绘制背景。
2.绘制View或者该View存在的子View。
3.还要对View加上相应的滚动条,比如有些View ScrollView就是带有滚动条的,还有有些列表也是带有的。

/**
     * Manually render this view (and all of its children) to the given Canvas.
     * The view must have already done a full layout before this function is
     * called.  When implementing a view, implement
     * {@link #onDraw(android.graphics.Canvas)} instead of overriding this method.
     * If you do need to override this method, call the superclass version.
     *
     * @param canvas The Canvas to which the View is rendered.
     */
    public void draw(Canvas canvas) {
        final int privateFlags = mPrivateFlags;
        final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
        mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

        /*
         * Draw traversal performs several drawing steps which must be executed
         * in the appropriate order:
         *
         *      1. Draw the background
         *      2. If necessary, save the canvas' layers to prepare for fading
         *      3. Draw view's content
         *      4. Draw children
         *      5. If necessary, draw the fading edges and restore layers
         *      6. Draw decorations (scrollbars for instance)
         */

        // Step 1, draw the background, if needed
        int saveCount;

        if (!dirtyOpaque) {
            drawBackground(canvas);
        }

        // skip step 2 & 5 if possible (common case)
        final int viewFlags = mViewFlags;
        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
        if (!verticalEdges && !horizontalEdges) {
            // Step 3, draw the content
            if (!dirtyOpaque) onDraw(canvas);

            // Step 4, draw the children
            dispatchDraw(canvas);

            // Step 6, draw decorations (scrollbars)
            onDrawScrollBars(canvas);

            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }

            // we're done...
            return;
        }

        /*
         * Here we do the full fledged routine...
         * (this is an uncommon case where speed matters less,
         * this is why we repeat some of the tests that have been
         * done above)
         */

        boolean drawTop = false;
        boolean drawBottom = false;
        boolean drawLeft = false;
        boolean drawRight = false;

        float topFadeStrength = 0.0f;
        float bottomFadeStrength = 0.0f;
        float leftFadeStrength = 0.0f;
        float rightFadeStrength = 0.0f;

        // Step 2, save the canvas' layers
        int paddingLeft = mPaddingLeft;

        final boolean offsetRequired = isPaddingOffsetRequired();
        if (offsetRequired) {
            paddingLeft += getLeftPaddingOffset();
        }

        int left = mScrollX + paddingLeft;
        int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
        int top = mScrollY + getFadeTop(offsetRequired);
        int bottom = top + getFadeHeight(offsetRequired);

        if (offsetRequired) {
            right += getRightPaddingOffset();
            bottom += getBottomPaddingOffset();
        }

        final ScrollabilityCache scrollabilityCache = mScrollCache;
        final float fadeHeight = scrollabilityCache.fadingEdgeLength;
        int length = (int) fadeHeight;

        // clip the fade length if top and bottom fades overlap
        // overlapping fades produce odd-looking artifacts
        if (verticalEdges && (top + length > bottom - length)) {
            length = (bottom - top) / 2;
        }

        // also clip horizontal fades if necessary
        if (horizontalEdges && (left + length > right - length)) {
            length = (right - left) / 2;
        }

        if (verticalEdges) {
            topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
            drawTop = topFadeStrength * fadeHeight > 1.0f;
            bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
            drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
        }

        if (horizontalEdges) {
            leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
            drawLeft = leftFadeStrength * fadeHeight > 1.0f;
            rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
            drawRight = rightFadeStrength * fadeHeight > 1.0f;
        }

        saveCount = canvas.getSaveCount();

        int solidColor = getSolidColor();
        if (solidColor == 0) {
            final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;

            if (drawTop) {
                canvas.saveLayer(left, top, right, top + length, null, flags);
            }

            if (drawBottom) {
                canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
            }

            if (drawLeft) {
                canvas.saveLayer(left, top, left + length, bottom, null, flags);
            }

            if (drawRight) {
                canvas.saveLayer(right - length, top, right, bottom, null, flags);
            }
        } else {
            scrollabilityCache.setFadeColor(solidColor);
        }

        // Step 3, draw the content
        if (!dirtyOpaque) onDraw(canvas);

        // Step 4, draw the children
        dispatchDraw(canvas);

        // Step 5, draw the fade effect and restore layers
        final Paint p = scrollabilityCache.paint;
        final Matrix matrix = scrollabilityCache.matrix;
        final Shader fade = scrollabilityCache.shader;

        if (drawTop) {
            matrix.setScale(1, fadeHeight * topFadeStrength);
            matrix.postTranslate(left, top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(left, top, right, top + length, p);
        }

        if (drawBottom) {
            matrix.setScale(1, fadeHeight * bottomFadeStrength);
            matrix.postRotate(180);
            matrix.postTranslate(left, bottom);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(left, bottom - length, right, bottom, p);
        }

        if (drawLeft) {
            matrix.setScale(1, fadeHeight * leftFadeStrength);
            matrix.postRotate(-90);
            matrix.postTranslate(left, top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(left, top, left + length, bottom, p);
        }

        if (drawRight) {
            matrix.setScale(1, fadeHeight * rightFadeStrength);
            matrix.postRotate(90);
            matrix.postTranslate(right, top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(right - length, top, right, bottom, p);
        }

        canvas.restoreToCount(saveCount);

        // Step 6, draw decorations (scrollbars)
        onDrawScrollBars(canvas);

        if (mOverlay != null && !mOverlay.isEmpty()) {
            mOverlay.getOverlayView().dispatchDraw(canvas);
        }
    }

     那倒这里我又有一个疑惑了,为什么有些View是带有滚动条的呢,而有些View就是没有呢,比如我在项目中用一个ListView,若是ListView中有很多很多条数据,加入数据超过了相应的显示长度,这个时候我们是不是要在ListView里加一个滚动条组件,这样就可以允许用户通过滚动条的滑动来查看下面的数据,但是:有些组件,比如我要上传自己的一个照片到服务器端,但是照片太大了 ,我要进行相应的裁剪,这个时候View其实是做了一个裁剪的操作,就是说:每个不同的View其实根据实际的应用需求来对View做相应的处理。那具体是根据什么方式来划分的呢?别着急,这些源代码里都有下面有一个方法确定View大小的方法 onMeasure()里就有涉及到这方面的处理!
     当然draw()方法还有一个重载的方法 ,这个英文的描述:
这个方法是被ViewGroup.drawChild()方法来调用的,用来绘制依次在ViewGroup里的childView。这样一说其实也就明白了,原来Viewgroup就是一个View的容器,他是用来装View的地方的。那可以联想到我们常用的LinearLayout,RelativeLayout布局容器,他们其实也不就是ViewGroup嘛,在写XML的时候他们是作为根节点的,他们下面有很多子节点作为父容器的孩子节点。
再来看一下确定View位置的相关方法Layout()方法,为什么把这2个方法放在一起说呢,就是因为刚才说到一点,layout()方法和draw()方法一样,若是子类要重写父类这个方法的时候,也是要调用onLayout()方法来重写,所以他的代码结构其实和draw()方法是一样的。
public void layout(int l, int t, int r, int b) {
        if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
            onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
            mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
        }

        int oldL = mLeft;
        int oldT = mTop;
        int oldB = mBottom;
        int oldR = mRight;

        boolean changed = isLayoutModeOptical(mParent) ?
                setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

        if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
            onLayout(changed, l, t, r, b);
            mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;

            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnLayoutChangeListeners != null) {
                ArrayList<OnLayoutChangeListener> listenersCopy =
                        (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
                int numListeners = listenersCopy.size();
                for (int i = 0; i < numListeners; ++i) {
                    listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
                }
            }
        }

        mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
        mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
    }
在这个方法里要分三段来看,(1)setFrame() 要设置改组件距离左边,上边,底部和右边的距离。这几个值构成的矩形的区域就是该View显示的位置,不过这里的具体位置都是相对父容器的(2)此处有一个判断位置是否改变的返回boolean值的函数,若是位置改变,则进行相应的重绘。然后回调onLayout函数,最后回调所有注册过的listener的onLayoutChange函数。对于View来说,onLayout只是一个空实现,一般情况下我们也不需要重载该函数:
然后在看代码的时候我发现一个细节要特别注意,setFrame()有一个这个方法中notifySubtreeAccessibilityStateChangedIfNeeded()
该方法我单从字面意思上去理解就是说,这个方法是父容器通知子容器的有效重绘的通知,为了不必要的系统开销而建立的通知机制。
然后按F3跟踪进去 
**
     * Notifies that the accessibility state of this view changed. The change
     * is *not* local to this view and does represent structural changes such
     * as children and parent. For example, the view size changed. The
     * notification is at at most once every
     * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}
     * to avoid unnecessary load to the system. Also once a view has a pending
     * notification this method is a NOP until the notification has been sent.
     *
     * @hide
     */
    public void notifySubtreeAccessibilityStateChangedIfNeeded() {
        if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
            return;
        }
        if ((mPrivateFlags2 & PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED) == 0) {
            mPrivateFlags2 |= PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED;
            if (mParent != null) {
                try {
                    mParent.notifySubtreeAccessibilityStateChanged(
                            this, this, AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
                } catch (AbstractMethodError e) {
                    Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
                            " does not fully implement ViewParent", e);
                }
            }
        }
    }

三,再来看view中非常重要的方法 onMeasure()方法。这个方法从字面意思上来猜,就是测量View的长度的方法
其实有没有观察到这个细节:注释里面有用html标签<p></p>这个也很帮助你阅读代码,<p></p>就是划分段落,要理解这个代码,一个段落代表一个意思:首先是整体概括:这个方法被他的子类所重写,目的是为子类的内容来提供高效,准确的测量。
<1> 当内容被重写的时候,你必须调用setMeasuredDimension(int,int)去存储测量measure的宽度和高度的值,如果该操作失败了,就会抛出一个IllegalStateException,但是有一个前提:要确保父类的onMeasure(int,int)调用是一个有效的调用。所以第一步做的操作其实很简单,就是由View的子类通过调用父类的setMeasuredDimension(int,int)方法,来存储当前View的宽度和高度值,若是没有存储成功,那就会抛出相应的异常,知道了这个原理了之后,以后碰到这个IllegalStateException再也不会恐慌了!
<2> 除非是有更大的尺寸允许,否则这个父类会实现测量一个默认的大小值。当父类容器的默认尺寸容纳不下子View的时候,那会重新调用onMeasure()进行重绘。
<3> 一但width和height计算好了,就应该调用View.setMeasuredDimension(int width,int height)方法,否则将导致抛出异常.
然后这三步步骤挑一个我看来比较重要的,就是第二步。
测量需要2个关键的东西:大小(size)和模式(mode)
measureSpec,size,mode他们三个的关系,都封装在View类中的一个内部类里,名叫MeasureSpec
此处有一个静态的MeasureSpec的内部类。
/**
* MeasureSpec封装了父布局传递给子布局的布局要求,每个MeasureSpec代表了一组宽度和高度的要求
* MeasureSpec由size和mode组成。
* 三种Mode:
* 1.UNSPECIFIED
* 父没有对子施加任何约束,子可以是任意大小(也就是未指定)
* (UNSPECIFIED在源码中的处理和EXACTLY一样。当View的宽高值设置为0的时候或者没有设置宽高时,模式为UNSPECIFIED
* 2.EXACTLY
* 父决定子的确切大小,子被限定在给定的边界里,忽略本身想要的大小。
* (当设置width或height为match_parent时,模式为EXACTLY,因为子view会占据剩余容器的空间,所以它大小是确定的)
* 3.AT_MOST
* 子最大可以达到的指定大小
* (当设置为wrap_content时,模式为AT_MOST, 表示子view的大小最多是多少,这样子view会根据这个上限来设置自己的尺寸)

* MeasureSpecs使用了二进制去减少对象的分配。
*/ 
通过查看其中的源代码可以很清楚的知道,原来,他们设置了三种规定如何处理View大小边界的模式,
<1>当没有超过默认限制的大小,那就按该多大就多大处理
<2>超过默认限制的大小里又分了2种情况,一种是超过默认大小直接给裁剪掉
<3>第二种是超过默认大小的时候,会加入滚动条这样的属性。
/**
     * <p>
     * Measure the view and its content to determine the measured width and the
     * measured height. This method is invoked by {@link #measure(int, int)} and
     * should be overriden by subclasses to provide accurate and efficient
     * measurement of their contents.
     * </p>
     *
     * <p>
     * <strong>CONTRACT:</strong> When overriding this method, you
     * <em>must</em> call {@link #setMeasuredDimension(int, int)} to store the
     * measured width and height of this view. Failure to do so will trigger an
     * <code>IllegalStateException</code>, thrown by
     * {@link #measure(int, int)}. Calling the superclass'
     * {@link #onMeasure(int, int)} is a valid use.
     * </p>
     *
     * <p>
     * The base class implementation of measure defaults to the background size,
     * unless a larger size is allowed by the MeasureSpec. Subclasses should
     * override {@link #onMeasure(int, int)} to provide better measurements of
     * their content.
     * </p>
     *
     * <p>
     * If this method is overridden, it is the subclass's responsibility to make
     * sure the measured height and width are at least the view's minimum height
     * and width ({@link #getSuggestedMinimumHeight()} and
     * {@link #getSuggestedMinimumWidth()}).
     * </p>
     *
     * @param widthMeasureSpec horizontal space requirements as imposed by the parent.
     *                         The requirements are encoded with
     *                         {@link android.view.View.MeasureSpec}.
     * @param heightMeasureSpec vertical space requirements as imposed by the parent.
     *                         The requirements are encoded with
     *                         {@link android.view.View.MeasureSpec}.
     *
     * @see #getMeasuredWidth()
     * @see #getMeasuredHeight()
     * @see #setMeasuredDimension(int, int)
     * @see #getSuggestedMinimumHeight()
     * @see #getSuggestedMinimumWidth()
     * @see android.view.View.MeasureSpec#getMode(int)
     * @see android.view.View.MeasureSpec#getSize(int)
     */
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }

谈到这里算是结束了最基本的View的三个比较重要的方法的阐述。

然后,我这里还会提出一个问题:
你在创建对象的时候,都会用该对象的构造方法。我们每次都是通过构造方法来实现对该对象的内存分配,属性赋值等相关操作,那View的构造方法上有什么可以值得推敲的地方呢,这个时候促使我想起,View是怎么来的,如何被创建的。
先小看一下View的三个构造方法:
1.public void CustomView(Context context) {}
2.public void CustomView(Context context, AttributeSet attrs) {}
3.public void CustomView(Context context, AttributeSet attrs, int defStyle) {}
那这个时候我们需要好好的问问自己:为什么要定义三个构造方法呢,2个不够用嘛?然后再猜想一下这个AttributeSet和 defStyle是用来干嘛的?从名字看是关于View的设计风格的文件的,那他又是如何加载这些设计风格的文件呢?程序员去编程的时候如何使用这些用户接口呢?将再下一块涉及到这些问题。

  • 大小: 279.5 KB
分享到:
评论

相关推荐

    深入理解Android卷1全

    深入理解Android 卷1 不是扫描版的,是全版电子书的,非PDF,可编辑,各种阅览器以打开!包括书签和同步目录! 第1章 阅读前的准备工作 / 1 1.1 系统架构 / 2 1.1.1 Android系统架构 / 2 1.1.2 本书的架构 / 3 1.2 ...

    深入理解Android:卷I--详细书签版

     《深入理解android:卷1》是一本以情景方式对android的源代码进行深入分析的书。内容广泛,以对framework层的分析为主,兼顾native层和application层;分析深入,每一部分源代 码的分析都力求透彻;针对性强,...

    《深入理解Android》卷Ⅰ

    第2章 深入理解JNI 2.1 JNI概述 2.2 学习JNI的实例:MediaScanner 2.3 Java层的MediaScanner分析 2.3.1 加载JNI库 2.3.2 Java的native函数和总结 2.4 JNI层MediaScanner的分析 2.4.1 注册JNI函数 2.4.2 数据类型转换...

    深入理解Android中View绘制的三大流程

    主要给大家介绍了关于Android中View绘制的三大流程,View的工作流程主要是指measure、layout、draw这三大流程,即测量、布局和绘制,文中通过示例代码介绍的非常详细,对大家具有一定的参考学习价值,需要的朋友们...

    深入浅析Android坐标系统

    1 背景 去年有很多人私信告诉我让说说自定义控件,其实通观网络上的很多博客都...所谓Android自定义View那几大主要onXXX()方法的重写实质其实大多数都是在处理坐标逻辑运算,所以我们就先来就题重谈一下Android坐标系。

    android 实现FlowLayout 流线布局(自定义ViewGroup)

    [快速理解android View的测量onMeasure()与MeasureSpec](http://blog.csdn.net/double2hao/article/details/51553703) ###2、Layoutparams以及MarginLayoutParams [Android开发:LayoutParams的用法]...

    深入理解Android中Scroller的滚动原理

    View的平滑滚动效果 什么是实现View的平滑滚动效果呢,举个简单的例子,...1、创建一个Scroller对象,一般在View的构造器中创建: public ScrollViewGroup(Context context) { this(context, null); } public ScrollV

    疯狂Android讲义源代码2

    第4章 深入理解Activity 第5章 使用Intent和IntentFilter进行通信 第6章 Android应用的资源 第7章 图形与图像处理 第8章 Android的数据存储和IO 第9章 使用ContentProvider实现数据共享 第10章 Service与Broadcast ...

    深入理解Android中的Window和WindowManager

    Window表示一个窗口的概念,Window是...Android中,所有的视图都是通过Window来呈现,不管是Activity、Dialog、还是Toast,它们的视图实际上都是附加在Window上,因此Window是实际View的直接管理者,单击事件由Window传

    Android_layout.rar_AbsoluteLayout _android layout_android tabhos

    在继续深入Android开发之旅之前,有必要解决前两篇中没有介绍的遗留问题:View的几种布局显示方法,以后就不会在针对布局方面做过多的介绍。View的布局显示方式有下面几种:线性布局(Linear Layout)、相对布局...

    从源码解析Android中View的容器ViewGroup

    希望有志同道合的朋友一起来探讨,深入Android内部,深入理解Android。 一、ViewGroup是什么?  一个ViewGroup是一个可以包含子View的容器,是布局文件和View容器的基类。在这个类里定义了ViewGroup.LayoutParams类...

    Android开发艺术探索

    第7章 Android动画深入分析 / 265 7.1 View动画 / 265 7.1.1 View动画的种类 / 265 7.1.2 自定义View动画 / 270 7.1.3 帧动画 / 272 7.2 View动画的特殊使用场景 / 273 7.2.1 LayoutAnimation / 273 ...

    Android开发艺术探索.任玉刚(带详细书签).pdf

    第二,结合Android源代码和应用层开发过程,融会贯通,介绍一些比较深入的知识点;第三,介绍一些核心技术和Android的性能优化思想。 第1章 Activity的生命周期和启动模式 1 1.1 Activity的生命周期全面分析 1 ...

    Android高级编程--源代码

    1.9.2 理解Android软件栈 12 1.9.3 Dalvik虚拟机 13 1.9.4 Android应用程序架构 14 1.9.5 Android库 14 1.9.6 高级Android库 15 1.10 小结 16 第2章 开始入手 17 2.1 Android开发 18 2.1.1 开始前的准备工作...

    Android高级编程 part1

     1.9.2 理解Android软件栈  1.9.3 Dalvik虚拟机  1.9.4 Android应用程序架构  1.9.5 Android库  1.9.6 高级Android库  1.10 小结  第2章 开始入手  2.1 Android开发  2.1.1 开始前的准备工作  2.1.2 创建...

    android的拖动效果

    在继续深入Android开发之旅之前,有必要解决前两篇中没有介绍的遗留问题:View的几种布局显示方法,以后就不会在针对布局方面做过多的介绍。View的布局显示方式有下面几种:线性布局(Linear Layout)、相对布局...

    《Android高级编程》

    1.7 运行Android的环境 1.8 从事Android开发的原因 1.8.1 推动Android普及的因素 1.8.2 Android的独到之处 1.8.3 改变移动开发格局 1.9 开发框架简介 1.9.1 开发包中的资源 1.9.2 理解Android软件栈 1.9.3 Dalvik...

    Android 开发之旅:详解view的几种布局方式及实践

    在继续深入Android开发之旅之前,有必要解决前两篇中没有介绍的遗留问题:View的几种布局显示方法,以后就不会在针对布局方面做过多的介绍。View的布局显示方式有下面几种:线性布局(Linear Layout)、相对布局...

Global site tag (gtag.js) - Google Analytics