Android xml解析到View的过程

文章目录

    • 分析 View 系列
    • 源码分析
      • 前言,一些必须知道的知识
      • 进入主题
      • 查看 View的创建流程
      • 总结

分析 View 系列

https://blog.csdn.net/qq_30889373/article/details/80538211#Android_View_11

源码分析

前言,一些必须知道的知识

API 版本 27

我们先看看 AppCompatActivity 的跟这次主题相关的
重要方法

public class  AppCompatActivity ...{

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        final AppCompatDelegate delegate = getDelegate();
        // 这个方法我们稍微记一下,待会要
        delegate.installViewFactory();
        // ....
        super.onCreate(savedInstanceState);
    }
    
    @NonNull
    public AppCompatDelegate getDelegate() {
        if (mDelegate == null) {
            mDelegate = AppCompatDelegate.create(this, this);
        }
        return mDelegate;
    }

    @Override
    public void setContentView(@LayoutRes int layoutResID) {
        getDelegate().setContentView(layoutResID);
    }
}

AppCompatDelegate 是什么呢,我们进入继续观看

public abstract class AppCompatDelegate {
	....  
    public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
        return create(activity, activity.getWindow(), callback);
    }

	// 根据不同的系统版本 创建不同的 Imp
    private static AppCompatDelegate create(Context context, Window window,
            AppCompatCallback callback) {
        if (Build.VERSION.SDK_INT >= 24) {
            return new AppCompatDelegateImplN(context, window, callback);
        } else if (Build.VERSION.SDK_INT >= 23) {
            return new AppCompatDelegateImplV23(context, window, callback);
        } else {
            return new AppCompatDelegateImplV14(context, window, callback);
        }
    }
}

我们 查看一下继承关系

Android xml解析到View的过程_第1张图片

对应的类点击查看发现

public class  AppCompatActivity ...{
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        final AppCompatDelegate delegate = getDelegate();
        // 这个方法我们稍微记一下,待会要进入主题
        delegate.installViewFactory();
}

// 重点关注下  LayoutInflater.Factory2 
class AppCompatDelegateImplV9 extends AppCompatDelegateImplBase
        implements MenuBuilder.Callback, LayoutInflater.Factory2 {
        
      
    @Override
    public void installViewFactory() {
        LayoutInflater layoutInflater = LayoutInflater.from(mContext);
        if (layoutInflater.getFactory() == null) {
        	// 重点方法,第二个参数是 LayoutInflater.Factory2  也就是自己实现了
            LayoutInflaterCompat.setFactory2(layoutInflater, this);
        } else {
            if (!(layoutInflater.getFactory2() instanceof AppCompatDelegateImplV9)) {
                Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed"
                        + " so we can not install AppCompat's");
            }
        }
    }
}

// 而 Factory2 接口就只有一个方法
    public interface Factory2 extends Factory {
    	
        public View onCreateView(View parent, String name, Context context, AttributeSet attrs);
    }

这里可以自行跟进去看看,我们这里只讲一个重点知识,因为类似
https://github.com/ximsfei/Android-skin-support
通过替换 Factory2 来实现换肤的方案,需要知道这个知识点

public abstract class LayoutInflater {

    public void setFactory2(Factory2 factory) {
    	// 可以看到只能设置一次,而上面说明的开源库,是通过反射修改这个值
    	// 替换自己的 Factory2
        if (mFactorySet) {
            throw new IllegalStateException("A factory has already been set on this LayoutInflater");
        }
        if (factory == null) {
            throw new NullPointerException("Given factory can not be null");
        }
        mFactorySet = true;
        if (mFactory == null) {
            mFactory = mFactory2 = factory;
        } else {
            mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);
        }
    }
}

进入主题

那么上面讲了这么多跟 xml解析有什么关系呢,我们进入主题后,你就明白前面的意思了
···

// 1
public class  AppCompatActivity ...{

    @Override
    public void setContentView(@LayoutRes int layoutResID) {
        getDelegate().setContentView(layoutResID);
    }
}

// 2
class AppCompatDelegateImplV9 {
    @Override
    public void setContentView(int resId) {
        ensureSubDecor();
        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        // 通过 LayoutInflater 解析,我们跟进去看下
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        mOriginalWindowCallback.onContentChanged();
    }
}

// 3
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) {
	     final Resources res = getContext().getResources();
	     // 通过 资源ID 获取 XmlResourceParser
	     final XmlResourceParser parser = res.getLayout(resource);
	     try {
	     	// 进入查看
	         return inflate(parser, root, attachToRoot);
	     } finally {
	         parser.close();
	     }
     }
     
	// 关键方法来了
     public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
     	....
        if (TAG_MERGE.equals(name)) {
            .. 其实内部最终也会调用 createViewFromTag
            rInflate(parser, root, inflaterContext, attrs, false);
        } else {
            //  关键方法 传入
            final View temp = createViewFromTag(root, name, inflaterContext, attrs);
        }
     }
	
	// 4 
    private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {
        return createViewFromTag(parent, name, context, attrs, false);
    }
    View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
            boolean ignoreThemeAttr) {
	  //...可以看到
       if (mFactory2 != null) {
           view = mFactory2.onCreateView(parent, name, context, attrs);
       } else if (mFactory != null) {
           view = mFactory.onCreateView(name, context, attrs);
       } else {
           view = null;
       }
}

