在android当中对于UI体系当中往往我们会在绘制UI的时候碰到各种各样的问题而不知道从何解决, 也有时需要开发更改自定义组件时,需要做自己的调整,或者是实现某个自定义特效时的思路不明确,因此了解UI绘制流程及原理是十分必要的,本文就UI绘制流程之前的相关知识进行简单的分析和梳理,便于后续进一步了解UI绘制原理 要弄清楚UI绘制流程和原理,我们首先要了解的就是View是如何被添加到屏幕窗口上的。带着这个问题我们来进行源码分析,关于界面的展示,立马浮现在脑海的就是这样一段代码: 通过传入布局资源ID,setContentView方法又做了什么事情呢?经过一系列线索最终找到了的位置也就是Window的唯一实现类PhoneWindow: 注释1处installDecor方法顾名思义就是初始化操作,注释2处就是将布局资源ID填充到mContentParent内容布局容器。先进入installDecor方法: 注释1处,如果mDecor为空,就调用generateDecor方法,进入该方法就发现通过返回一个new出来DecorView,然后赋值给mDecor。注释2处调用generateLayout方法,那么该方法是如何给mContentParent赋值的呢? 省去了很多类似的特性设置代码,在注释1处我们发现根据系统属性的不同,通过requestFeature和setFlag方法设置了许多属性。在注释2处,看到解析窗口decor的提示,继续往下看,如注释3处,会根据不同的特性对布局资源进行不同的赋值,即后续加载不同的布局(就是不同的ActionBar,TitleBar之类的)。这就是为什么我们自己要去getWindow.requestFeature时必须在 setContent之前的原因。再看注释4处的onResourcesLoaded方法: 主要逻辑就是将传入layoutResource即布局资源通过addView添加到DecorView中。在回到注释5处,通过findViewById,获取id为com.android.internal.R.id.content的contentView,即内容布局容器,最后返回。分析完installDecor方法,再回到PhoneWindow的setContentView方法的注释2处,调用inflate方法,就是将MainActivity的layoutResID即对应的资源布局,添加到mContentParent内容布局容器。至此setContentView的分析就告一段落。 方法内部逻辑比较多,主要做了以下几件事: installDecor方法内部的generateDecor方法初始化DecorView installDecor方法内部的generateLayout 根据不同的系统属性,通过requestFeature和setFlag方法设置不同(feature)特性 根据不同的feature,通过onResourcesLoaded方法的addView加载不同的layoutResource(布局资源,一般是ActionBar,Title等) 通过findViewById获取固定id为com.android.internal.R.id.content的内容布局容器contentParent 返回contentParent setContentView方法内通过inflate方法将初始的layoutResID对于的布局添加到contentParent布局容器 谈到View的绘制入口,就需要知晓Activity的启动过程,如果还不太清楚可以查阅下面两篇文章了解相关细节 在handleLaunchActivity方法中调用performLaunchActivity后续会调用Activity的onCreate方法,在performLaunch之后会调用handleResumeActivity方法,顾名思义就知道它会是onResume方法的入口,走进该方法: 在注释2处调用window的getDecorView方法,最终还是调用PhoneWindow的相关方法获取DecorView,在注释3处调用Activity的getWindowManager方法获取ViewManager,在注释4处获取窗口的布局属性对象,在注释5处调用WindowManager的addView方法,进入Activity的getWindowManger方法: 在Activity中搜索mWindowManager赋值的逻辑: 接着进入Window中查找mWindowManager赋值的地方 接着进入createLocalWindowManager方法,来到了WindowManagerImpl 即WindowManager的实现类: 进入WindowManagerImpl的addView方法: 接着进入mGlobal即WindowManagerGlobal的addView方法: 在注释1处,实例化了一个ViewRootImpl,在注释2处,设置布局参数,添加到相关集合,在注释3处通过ViewRootImpl的setView方法将View和布局参数等进行了关联,进入setView方法: 需要关心的代码就这一句requestLayout,我们知道该方法会触发View的绘制流程,进入该方法: 进入scheduleTravels方法: 注释1处方法参数mTraversalRunnable是一个Runnable,进入查看它的run方法: 继续追踪进入doTraversal方法 进入performTraversals方法, 正式进入View绘制的三大流程 绘制入口的简单小结 ViewRootImpl是连接WindowManager和DecorView的纽带,View绘制的三大流程均是通过它来完成的,在ActivityThread中,当Activity对象被创建完毕后,会将DecorView添加到Window中,同时会创建ViewRootImple对象,并将ViewRootImpl和DecorView建立关联。View的具体绘制从ViewRootImpl的performTraversals方法开始的,它经过measure、layout和draw三个过程才能最终将一个View绘制出来,其中measure用来测量View的宽和高,layout用来确定View在父容器中的放置位置,而draw则负责将View绘制在屏幕上。
1.前言
2.View是如何添加到屏幕窗口上的
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
public class PhoneWindow extends Window implements MenuBuilder.Callback { ... // This is the top-level view of the window, containing the window decor. // 这是在窗口当中的顶层View,包含窗口的decor private DecorView mDecor; // This is the view in which the window contents are placed. It is either // mDecor itself, or a child of mDecor where the contents go. // 这是窗口内容放置的视图,它要么是mDecor本身,要么是mDecor的子类的内容 ViewGroup mContentParent; ... @Override public void setContentView(int layoutResID) { if (mContentParent == null) { // 注释1 installDecor(); } ... // 注释2 mLayoutInflater.inflate(layoutResID, mContentParent); ... } }
private void installDecor() { mForceDecorInstall = false; if (mDecor == null) { // 注释1 mDecor = generateDecor(-1); ... } if (mContentParent == null) { // 注释2 mContentParent = generateLayout(mDecor); ... } }
protected ViewGroup generateLayout(DecorView decor) { ... // 注释1 // 根据系统主题的属性设置了许多了特性 if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) { requestFeature(FEATURE_ACTION_BAR_OVERLAY); } if (a.getBoolean(R.styleable.Window_windowActionModeOverlay, false)) { requestFeature(FEATURE_ACTION_MODE_OVERLAY); } if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) { requestFeature(FEATURE_SWIPE_TO_DISMISS); } if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) { setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags())); } ... // 注释2 // Inflate the window decor. int layoutResource;// 布局资源id int features = getLocalFeatures(); if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) { // 注释3 // 根据不同feature, 对layoutResource进行不同的赋值操作 // 即后续加载不同的布局,这就很好的解释了为什么我们自己要去getWindow.requestFeature时必须在 // setContent之前的原因 layoutResource = R.layout.screen_swipe_dismiss; setCloseOnSwipeEnabled(true); } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) { ... } mDecor.startChanging(); // 注释4 mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); // 注释5 // ID_ANDROID_CONTENT = com.android.internal.R.id.content; ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); if (contentParent == null) { throw new RuntimeException("Window couldn't find content container view"); } ... return contentParent; }
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) { ... addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); ... mContentRoot = (ViewGroup) root; initializeElevation(); }
总结一下,View是如何添加到屏幕窗口上的,主要分为三个步骤:
3.View的绘制流程
3.1绘制入口
Activity的启动流程分析与总结
Application创建流程分析
受篇幅所限,就不具体分析了。就Activity启动过程的部分与View绘制相关的流程进行简单的梳理,如下图final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) { ActivityClientRecord r = mActivities.get(token); ... // 注释1 // 回调Activity的生命周期方法onResume r = performResumeActivity(token, clearHide, reason); if (r != null) { final Activity a = r.activity; boolean willBeVisible = !a.mStartedActivity; if (!willBeVisible) { try { willBeVisible = ActivityManager.getService().willActivityBeVisible( a.getActivityToken()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); // 注释2 View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); // 注释3 // 调用Activity的getWindowManager获取wm ViewManager wm = a.getWindowManager(); // 注释4 // 获取窗口的布局属性对象 WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (r.mPreserveWindow) { a.mWindowAdded = true; r.mPreserveWindow = false; ViewRootImpl impl = decor.getViewRootImpl(); if (impl != null) { impl.notifyChildRebuilt(); } } if (a.mVisibleFromClient) { if (!a.mWindowAdded) { a.mWindowAdded = true; // 注释5 wm.addView(decor, l); } else { a.onWindowAttributesChanged(l); } } ... }
public WindowManager getWindowManager() { return mWindowManager; }
final void attach(Context context, ActivityThread aThread, ... mWindowManager = mWindow.getWindowManager(); ... }
public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) { ... mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this); }
public WindowManagerImpl createLocalWindowManager(Window parentWindow) { return new WindowManagerImpl(mContext, parentWindow); }
@Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); }
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ... ViewRootImpl root; ... // 注释1 root = new ViewRootImpl(view.getContext(), display); // 注释2 view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams); // do this last because it fires off messages to start doing things try { // 注释3 root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { ... throw e; } } }
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { ... // Schedule the first layout -before- adding to the window // manager, to make sure we do the relayout before receiving // any other events from the system. requestLayout(); ... }
@Override public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } }
@UnsupportedAppUsage void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); // 注释1 mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } }
final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } }
void doTraversal() { ... performTraversals(); ... }
private void performTraversals() { ... // 执行测量 performMeasure(xxx) ... // 执行布局 performLayout(xxx); ... // 执行绘制 performDraw(); ... }
3.2绘制涉及的类及方法
3.3绘制三大步骤
结语
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算