Appcompat兼容性的处理


了解了下AppcompatActivity的中的兼容性的处理

AppCompat兼容性

控件的兼容性 v4 v7 v13 数字代表的是支持的api 的version
AppCompatActivity 是如何做到兼容性的
原理: 继承了AppCompatActivity 的Activity 都会在解析XML 的时候,将xml里面所有的系统控件转换为 appCompatButton。它的源码是怎样的呢? 所谓的兼容,就是一个着色问题!!!
AppCompatDelegate 的工作就是涂色。 替换:widget着色是通过这个widget 的layout 在inflation 的时候,被AppCompatDelegate 拦截下来,然后根据 控件的名字,强制被系统转换成为
以AppCompat 开头的控件。

1.首先查看继承的关系

public class AppCompatActivity extends FragmentActivity implements AppCompatCallback,
        TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider {
public class FragmentActivity extends BaseFragmentActivityJB implements
        ActivityCompat.OnRequestPermissionsResultCallback,
        ActivityCompatApi23.RequestPermissionsRequestCodeValidator {
abstract class BaseFragmentActivityJB extends BaseFragmentActivityHoneycomb {
abstract class BaseFragmentActivityHoneycomb extends BaseFragmentActivityGingerbread {
abstract class BaseFragmentActivityGingerbread extends SupportActivity {
public class SupportActivity extends Activity {

2.AppCompatActivity 中代理的实现

  @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        //获取到代理的实例对象
        final AppCompatDelegate delegate = getDelegate();
        delegate.installViewFactory();
        delegate.onCreate(savedInstanceState);
        if (delegate.applyDayNight() && mThemeId != 0) {
            // If DayNight has been applied, we need to re-apply the theme for
            // the changes to take effect. On API 23+, we should bypass
            // setTheme(), which will no-op if the theme ID is identical to the
            // current theme ID.
            if (Build.VERSION.SDK_INT >= 23) {
                onApplyThemeResource(getTheme(), mThemeId, false);
            } else {
                setTheme(mThemeId);
            }
        }
        super.onCreate(savedInstanceState);
    }
     /**
     * @return The {@link AppCompatDelegate} being used by this Activity.
     */
    @NonNull
    public AppCompatDelegate getDelegate() {
        if (mDelegate == null) {
            mDelegate = AppCompatDelegate.create(this, this);
        }
        return mDelegate;
    }
    private static AppCompatDelegate create(Context context, Window window,
            AppCompatCallback callback) {
    //这边会根据不同的系统的版本号得到不同的代理对象,但是其他的本质都是直接,或者间接的继承AppCompatDelegateImplV9的s
        final int sdk = Build.VERSION.SDK_INT;
        if (BuildCompat.isAtLeastN()) {
            return new AppCompatDelegateImplN(context, window, callback);
        } else if (sdk >= 23) {
            return new AppCompatDelegateImplV23(context, window, callback);
        } else if (sdk >= 14) {
            return new AppCompatDelegateImplV14(context, window, callback);
        } else if (sdk >= 11) {
            return new AppCompatDelegateImplV11(context, window, callback);
        } else {
            return new AppCompatDelegateImplV9(context, window, callback);
        }
    }
    //比如AppCompatDelegateImplN  中的继承关系为
    @RequiresApi(24)
    @TargetApi(24)
    class AppCompatDelegateImplN extends AppCompatDelegateImplV23
    @RequiresApi(23)
    @TargetApi(23)
    class AppCompatDelegateImplV23 extends AppCompatDelegateImplV14 
    @RequiresApi(14)
    @TargetApi(14)
    class AppCompatDelegateImplV14 extends AppCompatDelegateImplV11
    @RequiresApi(11)
    @TargetApi(11)
    //当我们在我们的代码中执行了下面的代码的时候
     @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    //执行  setContentView(R.layout.activity_main); 的时候
    @Override
    public void setContentView(@LayoutRes int layoutResID) {
        getDelegate().setContentView(layoutResID);
    }
    //执行getDelegate().setContentView(layoutResID);的时候,就会回调到AppCompatDelegateImplV9中的
    @Override
    public void setContentView(int resId) {
        ensureSubDecor();
        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        //这边会采用布局加载器来加载布局,这是因为下面为  LayoutInflaterCompat.setFactory(layoutInflater, this);
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        mOriginalWindowCallback.onContentChanged();
    }
    class AppCompatDelegateImplV11 extends AppCompatDelegateImplV9
    //AppCompatDelegateImplV9 中部分的代码,这个类为public abstract class AppCompatDelegat中的抽象方法的实现
    @Override
    public void installViewFactory() {
        LayoutInflater layoutInflater = LayoutInflater.from(mContext);
        if (layoutInflater.getFactory() == null) {
            //设置了LayoutInflaterFactory 的回调this
            LayoutInflaterCompat.setFactory(layoutInflater, this);
        } else {
            if (!(LayoutInflaterCompat.getFactory(layoutInflater)
                    instanceof AppCompatDelegateImplV9)) {
                Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed"
                        + " so we can not install AppCompat's");
            }
        }
    }
    //这个方法的回调onCreateView 为public interface LayoutInflaterFactory接口的实现
     /**
     * 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);
    }
    //当执行到createView(parent, name, context, attrs);的时候
    @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 */
        );
    }
    //当执行到 mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext的时候
    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);
        }
        //name为在布局里面设置的TextView,ImageView,这边就会根据这些名字对应的生成Appcompat对应的控件,这里就是适配的实现,可以在不同的版本下,内容的体现是一样
        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;
    }

LinearLayoutCompat

  • 这个类是干什么的,是控件还是组件,看他的初始化在干什么
  • 找到入口,一般是构造器
  • 找关键方法:onMeasure onLayout onDraw
  • onMeasure 是 计算子控件的大小,同时计算自己的大小。自己的宽高是由子空间的宽高决定的,根据摆放的方向不同而算法不同
  • onLayout 是将子控件的上下左右位置进行确定。然后布局到layout上面
  • onDraw 只用来画layout 自己的分割线,其他的内容由具体的item来绘制

    @Override
    protected void onDraw(Canvas canvas) {
         if (mDivider == null) {
             return;
         }
    
         if (mOrientation == VERTICAL) {
             drawDividersVertical(canvas);
         } else {
             drawDividersHorizontal(canvas);
         }
     }
    
    void drawDividersVertical(Canvas canvas) {
         final int count = getVirtualChildCount();
         for (int i = 0; i < count; i++) {
             final View child = getVirtualChildAt(i);
    
             if (child != null && child.getVisibility() != GONE) {
                 if (hasDividerBeforeChildAt(i)) {
                     final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                     final int top = child.getTop() - lp.topMargin - mDividerHeight;
                     drawHorizontalDivider(canvas, top);
                 }
             }
         }
    
         if (hasDividerBeforeChildAt(count)) {
             final View child = getVirtualChildAt(count - 1);
             int bottom = 0;
             if (child == null) {
                 bottom = getHeight() - getPaddingBottom() - mDividerHeight;
             } else {
                 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                 bottom = child.getBottom() + lp.bottomMargin;
             }
             drawHorizontalDivider(canvas, bottom);
         }
     }
     void drawHorizontalDivider(Canvas canvas, int top) {
         mDivider.setBounds(getPaddingLeft() + mDividerPadding, top,
                 getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight);
         mDivider.draw(canvas);
     }
    
     void drawVerticalDivider(Canvas canvas, int left) {
         mDivider.setBounds(left, getPaddingTop() + mDividerPadding,
                 left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding);
         mDivider.draw(canvas);
     }
    

文章作者: AheadSnail
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 AheadSnail !
评论
  目录