总结前面分析

关键方法 描述
LayoutInflater.from(mContext).inflate(resId, contentParent) 传入资源ID ,父容器,可空
XmlResourceParser parser = res.getLayout(resource); 通过资源ID 获取 xml解析后的类
mFactory2.onCreateView(parent, name, context, attrs) 而后调用 mFactory2.onCreateView 创建对应view

mFactory2 在上面的 AppCompatDelegateImplV9 的 installViewFactory() 中已设置好了的,
其实 mFactory2 就是 AppCompatDelegateImplV9.

查看 View的创建流程

public class AppCompatDelegateImplV9{
    @Override
    public final View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
        // First let the Activity's Factory try and inflate the view
        final View view = callActivityOnCreateView(parent, name, context, attrs);
        if (view != null) {
            return view;
        }

        // If the Factory didn't handle it, let our createView() method try
        return createView(parent, name, context, attrs);
    }
	@Override
    public View createView(View parent, final String name, @NonNull Context context,
            @NonNull AttributeSet attrs) {
        if (mAppCompatViewInflater == null) {
        		....
                mAppCompatViewInflater = new AppCompatViewInflater();
            } else {
               ....
        }
		...
        return mAppCompatViewInflater.createView(....);
    }
}

下面我们查看 系统控件的创建过程

public AppCompatViewInflater {
	
	/**
	* 可以发现,如果是TextView,就直接返回V7包中的AppCompatTextView类,
	* 所以这就是为什么当我们使   用AppCompatActivity的时候,TextView变成AppCompatTextView的原因。
	*/
    @NonNull
    protected AppCompatTextView createTextView(Context context, AttributeSet attrs) {
        return new AppCompatTextView(context, attrs);
    }

	// 创建 View
    final View createView(View parent, final String name, @NonNull Context context,
            @NonNull AttributeSet attrs, boolean inheritContext,
            boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
        final Context originalContext = context;
		.... 
        switch (name) {
            case "TextView":
                view = createTextView(context, attrs);
                verifyNotNull(view, name);
                break;
          
            default:
                view = createView(context, name, attrs);
        }
        if (view == null && originalContext != context) {
            // 使用名字 inflate,主题将会无效
            view = createViewFromTag(context, name, attrs);
        }
        
        if (view != null) {
            // 如果我们尝试创建一个 view, check its android:onClick
            checkOnClickListener(view, attrs);
        }

        return view;
    }
}

那么自定义控件,还有哪些需要 v7 包名的组件怎么创建的呢?

public AppCompatViewInflater {
	
	// 默认前缀包名列表
    private static final String[] sClassPrefixList = {
            "android.widget.",
            "android.view.",
            "android.webkit."
    };
    
    private View createViewFromTag(Context context, String name, AttributeSet attrs) {
        if (name.equals("view")) {
            name = attrs.getAttributeValue(null, "class");
        }

        try {
            mConstructorArgs[0] = context;
            mConstructorArgs[1] = attrs;
			// 尝试从 默认前缀包名列表 中查找
            if (-1 == name.indexOf('.')) {
                for (int i = 0; i < sClassPrefixList.length; i++) {
                    final View view = createViewByPrefix(context, name, sClassPrefixList[i]);
                    if (view != null) {
                        return view;
                    }
                }
                return null;
            } else {
            	// 加载自定义View,也就是完整名 比如 com.xm.xbutton
                return createViewByPrefix(context, name, null);
            }
        } catch (Exception e) {
            return null;
        } finally {
            mConstructorArgs[0] = null;
            mConstructorArgs[1] = null;
        }
    }
}

总结

public interface Factory2 extends Factory {
    pulic View onCreateView(View parent, String name, Context context, AttributeSet attrs);
    }
}

简单几句话讲解下 xml的创建过程

  1. AppCompatActivity onCreate 时调用 installViewFactory (实际为 AppCompatDelegateImplV9)
  2. AppCompatDelegateImplV9 实现接口 Factory2
  3. installViewFactory 方法将 LayoutInflater.setFactory2(AppCompatDelegateImplV9.this)
  4. 将资源 id 解析为 XmlResourceParser ,并 createViewFromTag 的方法
  5. 内部调用 Factory2.createView 创建View
  6. 内部使用 AppCompatViewInflater.java 专门用来根据 xml中分别的名称,创建View

你可能感兴趣的:(Android,View篇,深入安卓源码)