AppCompat中的坑
http://blog.csdn.net/liuxu0703/article/details/70145168
在使用类似textView等View时, 5.0以下的版本中,android会做一个兼容性的处理,生成AppCompatTextView,这样的view的getContext()是TineContextWrapper,如果要强制转为activity或者instanceOfActivity的判断时,不会符合预期的操作
有两种解决办法
根本的解决办法,采用stackoverflow中的办法,一个工具方法来替换所有的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; }
发现 callActivityOnCreateView返回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
更多相关文章
- H5 调用android原生相机代码分析
- android的Fragment解析(一行代码引发的思考)
- Android单例关闭应用程序
- android的两种启动service方式及混合方式
- Android(安卓)MediaScannerService源码分析
- RxJava RxAndroid(安卓)在android中最重要的应用(一)
- Android学习七---Hello OpenCV samples
- Android(安卓)Suspend
- Android(安卓)Studio使用来自控制台的模拟器,/dev/kvm device: