android-自定义换肤(2)

android-自定义换肤(1) https://blog.csdn.net/tiangaopan/article/details/104895134

前面说到本质上是通过实现LayoutInflater.Factory2,在onCreateView()方法中进行处理

这就需要我们找到对应的控件,控件又有系统控件和自定义控件,这里我们先说系统控件,自定义后面会说处理方法

通过看源码,我们可以找到系统控件在这三个包下,

private static final String[] mClassPrefixList = {"android.widget.", "android.view.", "android.webkit.",}
 private static final Class[] mConstructorSignature = new Class[]{Context.class, AttributeSet.class};
private static final HashMap<String, Constructor<? extends View>> mConstructorMap = new HashMap<>();
 @Override
    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
        //反射获取
        View view = createViewFromTag(name, context, attrs);
        //自定义view
        if (view == null) {
            view = createView(name, context, attrs);
        }
        //筛选符合属性的attrs
        mSkinAttribute.load(view, attrs);
        return view;
    }
 private View createViewFromTag(String name, Context context, AttributeSet attrs) {
        //包含自定义控件
        if (-1 != name.indexOf(".")) {
            return null;
        }
        View view = null;
        for (String s : mClassPrefixList) {
            //mClassPrefixList[i] + name 组成全类名
            view = createView(s + name, context, attrs);
            if (view != null) {
                break;
            }
        }
        return view;
    }
