2.1.2 Android动态换肤

首先代码基于上一节的内容来实现的,先看一下效果:

看一下代码的目录结构:

ISkinable:

/**
 * @author Eason
 * @createtime 2020/3/19
 * @desc 皮肤切换的接口,所有需要修改日/夜间模式的都要实现这个接口
 */
public interface ISkinable {
    //在接口中的变量都默认是public static final的全局常量
    int[] mAttrs = R.styleable.SkinAllAttr;

    /**
     * 切换模式
     */
    void changeSkin();
}
    public class MyAppCompatViewInflater extends AppCompatViewInflater {
    private Context mContext;

    public MyAppCompatViewInflater(Context context) {
        this.mContext = context;
    }

    /**
     * 自动匹配控件名,并初始化控件对象
     */
    public View generateView(String name, AttributeSet attrs) {
        View view;
        switch (name) {
            case "TextView":
                view = new SkinTextView(mContext, attrs);
                verifyNotNull(view, name);
                break;
            case "Button":
                view = new SkinButton(mContext, attrs);
                verifyNotNull(view, name);
                break;
            case "ImageView":
                view = new SkinImageView(mContext, attrs);
                verifyNotNull(view, name);
                break;
            case "androidx.constraintlayout.widget.ConstraintLayout":
                view = new SkinConstraintLayout(mContext, attrs);
                verifyNotNull(view, name);
                break;
            case "RelativeLayout":
                view = new SkinRelativeLayout(mContext, attrs);
                verifyNotNull(view, name);
                break;
            case "LinearLayout":
                view = new SkinLinearLayout(mContext, attrs);
                verifyNotNull(view, name);
                break;
            //todo 继续添加如 FrameLayout、自定义View...
            default:
                view = super.createView(mContext, name, attrs);
        }
        return view;
    }

    /**
     * 校验控件不为空(源码方法,由于private修饰,只能复制过来了。为了代码健壮,可有可无)
     *
     * @param view 被校验控件,如:AppCompatTextView extends TextView(v7兼容包,兼容是重点!!!)
     * @param name 控件名,如:"ImageView"
     */
    private void verifyNotNull(View view, String name) {
        if (view == null) {
            throw new IllegalStateException(this.getClass().getName()
                    + " asked to inflate view for <" + name + ">, but returned null");
        }
    }
}

ActionBarUtils:

public class ActionBarUtils {

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public static void setColor(AppCompatActivity activity) {
        TypedArray a = activity.getTheme().obtainStyledAttributes(0, new int[]{
                android.R.attr.colorPrimary
        });
        int color = a.getColor(0, 0);
        a.recycle();
        ActionBar actionBar = activity.getSupportActionBar();
        if (actionBar != null) {
            actionBar.setBackgroundDrawable(new ColorDrawable(color));
        }
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public static void setColor(AppCompatActivity activity, int skinColor) {
        ActionBar actionBar = activity.getSupportActionBar();
        if (actionBar != null) {
            actionBar.setBackgroundDrawable(new ColorDrawable(skinColor));
        }
    }
}

SkinButton:

/**
 * 继承TextView兼容包,9.0源码中也是如此
 * 参考:AppCompatViewInflater.java
 * 86行 + 138行 + 206行
 */
public class SkinButton extends AppCompatButton implements ISkinable {

    private SparseIntArray mResourceMap = new SparseIntArray();

    public SkinButton(Context context) {
        this(context, null);
    }

    public SkinButton(Context context, AttributeSet attrs) {
        //TODO 这里需要更改
        this(context, attrs, android.R.attr.buttonStyle);
    }

    public SkinButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // 根据自定义属性,匹配控件属性的类型集合,如:background + textColor
        TypedArray a = context.obtainStyledAttributes(attrs, mAttrs, defStyleAttr, 0);
        //R.styleable.SkinTextView 为在attrs定义的属性值数组
        for (int i = 0; i < mAttrs.length; i++) {
            mResourceMap.put(mAttrs[i], a.getResourceId(i, -1));
        }
        a.recycle();
    }

    @Override
    public void changeSkin() {
        //设置background
        setBackground();

        //设置textColor
        setTextColor();

        //设置字体
        setTextFont();
    }

    /**
     * 设置字体
     */
    private void setTextFont() {
        if (SkinManager.getInstance().isOpenFontChange()) {
            int textFontId = mAttrs[R.styleable.SkinAllAttr_custom_typeface];
            int textTypefaceResourceId = mResourceMap.get(textFontId);
            //有一种情况是,如果在宿主app内没有定义custom_typeface这个字段,则textTypefaceResourceId返回-1,字体不生效
            if (textTypefaceResourceId > 0) {
                if (SkinManager.getInstance().isLoadDefault()) {
                    setTypeface(Typeface.DEFAULT);
                } else {
                    setTypeface(SkinManager.getInstance().getTypeface(textTypefaceResourceId));
                }
            }
        }
    }

    /**
     * 设置字体颜色
     */
    private void setTextColor() {
        int textColorKey = mAttrs[R.styleable.SkinAllAttr_android_textColor];
        int textColorId = mResourceMap.get(textColorKey);
        if (textColorId > 0) {
            if (SkinManager.getInstance().isLoadDefault()) {
                setTextColor(ContextCompat.getColorStateList(getContext(), textColorId));
            } else {
                setTextColor(SkinManager.getInstance().getColor(textColorId));//注意这里返回是直接可以使用的
            }
        }
    }

    /**
     * 设置背景
     */
    private void setBackground() {
        int backgroundKey = mAttrs[R.styleable.SkinAllAttr_android_background];
        int backgroundId = mResourceMap.get(backgroundKey);
        if (backgroundId > 0) {
            if (SkinManager.getInstance().isLoadDefault()) {
                setBackgroundDrawable(ContextCompat.getDrawable(getContext(), backgroundId))
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值