Android LayoutInflater.setFactor

作者: 折剑游侠 | 来源:发表于2020-05-12 14:36 被阅读0次

    前面写LayoutInflater时看到了LayoutInflater.Factory和LayoutInflater.Factory2接口

    在LayoutInflater.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 && mPrivateFactory != null) {
                view = mPrivateFactory.onCreateView(parent, name, context, attrs);
            }
    

    调用LayoutInflater.createView()创建View之前会判断是否有设置Factory/Factory2。若设置,通过Factory/Factory2创建View。当然最终流程还是走的createView()方法反射创建View,只不过在创建之前做了hook处理,下面看看源码流程。

    LayoutInflater
        private Factory mFactory;
        private Factory2 mFactory2;
        private Factory2 mPrivateFactory;
    
        public void setFactory(Factory factory) {
            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 = factory;
            } else {
                mFactory = new FactoryMerger(factory, null, mFactory, mFactory2);
            }
        }
    
        public void setFactory2(Factory2 factory) {
            //Factory/Factory2只能设置一次,最终都返回的包装类FactoryMerger
            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;
            //设置Factory2也会赋值给Factory
            if (mFactory == null) {
                mFactory = mFactory2 = factory;
            } else {
                mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);
            }
        }
    
        /**
         * @hide for use by framework
         * 官方注释mPrivateFactory为framework使用
         */
        public void setPrivateFactory(Factory2 factory) {
            if (mPrivateFactory == null) {
                mPrivateFactory = factory;
            } else {
                mPrivateFactory = new FactoryMerger(factory, factory, mPrivateFactory, mPrivateFactory);
            }
        }
    
        public interface Factory {
            @Nullable
            View onCreateView(@NonNull String name, @NonNull Context context,
                    @NonNull AttributeSet attrs);
        }
    
        public interface Factory2 extends Factory {
            @Nullable
            View onCreateView(@Nullable View parent, @NonNull String name,
                    @NonNull Context context, @NonNull AttributeSet attrs);
        }
    
        private static class FactoryMerger implements Factory2 {
            private final Factory mF1, mF2;
            private final Factory2 mF12, mF22;
    
            FactoryMerger(Factory f1, Factory2 f12, Factory f2, Factory2 f22) {
                mF1 = f1;
                mF2 = f2;
                mF12 = f12;
                mF22 = f22;
            }
    
             //Factory的抽象方法优先调用Factory.onCreateView()
            @Nullable
            public View onCreateView(@NonNull String name, @NonNull Context context,
                    @NonNull AttributeSet attrs) {
                View v = mF1.onCreateView(name, context, attrs);
                if (v != null) return v;
                return mF2.onCreateView(name, context, attrs);
            }
    
             //Factory2的抽象方法优先调用Factory2.onCreateView()
            @Nullable
            public View onCreateView(@Nullable View parent, @NonNull String name,
                    @NonNull Context context, @NonNull AttributeSet attrs) {
                View v = mF12 != null ? mF12.onCreateView(parent, name, context, attrs)
                        : mF1.onCreateView(name, context, attrs);
                if (v != null) return v;
                return mF22 != null ? mF22.onCreateView(parent, name, context, attrs)
                        : mF2.onCreateView(name, context, attrs);
            }
        }
    

    其中mPrivateFactory标记为hide,use by framework。我们可自行设置Factory/Factory2。
    下面跟一下setFactory()/setFactory2()方法调用处

    LayoutInflaterCompat
        @Deprecated
        public static void setFactory(
                @NonNull LayoutInflater inflater, @NonNull LayoutInflaterFactory factory) {
            if (Build.VERSION.SDK_INT >= 21) {
                inflater.setFactory2(factory != null ? new Factory2Wrapper(factory) : null);
            } else {
                final LayoutInflater.Factory2 factory2 = factory != null
                        ? new Factory2Wrapper(factory) : null;
                inflater.setFactory2(factory2);
    
                final LayoutInflater.Factory f = inflater.getFactory();
                if (f instanceof LayoutInflater.Factory2) {
                    forceSetFactory2(inflater, (LayoutInflater.Factory2) f);
                } else {
                    forceSetFactory2(inflater, factory2);
                }
            }
        }
    
        public static void setFactory2(
                @NonNull LayoutInflater inflater, @NonNull LayoutInflater.Factory2 factory) {
            inflater.setFactory2(factory);
    
            if (Build.VERSION.SDK_INT < 21) {
                final LayoutInflater.Factory f = inflater.getFactory();
                if (f instanceof LayoutInflater.Factory2) {
                    forceSetFactory2(inflater, (LayoutInflater.Factory2) f);
                } else {
                    forceSetFactory2(inflater, factory);
                }
            }
        }
    

    笔者sdk版本为29,setFactory()方法已被标记为弃用
    继续跟进LayoutInflaterCompat.setFactory2()方法调用处

    AppCompatDelegateImpl.installViewFactory()
        @Override
        public void installViewFactory() {
            LayoutInflater layoutInflater = LayoutInflater.from(mContext);
            if (layoutInflater.getFactory() == null) {
                LayoutInflaterCompat.setFactory2(layoutInflater, this);
            } else {
                if (!(layoutInflater.getFactory2() instanceof AppCompatDelegateImpl)) {
                    Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed"
                            + " so we can not install AppCompat's");
                }
            }
        }
    

    LayoutInflaterCompat.setFactory2(layoutInflater, this)这里的this即AppCompatDelegateImpl实现了LayoutInflater.Factory2接口

    AppCompatDelegateImpl是AppCompatActivity的代理类

    AppCompatActivity.onCreate()
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            final AppCompatDelegate delegate = getDelegate();
            //这里调用
            delegate.installViewFactory();
            delegate.onCreate(savedInstanceState);
            super.onCreate(savedInstanceState);
        }
    

    也就是说Activity继承自AppCompatActivity后便为LayoutInflater设置了Factory2接口,接口实例为AppCompatDelegateImpl,最终hook操作就在AppCompatDelegateImpl的接口方法中了。

    AppCompatDelegateImpl.onCreateView()
        @Override
        public final View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
            return createView(parent, name, context, attrs);
        }
    
        @Override
        public View onCreateView(String name, Context context, AttributeSet attrs) {
            return onCreateView(null, name, context, attrs);
        }
    
    AppCompatDelegateImpl.CreateView()
       @Override
        public View createView(View parent, final String name, @NonNull Context context,
                @NonNull AttributeSet attrs) {
            if (mAppCompatViewInflater == null) {
                TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
                String viewInflaterClassName =
                        a.getString(R.styleable.AppCompatTheme_viewInflaterClass);
                if ((viewInflaterClassName == null)
                        || AppCompatViewInflater.class.getName().equals(viewInflaterClassName)) {
                    mAppCompatViewInflater = new AppCompatViewInflater();
                } else {
                    try {
                        Class<?> viewInflaterClass = Class.forName(viewInflaterClassName);
                        mAppCompatViewInflater =
                                (AppCompatViewInflater) viewInflaterClass.getDeclaredConstructor()
                                        .newInstance();
                    } catch (Throwable t) {
                        Log.i(TAG, "Failed to instantiate custom view inflater "
                                + viewInflaterClassName + ". Falling back to default.", t);
                        mAppCompatViewInflater = new AppCompatViewInflater();
                    }
                }
            }
    
            boolean inheritContext = false;
            if (IS_PRE_LOLLIPOP) {
                inheritContext = (attrs instanceof XmlPullParser)
                        ? ((XmlPullParser) attrs).getDepth() > 1
                        : shouldInheritContext((ViewParent) parent);
            }
    
            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 */
            );
        }
    
    AppCompatViewInflater.createView()
        final View createView(View parent, final String name, @NonNull Context context,
                @NonNull AttributeSet attrs, boolean inheritContext,
                boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
            ...
            switch (name) {
                case "TextView":
                    view = createTextView(context, attrs);
                    verifyNotNull(view, name);
                    break;
                case "ImageView":
                    view = createImageView(context, attrs);
                    verifyNotNull(view, name);
                    break;
                case "Button":
                    view = createButton(context, attrs);
                    verifyNotNull(view, name);
                    break;
                case "EditText":
                    view = createEditText(context, attrs);
                    verifyNotNull(view, name);
                    break;
                case "Spinner":
                    view = createSpinner(context, attrs);
                    verifyNotNull(view, name);
                    break;
                case "ImageButton":
                    view = createImageButton(context, attrs);
                    verifyNotNull(view, name);
                    break;
                case "CheckBox":
                    view = createCheckBox(context, attrs);
                    verifyNotNull(view, name);
                    break;
                case "RadioButton":
                    view = createRadioButton(context, attrs);
                    verifyNotNull(view, name);
                    break;
                case "CheckedTextView":
                    view = createCheckedTextView(context, attrs);
                    verifyNotNull(view, name);
                    break;
                case "AutoCompleteTextView":
                    view = createAutoCompleteTextView(context, attrs);
                    verifyNotNull(view, name);
                    break;
                case "MultiAutoCompleteTextView":
                    view = createMultiAutoCompleteTextView(context, attrs);
                    verifyNotNull(view, name);
                    break;
                case "RatingBar":
                    view = createRatingBar(context, attrs);
                    verifyNotNull(view, name);
                    break;
                case "SeekBar":
                    view = createSeekBar(context, attrs);
                    verifyNotNull(view, name);
                    break;
                case "ToggleButton":
                    view = createToggleButton(context, attrs);
                    verifyNotNull(view, name);
                    break;
                default:
                    view = createView(context, name, attrs);
            }
    
            if (view == null && originalContext != context) {
                view = createViewFromTag(context, name, attrs);
            }
    
            if (view != null) {
                checkOnClickListener(view, attrs);
            }
    
            return view;
        }
    
        @NonNull
        protected AppCompatTextView createTextView(Context context, AttributeSet attrs) {
            return new AppCompatTextView(context, attrs);
        }
    
        @NonNull
        protected AppCompatImageView createImageView(Context context, AttributeSet attrs) {
            return new AppCompatImageView(context, attrs);
        }
    
        @NonNull
        protected AppCompatButton createButton(Context context, AttributeSet attrs) {
            return new AppCompatButton(context, attrs);
        }
    
        @NonNull
        protected AppCompatEditText createEditText(Context context, AttributeSet attrs) {
            return new AppCompatEditText(context, attrs);
        }
        ...
    

    AppCompatActivity设置的Factory2将系统封装的常用View变为同名的AppCompatView以此支持tint属性。

    下面我们自己设置Factory2

    class Factory2Activity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            layoutInflater.factory2 = object : LayoutInflater.Factory2 {
                override fun onCreateView(parent: View?, name: String, context: Context, attrs: AttributeSet): View? {
                    //调用AppCompatDelegateImpl.CreateView()系统逻辑生成view
                    val view = delegate.createView(parent, name, context, attrs)
                    if (view != null && view is TextView) {
                        //这里打印出来是AppCompatTextView
                        Log.d("zjyx", view.javaClass.simpleName)
                        return AppCompatButton(context, attrs)
                    }
                    return view
                }
    
                override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {
                    return null
                }
    
            }
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_factory)
        }
    }
    

    xml只设置了一个TextView

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <TextView
            android:id="@+id/tv_factory"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="text_view"
            android:textColor="@android:color/holo_orange_light"
            android:textSize="20sp" />
    </RelativeLayout>
    

    这里将TextView变为了AppCompatButton
    借助Factory2接口,可以在BaseActivity中统一对所有View进行处理。包括改变字体,设置view背景色,统一替换等等。

    相关文章

      网友评论

        本文标题:Android LayoutInflater.setFactor

        本文链接:https://www.haomeiwen.com/subject/fgavnhtx.html