View加载详解(一)
来源:互联网 发布:植物学专业知乎 编辑:程序博客网 时间:2024/06/02 00:00
现在我们接着上一篇文章继续往下讲layoutInflat.inflater
那么inflate方法里面具体做了什么?跟踪代码,该方法的实现是在LayoutInflater类中。
public View inflate(int resource, ViewGroup root) { return inflate(resource, root, root != null); }
该方法很简单,方法体里面直接调用 如下方法
public View inflate(int resource, ViewGroup root, boolean attachToRoot) { final Resources res = getContext().getResources(); if (DEBUG) { Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" (" + Integer.toHexString(resource) + ")"); } final XmlResourceParser parser = res.getLayout(resource); try { return inflate(parser, root, attachToRoot); } finally { parser.close(); } }
调用XML的pull解析器将xml资源解析成XmlResourceParser对象作为参数传 inflate(parser, root, attachToRoot);方法。该方法实现如下:
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) { synchronized (mConstructorArgs) { final AttributeSet attrs = Xml.asAttributeSet(parser); Context lastContext = (Context)mConstructorArgs[0]; mConstructorArgs[0] = mContext; View result = root; try { // Look for the root node. int type; while ((type = parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) { // Empty } if (type != XmlPullParser.START_TAG) { throw new InflateException(parser.getPositionDescription() + ": No start tag found!"); } final String name = parser.getName(); if (TAG_MERGE.equals(name)) { if (root == null || !attachToRoot) { throw new InflateException("<merge /> can be used only with a valid " + "ViewGroup root and attachToRoot=true"); } rInflate(parser, root, attrs, false, false); } else { // Temp is the root view that was found in the xml final View temp = createViewFromTag(root, name, attrs, false); ViewGroup.LayoutParams params = null; if (root != null) { // Create layout params that match root, if supplied params = root.generateLayoutParams(attrs); if (!attachToRoot) { // Set the layout params for temp if we are not // attaching. (If we are, we use addView, below) temp.setLayoutParams(params); } } // Inflate all children under temp rInflate(parser, temp, attrs, true, true); // We are supposed to attach all the views we found (int temp) // to root. Do that now. if (root != null && attachToRoot) { root.addView(temp, params); } // Decide whether to return the root that was passed in or the // top view found in xml. if (root == null || !attachToRoot) { result = temp; } } } catch (XmlPullParserException e) { InflateException ex = new InflateException(e.getMessage()); ex.initCause(e); throw ex; } catch (IOException e) { InflateException ex = new InflateException( parser.getPositionDescription() + ": " + e.getMessage()); ex.initCause(e); throw ex; } finally { // Don't retain static reference on context. mConstructorArgs[0] = lastContext; mConstructorArgs[1] = null; } Trace.traceEnd(Trace.TRACE_TAG_VIEW); return result; } }
以上代码主要作用是根据xml资源的根节点来创建一个 root view 。我们来看看 rInflate方法的实现
void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs, boolean finishInflate, boolean inheritContext) throws XmlPullParserException, IOException { final int depth = parser.getDepth(); int type; while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { if (type != XmlPullParser.START_TAG) { continue; } final String name = parser.getName(); if (TAG_REQUEST_FOCUS.equals(name)) { parseRequestFocus(parser, parent); } else if (TAG_TAG.equals(name)) { parseViewTag(parser, parent, attrs); } else if (TAG_INCLUDE.equals(name)) { if (parser.getDepth() == 0) { throw new InflateException("<include /> cannot be the root element"); } parseInclude(parser, parent, attrs, inheritContext); } else if (TAG_MERGE.equals(name)) { throw new InflateException("<merge /> must be the root element"); } else { final View view = createViewFromTag(parent, name, attrs, inheritContext); final ViewGroup viewGroup = (ViewGroup) parent; final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs); rInflate(parser, view, attrs, true, true); viewGroup.addView(view, params); } } if (finishInflate) parent.onFinishInflate(); }
以上方法就是遍历xml资源根布局 root view 下的子元素,并且将子元素view依次添加到 root view下面。
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);相信大家也看到了这个generateLayoutParams(attrs),这个就是加载ViewGroup的属性的,当我们自定义ViewGroup的时候我们就要继承它。
LayoutInflater类给开发者暴露了两个方法用于加载布局
public View inflate(int resource, ViewGroup root)public View inflate(int resource, ViewGroup root, boolean attachToRoot)
当root=null时,attachToRoot不起任何作用。
当root!=null时,attachToRoot=false时,xml资源布局不添加到root根布局下,也就是root失效。
当root!=null,attachToRoot=true时,xml资源布局会添加到root根布局下。
在调用第一种方法,没有attachToRoot参数时,当root=null时的情况和第一种分析一样,当root!=null的情况和第三那种分析一样。
当大家读到这里的时候就知道Activity中的PhoneView对象帮我们创建了一个PhoneView内部类DecorView(父类为FrameLayout)窗口顶层视图,
然后通过LayoutInflater将xml内容布局解析成View树形结构添加到DecorView顶层视图中id为content的FrameLayout父容器上面。到此,我们已经知道Activity的content内容布局最终会添加到DecorView窗口顶层视图上面,相信很多人也会有这样的疑惑:窗口顶层视图DecorView是怎么绘制到我们的手机屏幕上的呢?
下面试着分析DecorView的绘制流程。(因为技术不是太6,出错的地方大家不要见怪)
顶层视图DecorView添加到窗口的过程
DecorView是怎么添加到窗口的呢?这时候我们不得不从Activity是怎么启动的说起,当Activity初始化 Window和将布局添加到PhoneWindow的内部类DecorView类之后,ActivityThread类会调用handleResumeActivity方法将顶层视图DecorView添加到PhoneWindow窗口,来看看handlerResumeActivity方法的实现:
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) { if (r.window == null && !a.mFinished && willBeVisible) { //获得当前Activity的PhoneWindow对象 r.window = r.activity.getWindow(); //获得当前phoneWindow内部类DecorView对象 View decor = r.window.getDecorView(); //设置窗口顶层视图DecorView可见度 decor.setVisibility(View.INVISIBLE); //得当当前Activity的WindowManagerImpl对象 ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (a.mVisibleFromClient) { //标记根布局DecorView已经添加到窗口 a.mWindowAdded = true; //将根布局DecorView添加到当前Activity的窗口上面 wm.addView(decor, l);
分析:详细步骤以上代码都有详细注释,这里就不一一解释。handlerResumeActivity()方法主要就是addView方法添加到Activity的顶层视图DecorView添加到窗口视图上。我们来看WindowManagerImpl类的addView()方法。
@Override public void addView(View view, ViewGroup.LayoutParams params) { mGlobal.addView(view, params, mDisplay, mParentWindow); }
源码很简单,直接调用了 mGlobal对象的addView()方法。继续跟踪,mGlobal对象是WindowManagerGlobal类。进入WindowManagerGlobal类看addView()方法。
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ............ ViewRootImpl root; View panelParentView = null; ............ //获得ViewRootImpl对象root root = new ViewRootImpl(view.getContext(), display); ........... // do this last because it fires off messages to start doing things try { //将传进来的参数DecorView设置到root中 root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { ........... } }
该方法中创建了一个ViewRootImpl对象root,然后调用ViewRootImpl类中的setView成员方法()。继续跟踪代码进入ViewRootImpl类分析
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { //将顶层视图DecorView赋值给全局的mView mView = view; ............. //标记已添加DecorView mAdded = true; ............. //请求布局 requestLayout(); } }
该方法实现有点长,我省略了其他代码,直接看以上几行代码:
将外部参数DecorView赋值给mView成员变量
标记DecorView已添加到ViewRootImpl
调用requestLayout方法请求布局
跟踪代码进入到 requestLayout()方法:
@Override public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } }void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); } }final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } }final TraversalRunnable mTraversalRunnable = new TraversalRunnable(); void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().removeSyncBarrier(mTraversalBarrier); try { performTraversals(); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } } }
跟踪代码,最后DecorView的绘制会进入到ViewRootImpl类中的performTraversals()成员方法,这个过程可以参考上面的代码流程图。现在我们主要来分析下 ViewRootImpl类中的performTraversals()方法。
private void performTraversals() { // cache mView since it is used so much below... //我们在Step3知道,mView就是DecorView根布局 final View host = mView; //在Step3 成员变量mAdded赋值为true,因此条件不成立 if (host == null || !mAdded) return; //是否正在遍历 mIsInTraversal = true; //是否马上绘制View mWillDrawSoon = true; ............. //顶层视图DecorView所需要窗口的宽度和高度 int desiredWindowWidth; int desiredWindowHeight; ..................... //在构造方法中mFirst已经设置为true,表示是否是第一次绘制DecorView if (mFirst) { mFullRedrawNeeded = true; mLayoutRequested = true; //如果窗口的类型是有状态栏的,那么顶层视图DecorView所需要窗口的宽度和高度就是除了状态栏 if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) { // NOTE -- system code, won't try to do compat mode. Point size = new Point(); mDisplay.getRealSize(size); desiredWindowWidth = size.x; desiredWindowHeight = size.y; } else {//否则顶层视图DecorView所需要窗口的宽度和高度就是整个屏幕的宽高 DisplayMetrics packageMetrics = mView.getContext().getResources().getDisplayMetrics(); desiredWindowWidth = packageMetrics.widthPixels; desiredWindowHeight = packageMetrics.heightPixels; } }............//获得view宽高的测量规格,mWidth和mHeight表示窗口的宽高,lp.widthhe和lp.height表示DecorView根布局宽和高 int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); // Ask host how big it wants to be //执行测量操作 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);........................//执行布局操作 performLayout(lp, desiredWindowWidth, desiredWindowHeight);.......................//执行绘制操作performDraw();}
该方法主要流程就体现了View绘制渲染的三个主要步骤,分别是测量,布局,绘制三个阶段。
这里先给出Android系统View的绘制流程:依次执行View类里面的如下三个方法:
measure(int ,int) :测量View的大小
layout(int ,int ,int ,int) :设置子View的位置
draw(Canvas) :绘制View内容到Canvas画布上。
由于篇幅的原因我会放在下一篇再讲谢谢大家
- View加载详解(一)
- view 详解 (一) 将view加载到窗口过程分析
- View加载详解(二)
- ViewStub(惰性加载View)详解
- View加载详解
- View (一)LayoutInflater()方法详解
- 【Android 动画】View Animation详解(一)
- 【Android 动画】View Animation详解(一)
- Android群英传--自定义View详解(一)
- Android view 详解(一)LayoutInflater
- View详解【一】
- 学习自定义View(一)实现进度条加载
- Android中view的加载机制(一)
- 自定义View(一): OnMeasure详解
- View工作原理(一)事件传递原理详解
- ViewDragHelper详解(一)- 可拖动的view
- ViewDragHelper详解(一)- 可拖动的view
- View工作原理(一)事件传递原理详解
- 【Python Oracle】使用cx_Oracle 连接oracle的简单介绍
- Spring实现AOP的4种方式
- #java读书笔记#集合框架1
- Android nice在进程与线程调度中的作用
- Caffe学习系列(5):其它常用层及参数
- View加载详解(一)
- C/C++一些难为人知的小细节
- 通过CAGradientLayer制作渐变色效果【原创】
- POI操作EXCEL--源自技术
- Virtual-Key Codes(虚拟键码)
- 二维数组面试题(外加创建动态数组)
- JQuery跨域Ajax
- Caffe学习系列(6):Blob,Layer and Net以及对应配置文件的编写
- CADisplayLink结合UIBezierPath的神奇妙用