zoukankan      html  css  js  c++  java
  • [Android] 聊一下 AppCompatTextView 与 TextView

    ### 抛出
    1.compat库是如何将`TextView`替换为`AppCompatTextView`的?
    2.为什么要进行替换?
    3.根据替换相关原理,我们可以做哪些事情?
    > Answer1:
    ###### 第二问:
    先从第二问开始吧,`AppCompatTextView`继承自`TextView`,是对`TextView`的一种扩展,因为在5.0中首次推出了`MaterialDesign`这种设计风格,但是众所周知,5.0推出不可能所有的设备全部都更新到最新版本,为了在早期版本上实现新的功能(这些新功能比如从源码注释中解读到`backgroundTint`属性,根据文本内容自适应大小等),即为了新特性同样可以兼容老版本,framework在创建`TextView`实例的时候,自动帮我们进行了替换。其它的AppCompatXXX与XXX的关系也是如此。
    ###### 第一问:
    然后第一问,是如何完成替换的,我们这里只拿最直观的流程举例,且尽可能的简化源码过程,在讨论这个问题之前,先了解几个预备知识:
    + View是这么被解析创建出来的:
    1.LayoutInflater:将布局XML文件实例化为其对应的View对象,我们在Activity中通过setCountentView传入一个Layout的资源文件id,最终该方法调用到PhoneWindow的setContentView方法,这个方法里面有调用到`mLayoutInflater.inflate(layoutResID,mContentParent);`
    2.inflate方法,该方法的作用是将指定的XML文件填充到View的层次结构中去,最终无论通过什么途径调用到inflate方法,都会走到三个参数的重载方法这里:
    ```
    return inflate(parser,root,attachToRoot);
    ```
    parser你可以认为持有将Layout.XML解析后的数据,后两个参数的意义如下:
    + root 为null,attachToRoot无意义,inflate返回的是当前XML对应的根布局。
    + root不为null且attachToRoot为true,则整个XML对应的布局就设置了根布局是root
    + root不为null且attachToRoot为false,则会将root的`LayoutParams`设置给当前的XML布局。
    知道了LayoutInflate.inflate做了什么,再往下,inflate中会调用到`createViewFromTag`,从方法名就能知道,继续往下走,我们离答案越来越近了。
    `createViewFromTag`做的事情非常有意思:
    ```
    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 && mPrivateFacotry !=null){
    view =mPrivateFacotry.onCreateVoew(parent,name,context,attrs);
    }
    if(view ==null){
    final Object lastContext =mConstructorArgs[0];
    mConstructorArgs[0]=context;
    try{
    if(-1 == name.indexOf('.')){ //①
    view =onCreateView(parent,name,attrs);
    }else{
    view =createVoew(name,null,attrs);
    }
    }finally{
    mConstructorArgs[0]=lastContext;
    }
    }
    return view;
    ```
    先看①这个if-else,条件是name中有没有字符“.”,如果有我们会执行`onCreateView`,如果没有会执行createView。name啥时候有点?自定义控件的时候。当是系统控件的时候,`createView`会有一个填充了第二个参数的调用:`createVoew(name,"android.view.",attrs);`补上了View控件的全路径名,而自定义控件则不需要,因为传入的name就是一个全路径名。
    为什么要全路径名?因为View控件对象的创建是通过反射来实现的:
    ```
    clazz =mContext.getClassLoader().loadClass(
    prefix!= null ? (prefix+name):name).asSubclass(View.class);
    ...
    constructor=clazz.getConstructor(mConstructorSignature);
    constructor.setAccessible(true);
    sConstructorMap.put(name,constructor);
    ...
    args[1]=attrs;
    final View view =constructor.newInstance(args);
    ```
    下面对这几布做一个总结:
    XML中保存了`ViewTree`的结构和View的相关标签信息(包括View的类型和一些属性值),然后这些信息会在后面通过反射的方式(如果没有Factory2和Factory的话)创建实例对象,如果创建的是`ViewGroup`,则会对它的子View遍历重复创建步骤,创建完View对象后,会add到对应的`ViewGroup`中,其中相关方法调用流程是:`inflate->rInflate->createViewFromTag->createView`
    好像还是没有看到资源替换?
    我们只解释了后半部分,没有解释前半部分,那么什么是Factory?
    继续往下看:
    createViewFromTag中会先判断有没有Factory或者Factory2的对象,如果有,则调用Factory的onCreateView方法,这两个类都是接口,其中Factory2是Factory的子接口,都只有唯一一个onCreateView方法,不同之处在于Factory2的`onCreateView`方法传入了`parentView`
    **该方法的作用就是你可以借助它来改造XML中已经存在了的Tag的值**。所有Factory2可以达到改造parentView的目的。
    但是我们日常中根本就没有任何地方接触到了Factory2,那么它是不是就直接是null呢?到这里又是一番源码调来调去,为了便于理解,只需要知道,这个Factory2,在最开始`AppCompatActivity`(为了兼容低版本,我们现在Activity默认都继承自它)中的`onCreate`方法中就已经通过层层调用被设置好了。
    既然现在Factory2不为空,那么就应该去走它的onCreateView方法了,这里又是层层调用,最终来到了AppCompatViewInflater的onCreateView方法:
    ![image](https://upload-images.jianshu.io/upload_images/564272-5b50eff5cec1775c?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    如果创建的是非兼容控件(系统控件那么多,实现兼容的只是常用的一些控件),那么就会是143行,在146行中通过反射创建View对象。
    啰里啰嗦了一大堆,还是没回答第一个问题:`compat`库是如何将TextView替换为`AppCompatTextView`的?
    > 个人对这个的理解:在将XML文件解析成包含ViewTree信息之后,开始利用这些信息去创建每一个View节点,在创建View对象的时候,如果发现这个节点是属于支持兼容的控件比如`TextView`,那么就会去调用到`new AppCompatTextView()`来创建一个兼容View对象,也就是在创建的时候,就已经实现了替换。

    第三问:
    根据替换原理,我们可以做哪些事情?
    整个替换从上面的源码中就可以看到,能够被替换的关键是`Factory2`存在,那么我觉得,其实问题问的是Factory2可以用来做什么?
    [答案在这里](https://blog.csdn.net/lmj623565791/article/details/51503977)

    参考链接:
    [每日一问 AppCompatTextView 与 TextView](https://www.wanandroid.com/wenda/show/8832)

  • 相关阅读:
    rsync文件备份同步
    程序员的家!我终于拥有自己的blog了!!!
    使用APMServ服务配置如何进行Wordpress本地伪静态设置
    清理Windows.edb文件释放C盘空间(原创)
    不知道按到什么键了,代码前面出现了省略号,使用Ctrl+E+S恢复
    (转)c/c++资源(源码,开发工具)
    EditPlus自动换行
    不用外部软件,直接对文件批量重命名(转)
    C# 实现自定义处理窗体按键(整理)
    C# 控件名称缩写介绍(转)
  • 原文地址:https://www.cnblogs.com/merbn/p/11351459.html
Copyright © 2011-2022 走看看