AppCompatのピット

9355 ワード

http://blog.csdn.net/liuxu0703/article/details/70145168
textViewなどのViewを使用する場合、5.0以下のバージョンでandroidは互換性のある処理を行い、AppCompatTextViewを生成します.このようなviewのgetContext()はTineContextWrapperであり、activityまたはinstanceOfActivityの判断に強制的に移行する場合は、予想される操作に合致しません
AppCompat中的坑_第1张图片
二つの解決策がある
  • 根本的な解決策はstackoverflowの中の方法を採用して、1つのツールの方法はすべてのviewのgetContext()を交換して、工事の中のこの呼び出しの方式に適用するのは多くなくて、その上修正するリスクの少ないところ
  • 現在の問題の解決方法は、5.0以下で、このような行為を禁止できるかどうかを見て、サポートされています.方法は、私たちのBaseActivityのonCreate()でgetDegate()を呼び出すことです.setCompatVectorFromResourcesEnabled()

  • ソースコードで見ることができますが、AppCompatActivityのonCreate()にはLayoutInfatorFactoryがインストールされています.ここでは置き換えに使用します.
    1) AppCompatDelegateImplV9.onCreateView
     /**
         * From {@link android.support.v4.view.LayoutInflaterFactory}
         */
        @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);
        }
    

    2) AppCompatDelegateImplV11.callActivityOnCreateView()
     @Override
        View callActivityOnCreateView(View parent, String name, Context context, AttributeSet attrs) {
            // On Honeycomb+, Activity's private inflater factory will handle calling its
            // onCreateView(...)
            return null;
        }
    

    callActivity OnCreateViewはnullを返し、createView()によって制御されていることがわかりました.
    3) AppCompatDelegateImplV9.createView()
     @Override
        public View createView(View parent, final String name, @NonNull Context context,
                @NonNull AttributeSet attrs) {
            if (mAppCompatViewInflater == null) {
                mAppCompatViewInflater = new AppCompatViewInflater();
            }
    
            boolean inheritContext = false;
            if (IS_PRE_LOLLIPOP) {
                inheritContext = (attrs instanceof XmlPullParser)
                        // If we have a XmlPullParser, we can detect where we are in the layout
                        ? ((XmlPullParser) attrs).getDepth() > 1
                        // Otherwise we have to use the old heuristic
                        : 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 */
            );
        }  
    

    4) mAppCompatViewInflater.createView()の実装
    コードをめくり続けると、view置換のプロセスが表示されますが、ここでは制御されたパラメータも見られます.wrapContextがtrueの場合、置換されます.これがVectorEnabledTintResourcesです.shouldBeUsed()
        public 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) {
                case "TextView":
                    view = new AppCompatTextView(context, attrs);
                    break;
                case "ImageView":
                    view = new AppCompatImageView(context, attrs);
                    break;
                case "Button":
                    view = new AppCompatButton(context, attrs);
                    break;
                case "EditText":
                    view = new AppCompatEditText(context, attrs);
                    break;
                case "Spinner":
                    view = new AppCompatSpinner(context, attrs);
                    break;
                case "ImageButton":
                    view = new AppCompatImageButton(context, attrs);
                    break;
                case "CheckBox":
                    view = new AppCompatCheckBox(context, attrs);
                    break;
                case "RadioButton":
                    view = new AppCompatRadioButton(context, attrs);
                    break;
                case "CheckedTextView":
                    view = new AppCompatCheckedTextView(context, attrs);
                    break;
                case "AutoCompleteTextView":
                    view = new AppCompatAutoCompleteTextView(context, attrs);
                    break;
                case "MultiAutoCompleteTextView":
                    view = new AppCompatMultiAutoCompleteTextView(context, attrs);
                    break;
                case "RatingBar":
                    view = new AppCompatRatingBar(context, attrs);
                    break;
                case "SeekBar":
                    view = new AppCompatSeekBar(context, attrs);
                    break;
            }
    
            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 it's android:onClick
                checkOnClickListener(view, attrs);
            }
    
            return view;
        }
    

    5) VectorEnabledTintResources.shouldBeUsed()
    public static boolean shouldBeUsed() {
            return AppCompatDelegate.isCompatVectorFromResourcesEnabled()
                    && Build.VERSION.SDK_INT <= MAX_SDK_WHERE_REQUIRED;
        }
    
            AppCompatDelegate.isCompatVectorFromResourcesEnabled()
    

    6) AppCompatDelegate.isCompatVectorFromResourcesEnabled()
    関連する方法では、これは制御可能であることがわかります.
    /**
         * Sets whether vector drawables on older platforms (< API 21) can be used within
         * {@link android.graphics.drawable.DrawableContainer} resources.
         *
         * 

    When enabled, AppCompat can intercept some drawable inflation from the framework, which * enables implicit inflation of vector drawables within * {@link android.graphics.drawable.DrawableContainer} resources. You can then use those * drawables in places such as {@code android:src} on {@link android.widget.ImageView}, * or {@code android:drawableLeft} on {@link android.widget.TextView}. Example usage:

    * *
    
         * <selector xmlns:android="...">
         *     <item android:state_checked="true"
         *           android:drawable="@drawable/vector_checked_icon" />
         *     <item android:drawable="@drawable/vector_icon" />
         * </selector>
         *
         * <TextView
         *         ...
         *         android:drawableLeft="@drawable/vector_state_list_icon" />
         * 
    *
    * This feature defaults to disabled, since enabling it can cause issues with memory usage,
    * and problems updating {@link Configuration} instances. If you update the configuration
    * manually, then you probably do not want to enable this. You have been warned.
    *
    * Even with this disabled, you can still use vector resources through
    * {@link android.support.v7.widget.AppCompatImageView#setImageResource(int)} and it's
    * {@code app:srcCompat} attribute. They can also be used in anything which AppCompat inflates
    * for you, such as menu resources.
    *
    * Please note: this only takes effect in Activities created after this call.
    */
    public static void setCompatVectorFromResourcesEnabled(boolean enabled) {
    sCompatVectorFromResourcesEnabled = enabled;
    }
    /**
    * Returns whether vector drawables on older platforms (< API 21) can be accessed from within
    * resources.
    *
    * @see #setCompatVectorFromResourcesEnabled(boolean)
    */
    public static boolean isCompatVectorFromResourcesEnabled() {
    return sCompatVectorFromResourcesEnabled;
    }
    を つけたのは、BaseActivityでsuper.oncreate()の 、getDegate()を び します.setCompatVectorFromResourcesEnabled(false)この の に してください.それは5.0 のシステムでvectorを にしたことです.