package skin.support.app; import android.content.Context; import android.os.Build; import android.support.annotation.NonNull; import android.support.v4.view.LayoutInflaterFactory; import android.support.v4.view.ViewCompat; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.VectorEnabledTintResources; import android.util.AttributeSet; import android.view.View; import android.view.ViewParent; import java.util.ArrayList; import java.util.List; import skin.support.utils.SkinLog; import skin.support.widget.SkinCompatSupportable; /** * Created by ximsfei on 2017/1/9. */ public class SkinCompatDelegate implements LayoutInflaterFactory { private final AppCompatActivity mAppCompatActivity; private SkinCompatViewInflater mSkinCompatViewInflater; private List<SkinCompatSupportable> mSkinHelpers = new ArrayList<>(); private SkinCompatDelegate(AppCompatActivity appCompatActivity) { mAppCompatActivity = appCompatActivity; } @Override public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { View view = createView(parent, name, context, attrs); if (view == null) { return null; } if (view instanceof SkinCompatSupportable) { mSkinHelpers.add((SkinCompatSupportable) view); } return view; } public View createView(View parent, final String name, @NonNull Context context, @NonNull AttributeSet attrs) { final boolean isPre21 = Build.VERSION.SDK_INT < 21; if (mSkinCompatViewInflater == null) { mSkinCompatViewInflater = new SkinCompatViewInflater(); } // We only want the View to inherit its context if we're running pre-v21 final boolean inheritContext = isPre21 && shouldInheritContext((ViewParent) parent); return mSkinCompatViewInflater.createView(parent, name, context, attrs, inheritContext, isPre21, /* 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 */ ); } private boolean shouldInheritContext(ViewParent parent) { if (parent == null) { // The initial parent is null so just return false return false; } final View windowDecor = mAppCompatActivity.getWindow().getDecorView(); while (true) { if (parent == null) { // Bingo. We've hit a view which has a null parent before being terminated from // the loop. This is (most probably) because it's the root view in an inflation // call, therefore we should inherit. This works as the inflated layout is only // added to the hierarchy at the end of the inflate() call. return true; } else if (parent == windowDecor || !(parent instanceof View) || ViewCompat.isAttachedToWindow((View) parent)) { // We have either hit the window's decor view, a parent which isn't a View // (i.e. ViewRootImpl), or an attached view, so we know that the original parent // is currently added to the view hierarchy. This means that it has not be // inflated in the current inflate() call and we should not inherit the context. return false; } parent = parent.getParent(); } } public static SkinCompatDelegate create(AppCompatActivity appCompatActivity) { return new SkinCompatDelegate(appCompatActivity); } public void applySkin() { if (mSkinHelpers != null || !mSkinHelpers.isEmpty()) { SkinLog.d("size - " + mSkinHelpers.size()); for (SkinCompatSupportable helper : mSkinHelpers) { helper.applySkin(); } } } }