private View createView(String name, Context context, AttributeSet attrs) {
        Constructor<? extends View> constructor = mConstructorMap.get(name);
        if (constructor == null) {
            try {
                //通过全类名拿到class
                Class<? extends View> aClass = context.getClassLoader().loadClass(name).asSubclass(View.class);
                //获取构造方法
                constructor = aClass.getConstructor(mConstructorSignature);
                mConstructorMap.put(name, constructor);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (constructor != null) {
            try {
                return constructor.newInstance(context, attrs);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return null;
    }

对于已经通过反射获取到构造方法的,可以使用map进行存储,没必要每次都进行一次获取 

public class SkinAttribute {

    private static final List<String> sAttribute = new ArrayList<>();

    static {
        sAttribute.add("background");
        sAttribute.add("src");

        sAttribute.add("textColor");
        sAttribute.add("drawableLeft");
        sAttribute.add("drawableTop");
        sAttribute.add("drawableRight");
        sAttribute.add("drawableBottom");
    }

    private List<SkinView> skinViews = new ArrayList<>();
    private Typeface mSkinTypeface;

    public SkinAttribute(Typeface skinTypeface) {
        mSkinTypeface = skinTypeface;
    }

    public void load(View view, AttributeSet attrs) {
        List<SkinPain> skinPains = new ArrayList<>();
        int attributeCount = attrs.getAttributeCount();
        for (int i = 0; i < attributeCount; i++) {
            //获取属性名字
            String attributeName = attrs.getAttributeName(i);
            if (sAttribute.contains(attributeName)) {
                //获取属性值
                String attributeValue = attrs.getAttributeValue(i);
                //写死的情况
                if (attributeValue.startsWith("#")) {
                    continue;
                }
                int resId;
                //系统自带的 attributeValue = "?1313123"
                if (attributeValue.startsWith("?")) {
                    int attrId = Integer.parseInt(attributeValue.substring(1));
                    //存在数组的可能
                    resId = SkinThemeUtils.getResId(view.getContext(), new int[]{attrId})[0];
                } else {
                    //attributeValue = "@21321213"
                    resId = Integer.parseInt(attributeValue.substring(1));
                }
                if (resId != 0) {
                    SkinPain skinPain = new SkinPain(attributeName, resId);
                    skinPains.add(skinPain);
                }
            }
        }
        if (!skinPains.isEmpty() || view instanceof TextView || view instanceof SkinViewSupport) {
            SkinView skinView = new SkinView(view, skinPains);
            skinView.applySkin(mSkinTypeface);
            skinViews.add(skinView);
        }
    }

    public void applySkin() {
        for (SkinView skinView : skinViews) {
            skinView.applySkin(mSkinTypeface);
        }
    }

    public void setTypeface(Typeface skinTypeface) {
        this.mSkinTypeface = skinTypeface;
    }


    static class SkinView {
        View view;
        List<SkinPain> skinPains;

        public SkinView(View view, List<SkinPain> skinPains) {
            this.view = view;
            this.skinPains = skinPains;
        }

        public void applySkin(Typeface typeface) {
            applyTypeface(typeface);
            applySkinSupport();
            for (SkinPain skinPair : skinPains) {
                Drawable left = null, top = null, right = null, bottom = null;
                switch (skinPair.attributeName) {
                    case "background":
                        Object background = SkinResources.getInstance().getBackground(skinPair.resId);
                        //Color
                        if (background instanceof Integer) {
                            view.setBackgroundColor((Integer) background);
                        } else {
                            ViewCompat.setBackground(view, (Drawable) background);
                        }
                        break;
                    case "src":
                        background = SkinResources.getInstance().getBackground(skinPair.resId);
                        if (background instanceof Integer) {
                            ((ImageView) view).setImageDrawable(new ColorDrawable((Integer)
                                    background));
                        } else {
                            ((ImageView) view).setImageDrawable((Drawable) background);
                        }
                        break;
                    case "textColor":
                        ((TextView) view).setTextColor(SkinResources.getInstance().getColorStateList
                                (skinPair.resId));
                        break;
                    case "drawableLeft":
                        left = SkinResources.getInstance().getDrawable(skinPair.resId);
                        break;
                    case "drawableTop":
                        top = SkinResources.getInstance().getDrawable(skinPair.resId);
                        break;
                    case "drawableRight":
                        right = SkinResources.getInstance().getDrawable(skinPair.resId);
                        break;
                    case "drawableBottom":
                        bottom = SkinResources.getInstance().getDrawable(skinPair.resId);
                        break;
                    default:
                        break;
                }
                if (null != left || null != right || null != top || null != bottom) {
                    ((TextView) view).setCompoundDrawablesWithIntrinsicBounds(left, top, right,
                            bottom);
                }
            }
        }

        //自定义view替换
        private void applySkinSupport() {
            if (view instanceof SkinViewSupport) {
                ((SkinViewSupport) view).applySkin();
            }
        }
        
        //字体换肤
        private void applyTypeface(Typeface typeface) {
            if (view instanceof TextView) {
                ((TextView) view).setTypeface(typeface);
            }
        }
    }

    static class SkinPain {
        String attributeName;
        int resId;

        public SkinPain(String attributeName, int resId) {
            this.attributeName = attributeName;
            this.resId = resId;
        }
    }
}

通过attributeName来知道是background,textcolor等属性,通过resid可以知道是资源名,这时候去获取资源包中的对应resid,即可实现相应的换肤

还有个需要注意的是,每个界面都应该进行相应的换肤,所以我们需要去实现Application.ActivityLifecycleCallbacks,这里去进行加载和移除

@Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        //更新状态栏
        SkinThemeUtils.updateStatusBarColor(activity);
        //更新字体
        Typeface skinTypeface = SkinThemeUtils.getSkinTypeface(activity);

        try {
            LayoutInflater layoutInflater = LayoutInflater.from(activity);

            Field mFactorySet = LayoutInflater.class.getDeclaredField("mFactorySet");
            mFactorySet.setAccessible(true);
            mFactorySet.setBoolean(layoutInflater, false);

            SkinLayoutFactory skinLayoutFactory = new SkinLayoutFactory(activity, skinTypeface);
            //添加自定义创建view 工厂
            layoutInflater.setFactory2(skinLayoutFactory);
            //注册观察者
            SkinManager.getInstance().addObserver(skinLayoutFactory);
            mFactoryMap.put(activity, skinLayoutFactory);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

在这里我们对对应的setFactory2进行替换,有个需要注意的是mFactorySet这个属性,如果是true的话会抛出异常,同时该变量是私有的,所以我们需要通过反射找到该变量将其更改为false

 public void setFactory2(Factory2 factory) {
        if (mFactorySet) {
            throw new IllegalStateException("A factory has already been set on this LayoutInflater");
        }
        if (factory == null) {
            throw new NullPointerException("Given factory can not be null");
        }
        mFactorySet = true;
        if (mFactory == null) {
            mFactory = mFactory2 = factory;
        } else {
            mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);
        }
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章