AppCompatのピット
9355 ワード
http://blog.csdn.net/liuxu0703/article/details/70145168
textViewなどのViewを使用する場合、5.0以下のバージョンでandroidは互換性のある処理を行い、AppCompatTextViewを生成します.このようなviewのgetContext()はTineContextWrapperであり、activityまたはinstanceOfActivityの判断に強制的に移行する場合は、予想される操作に合致しません
二つの解決策がある根本的な解決策はstackoverflowの中の方法を採用して、1つのツールの方法はすべてのviewのgetContext()を交換して、工事の中のこの呼び出しの方式に適用するのは多くなくて、その上修正するリスクの少ないところ 現在の問題の解決方法は、5.0以下で、このような行為を禁止できるかどうかを見て、サポートされています.方法は、私たちのBaseActivityのonCreate()でgetDegate()を呼び出すことです.setCompatVectorFromResourcesEnabled()
ソースコードで見ることができますが、AppCompatActivityのonCreate()にはLayoutInfatorFactoryがインストールされています.ここでは置き換えに使用します.
1) AppCompatDelegateImplV9.onCreateView
2) AppCompatDelegateImplV11.callActivityOnCreateView()
callActivity OnCreateViewはnullを返し、createView()によって制御されていることがわかりました.
3) AppCompatDelegateImplV9.createView()
4) mAppCompatViewInflater.createView()の実装
コードをめくり続けると、view置換のプロセスが表示されますが、ここでは制御されたパラメータも見られます.wrapContextがtrueの場合、置換されます.これがVectorEnabledTintResourcesです.shouldBeUsed()
5) VectorEnabledTintResources.shouldBeUsed()
6) AppCompatDelegate.isCompatVectorFromResourcesEnabled()
関連する方法では、これは制御可能であることがわかります.
textViewなどのViewを使用する場合、5.0以下のバージョンでandroidは互換性のある処理を行い、AppCompatTextViewを生成します.このようなviewのgetContext()はTineContextWrapperであり、activityまたはinstanceOfActivityの判断に強制的に移行する場合は、予想される操作に合致しません
二つの解決策がある
ソースコードで見ることができますが、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を にしたことです.