Android AppCompatActivity

簡述

在很久很久以前,android的activity時繼承Activity的,但在后來卻改成了AppCompatActivity,那AppCompatActivity是什么呢?繼承他有什么用呢?
其實他最主要的工作是5.0之后的View兼容和頁面主題相關(guān)處理,例如AppCompatTextView之類的內(nèi)部都是有兼容操作的,而繼承AppCompatActivity后,其會在內(nèi)部將TextView替換為AppCompatTextView(之前一直在想使用TextView好還是AppCompatTextView好,其實都一樣,其內(nèi)部會進(jìn)行替換),那這里就來分析下它是怎么進(jìn)行替換的.

分析

Factory

首先來說說Factory

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

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

Factory的作用是對View的創(chuàng)建,在Activity創(chuàng)建View時會調(diào)用其Factory進(jìn)行創(chuàng)建,其調(diào)用過程是

Activyty
|
setContentView()
|
PhoneView.setContentView()
|
LayoutInflater.inflate()
|
createViewFromTag()

createViewFromTag的實現(xiàn)為

View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
            boolean ignoreThemeAttr) {
        ...
        try {
            View view;
            if (mFactory2 != null) {//檢查是否設(shè)置的Factory2,有就調(diào)用其來創(chuàng)建View
                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) {//mPrivateFactory 是內(nèi)部的工廠,activity中的實現(xiàn)為空實現(xiàn)
                view = mPrivateFactory.onCreateView(parent, name, context, attrs);
            }

            if (view == null) {//此處的View未建立則會通過反射來進(jìn)行創(chuàng)建
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                    if (-1 == name.indexOf('.')) {
                        view = onCreateView(parent, name, attrs);
                    } else {
                        view = createView(name, null, attrs);
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;
                }
            }

            return view;
        } ...//一系列異常處理
    }

由此可見,使用Factory可以對所有的View的創(chuàng)建進(jìn)行攔截,并自定義其創(chuàng)建方式

AppCompatActivity的onCreate

AppCompatActivity的替換操作主要在onCreate進(jìn)行,這里主要做的是設(shè)置Factory2(其繼承了Factory,所以內(nèi)部需要實現(xiàn)兩個創(chuàng)建View的方法)

 @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
       ...
        delegate.installViewFactory();//裝載Factory2
        ...
        super.onCreate(savedInstanceState);
    }

而具體的裝載過程在AppCompatDelegateImplV9中

@Override
    public void installViewFactory() {
        LayoutInflater layoutInflater = LayoutInflater.from(mContext);
        if (layoutInflater.getFactory() == null) {
            LayoutInflaterCompat.setFactory2(layoutInflater, this);//設(shè)置View創(chuàng)建工廠
        } 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");
            }
        }
    }

可以看出AppCompatActivity通過setFactory2來設(shè)置,而且設(shè)置前會檢查時候已經(jīng)設(shè)置,如果已經(jīng)設(shè)置了就會報錯.
其Factory的實現(xiàn)為

/**
     * From {@link LayoutInflater.Factory2}.
     */
    @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);//此處是調(diào)用Activity中的onCreateView方法
        if (view != null) {
            return view;
        }

        // If the Factory didn't handle it, let our createView() method try
        return createView(parent, name, context, attrs);//此處進(jìn)行兼容性View的替換
    }

    /**
     * From {@link LayoutInflater.Factory2}.
     */
    @Override
    public View onCreateView(String name, Context context, AttributeSet attrs) {
        return onCreateView(null, name, context, attrs);//直接調(diào)用上面的方法
    }

在createView的一系列的操作后會進(jìn)行兼容性View的替換,具體實現(xiàn)在AppCompatViewInflater中

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;

        // We can emulate Lollipop's android:theme attribute propagating down the view hierarchy
        // by using the parent's context
        if (inheritContext && parent != null) {
            context = parent.getContext();
        }
        if (readAndroidTheme || readAppTheme) {
            // We then apply the theme on the context, if specified
            context = themifyContext(context, attrs, readAndroidTheme, readAppTheme);
        }
        if (wrapContext) {
            context = TintContextWrapper.wrap(context);
        }

        View view = null;

        // We need to 'inject' our tint aware Views in place of the standard framework versions
        switch (name) {//創(chuàng)建兼容性View
            case "TextView":
                view = createTextView(context, attrs);//返回AppCompatTextView,以下類似
                verifyNotNull(view, name);//空監(jiān)測,為空會直接拋異常
                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;
            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);//這里是空實現(xiàn)
        }

        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);
        }

        return view;
    }

由此可見,在創(chuàng)建View 時AppCompatActivity將TextView創(chuàng)建為AppCompatTextView

End

綜上所訴,AppCompatActivity通過Factory將View替換為具有兼容性的View,當(dāng)然AppCompatActivity其中也很多其他的操作,這里沒有詳細(xì)去說明,若文中有什么錯誤的請指出糾正,謝謝喲~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容