AndroidUI的渲染 | 青训营笔记

114 阅读3分钟

这是我参与「第四届青训营 」笔记创作活动的的第6天

UI的渲染

1、布局的加载

在Android中大部分的UI界面都是以xml文件的形式编写,然后通过setContentView() 来对布局进行加载,让我们进入源码学习。

首先进入onCreate() 方法:

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

通过setContentView() 方法,进入AppCompatActivity类中的setContentView方法:

public class AppCompatActivity extends FragmentActivity implements AppCompatCallback,
       TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider {
           //.........
           @Override
           public void setContentView(@LayoutRes int layoutResID) {
               initViewTreeOwners();
               getDelegate().setContentView(layoutResID);
    }
           @NonNull
           public AppCompatDelegate getDelegate() {
           if (mDelegate == null) {
               mDelegate = AppCompatDelegate.create(this, this);
           }
           return mDelegate;
    }
}

通过AppCompatDelegate.create()方法进入AppCompatDelegate类,create()用来新建一个AppCompatDelegate类的实现类AppCompatDelegateImpl

public abstract class AppCompatDelegate {
 @NonNull
    public static AppCompatDelegate create(@NonNull Activity activity,
            @Nullable AppCompatCallback callback) {
        return new AppCompatDelegateImpl(activity, callback);
    }
}

然后进入AppCompatDelegateImpl的setContentView()方法

class AppCompatDelegateImpl extends AppCompatDelegate implements MenuBuilder.Callback, LayoutInflater.Factory2 {
      @Override
    public void setContentView(int resId) {
        ensureSubDecor();
        ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        //加载布局
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        mAppCompatWindowCallback.getWrapped().onContentChanged();
    }
}

最终通过LayoutInflater来加载了XML布局

2、布局的解析

从LayoutInflater开始,探索怎样解析布局,从inflate()方法开始分析

public abstract class LayoutInflater {
//...........省略
     public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
        return inflate(resource, root, root != null);
    }
      public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
      //省略。。。
        XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }
  public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        //省略。
        //核心代码
          rInflate(parser, root, inflaterContext, attrs, false);
    }
}

进入rInflate()方法;

  void rInflate(XmlPullParser parser, View parent, Context context,
            AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
​
        final int depth = parser.getDepth();
        int type;
        boolean pendingRequestFocus = false;
        //省略。。。。
        //使用parser对象进行布局的解析,
        while (((type = parser.next()) != XmlPullParser.END_TAG ||
                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
                //省略。。。。。
                //根据解析的xml中的属性值,创建对应的View对象
                final View view = createViewFromTag(parent, name, context, attrs);
                final ViewGroup viewGroup = (ViewGroup) parent;
                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
                rInflateChildren(parser, view, attrs, true);
                viewGroup.addView(view, params);
    }
​

rInflate()方法中的AttributeSet参数 AttributeSet是xml文件中元素属性的一个集合。其中提供了各种Api,供我们从已编译好的xml文件获取属性值,如getAttributeIntValue,getAttributeBooleanValue,getAttributeFloatValue等,会返回对应类型的属性值,传入的参数一般有两种形式,如下:

  • getAttributeXXXValue(int index, XXX defaultValue):根据对应属性的索引获取对应的属性值,index取值范围在0~count-1之间,找不到返回defaultValue
  • getAttributeXXXValue(String namespace, String attribute, XXX defaultValue):根据指定命名空间的属性名获取对应的属性值,找不到返回defaultValue

进入createViewFromTag方法中

  View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
            boolean ignoreThemeAttr) {
            //省略。。。。。。。。
            //主要方法
            View view = tryCreateView(parent, name, context, attrs);
            return view;
    }

进入tryCreateView方法中

  public final View tryCreateView(@Nullable View parent, @NonNull String name,
        @NonNull Context context,
        @NonNull AttributeSet attrs) {
        if (name.equals(TAG_1995)) {
            // Let's party like it's 1995!
            return new BlinkLayout(context, attrs);
        }
​
        View view;
        if (mFactory2 != null) {
            view = mFactory2.onCreateView(parent, name, context, attrs);
        } else if (mFactory != null) {
            view = mFactory.onCreateView(name, context, attrs);
        } else {
            view = null;
        }
​
        if (view == null && mPrivateFactory != null) {
            view = mPrivateFactory.onCreateView(parent, name, context, attrs);
        }
​
        return view;
    }

从上段代码看出,view是由 mFactory2.onCreateView()方法返回的,而从前面代码可以看出AppCompatDelegateImpl类实现了LayoutInflater.Factory2接口,所以实现了onCreateView的方法,进入AppCompatDelegateImpl类的onCreateView方法

​
    /**
     * From {@link LayoutInflater.Factory2}.
     */
    @SuppressWarnings("NullableProblems")
    @Override
    public final View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
        return createView(parent, name, context, attrs);
    }
​
    /**
     * From {@link LayoutInflater.Factory2}.
     */
    @SuppressWarnings("NullableProblems")
    @Override
    public View onCreateView(String name, Context context, AttributeSet attrs) {
        return onCreateView(null, name, context, attrs);
    }
 @Override
    public View createView(View parent, final String name, @NonNull Context context,
            @NonNull AttributeSet attrs) {
        //省略
        //核心
        return mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext,
                IS_PRE_LOLLIPOP, /* Only read android:theme pre-L (L+ handles this anyway) */
                true, /* Read read app:theme as a fallback at all times for legacy reasons */
                VectorEnabledTintResources.shouldBeUsed() /* Only tint wrap the context if enabled */
        );
  }

继续进入mAppCompatViewInflater.createView

final View createView(View parent, final String name, @NonNull Context context,
            @NonNull AttributeSet attrs, boolean inheritContext,
            boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
     //省略
        View view = null;
        // We need to 'inject' our tint aware Views in place of the standard framework versions
        switch (name) {
            case "TextView":
                view = createTextView(context, attrs);
                verifyNotNull(view, name);
                break;
            case "ImageView":
                view = createImageView(context, attrs);
                verifyNotNull(view, name);
                break;
                //.........
            default:
                // The fallback that allows extending class to take over view inflation
                // for other tags. Note that we don't check that the result is not-null.
                // That allows the custom inflater path to fall back on the default one
                // later in this method.
                view = createView(context, name, attrs);
        }
​
        if (view == null && originalContext != context) {
            // If the original context does not equal our themed context, then we need to manually
            // inflate it using the name so that android:theme takes effect.
            view = createViewFromTag(context, name, attrs);
        }
​
        if (view != null) {
            // If we have created a view, check its android:onClick
            checkOnClickListener(view, attrs);
            backportAccessibilityAttributes(context, view, attrs);
        }
​
        return view;
    }

我们在xml布局中写的控件,最终都会根据标识解析生成对应的View对象。

以下是流程的梳理

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3pkYzkwMjM=,size_16,color_FFFFFF,t_70.png

3、渲染

相关文章:juejin.cn/post/708810…

参考文章:blog.csdn.net/zdc9023/art…