Android动态换肤

知识点:

  • 1.在Activity的OnCreate方法中会调用AppCompatDelegateImpl类的installViewFactory方法
    public void installViewFactory() {
        LayoutInflater layoutInflater = LayoutInflater.from(this.mContext);
        if (layoutInflater.getFactory() == null) {
            LayoutInflaterCompat.setFactory2(layoutInflater, this);
        } 
    }

AppCompatDelegateImpl类中实现了Factory2的接口,且其中只有一个方法

    public interface Factory2 extends Factory {
        public View onCreateView(View parent, String name, Context context, AttributeSet attrs);
    }

Factory2 实现方法上主要是创建了AppCompatViewInflater类和调用了mAppCompatViewInflater.createView方法

    public View createView(View parent, String name, @NonNull Context context, @NonNull AttributeSet attrs) {
        if (this.mAppCompatViewInflater == null) {
            this.mAppCompatViewInflater = new AppCompatViewInflater();
        }
          return this.mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext, IS_PRE_LOLLIPOP, true, VectorEnabledTintResources.shouldBeUsed());

mAppCompatViewInflater.createView中则是根据布局的标签创建出相应的基础控件

 final View createView(View parent, String name, @NonNull Context context, @NonNull AttributeSet attrs, boolean inheritContext, boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
        Context originalContext = context;

        View view = null;
		/**省略代码**/
        switch(var12) {
        case 0:
            view = this.createTextView(context, attrs);
            break;
        case 1:
            view = this.createImageView(context, attrs);
            break;
        case 2:
            view = this.createButton(context, attrs);
            break;
        case 3:
            view = this.createEditText(context, attrs);
            break;
        case 4:
            view = this.createSpinner(context, attrs);
            break;
        case 5:
            view = this.createImageButton(context, attrs);
            break;
        case 6:
            view = this.createCheckBox(context, attrs);
            break;
        case 7:
            view = this.createRadioButton(context, attrs);
            break;
        case 8:
            view = this.createCheckedTextView(context, attrs);
            break;
        case 9:
            view = this.createAutoCompleteTextView(context, attrs);
            break;
        case 10:
            view = this.createMultiAutoCompleteTextView(context, attrs);
            break;
        case 11:
            view = this.createRatingBar(context, attrs);
            break;
        case 12:
            view = this.createSeekBar(context, attrs);
            break;
        default:
            view = this.createView(context, name, attrs);
        }


        return (View)view;
    }
  • 2.Activity在setContentView时会使布局转换成XmlResourceParser
    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
        final XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }

之后会调用createViewFromTag方法生成相关的view

 View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
            boolean ignoreThemeAttr) {
            View view;
            if (mFactory2 != null) {
                view = mFactory2.onCreateView(parent, name, context, attrs);
            } else if (mFactory != null) {
                view = mFactory.onCreateView(name, context, attrs);
            } else {
                view = null;
            }
            if (view == null) {
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                    if (-1 == name.indexOf('.')) {
                        view = onCreateView(parent, name, attrs);
                    } else {
                        view = createView(name, null, attrs);
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;
                }
            }

            return view;
 }

这里优先是调用mFactory2.onCreateView生成控件,也就是知识点1设置的参数,其次是调用mFactory其实在设置mFactory2时默认也会给mFactory设值.注意若生成的View为空时则会调用自身的方法生成view。

换肤思路

由于Activity是实现了Factory的接口

public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback, WindowControllerCallback,
        AutofillManager.AutofillClient {

我们可以在程序调用SetFactory2方法前也就是super.onCreate(savedInstanceState)代码前进行Factory的初始化,通过知识点1的代码可知,若**layoutInflater.getFactory()**不为空 则再创建,所以 添加以下代码

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        LayoutInflater layoutInflater = LayoutInflater.from(this);
        LayoutInflaterCompat.setFactory2(layoutInflater, this);
        super.onCreate(savedInstanceState);
    }

然后实现OnCreateView方法

    public View onCreateView(String name, Context context, AttributeSet attrs) {
        if (viewInflater == null) {
            viewInflater = new CustomAppCompatViewInflater(context);
        }
        viewInflater.setName(name);
        viewInflater.setAttrs(attrs);
        return viewInflater.autoMatch();
    }

其中CustomAppCompatViewInflater是自定义类,继承与AppCompatViewInflater类,在这个类中调用autoMatch方法根据控件类型创建自定义类

 public View autoMatch() {
        View view = null;
        switch (name) {
            case "LinearLayout":
                view = new SkinnableLinearLayout(context, attrs);
                break;
            case "RelativeLayout":
//                view = new SkinnableRelativeLayout(context, attrs);
                break;
            case "TextView":
                view = new SkinnableTextView(context, attrs);
                break;
            case "Button":
//                view = new SkinnableButton(context, attrs);
                break;
            case "ImageView":
                view = new SkinnableImageView(context, attrs);
                break;
        }
        return view;
    }

其中自定义控件如下:

public class SkinnableTextView extends AppCompatTextView implements ViewsMatch {
    AttrsBean attrsBean;//存储控件规定属性的字段和值
    public SkinnableTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        attrsBean = new AttrsBean();
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SkinnableTextView, defStyleAttr, 0);
        attrsBean.saveViewResource(typedArray, R.styleable.SkinnableTextView);
        typedArray.recycle();
    }

    @Override
    public void skinnableView() {
        int key = R.styleable.SkinnableTextView[R.styleable.SkinnableTextView_android_background];
        int backgroundResourceId = attrsBean.getViewResource(key);
        if (backgroundResourceId > 0) {
            if (SkinManager.getInstance().isDefaultSkin()) {
                Drawable drawable = ContextCompat.getDrawable(getContext(), backgroundResourceId);
                setBackground(drawable);
            } else {
            //调用SkinManager获取皮肤包的资源
                final Object backgroundOrSrc = SkinManager.getInstance().getBackgroundOrSrc(backgroundResourceId);
                if (backgroundOrSrc instanceof Integer) {
                    int color = (int) backgroundOrSrc;
                    setBackgroundColor(color);
                } else {
                    setBackground((Drawable) backgroundOrSrc);
                }

            }
        }
        int textColorKey = R.styleable.SkinnableTextView[R.styleable.SkinnableTextView_android_textColor];
        int textColorResourceId = attrsBean.getViewResource(textColorKey);
        if (textColorResourceId > 0) {
            if (SkinManager.getInstance().isDefaultSkin()) {
                ColorStateList colorStateList = ContextCompat.getColorStateList(getContext(), textColorResourceId);
                setTextColor(colorStateList);
            } else {
                int color = SkinManager.getInstance().getColor(textColorResourceId);
                setTextColor(color);
            }
        }
    }
}

皮肤包代码:

  public void loaderSkinResources(String skinPath) {
        try {
            //创建资源管理器
            AssetManager assetManager = AssetManager.class.newInstance();
            Method declaredMethod = assetManager.getClass().getDeclaredMethod(ADD_ASSET_PATH, String.class);
            //设置私有方法可访问
            declaredMethod.setAccessible(true);
            //执行addAssetPath方法 加载资源包到本地应用
            declaredMethod.invoke(assetManager, skinPath);
            //创建加载外部皮肤包资源
            skinResources = new Resources(assetManager, appResources.getDisplayMetrics(), appResources.getConfiguration());
            //根据皮肤包文件获取包名
            skinPackageName = application.getPackageManager().getPackageArchiveInfo(skinPath, PackageManager.GET_ACTIVITIES)
                    .packageName;
            //无法获取皮肤包包名,加载内置资源
            isDefaultSkin = TextUtils.isEmpty(skinPackageName);

        } catch (Exception e) {
            e.printStackTrace();
            isDefaultSkin = true;
        }
    }

    /**
     * 通过id值获取资源的name和type
     */
    private int getSkinResourceIds(int resourceId) {
        if (isDefaultSkin) {
            return resourceId;
        }
        String resourceEntryName = appResources.getResourceEntryName(resourceId);//"music_bg"
        String resourceTypeName = appResources.getResourceTypeName(resourceId); //"drawable"
        //动态获取皮肤包的指定资源
        if (skinResources != null) {
            int skinResourceId = skinResources.getIdentifier(resourceEntryName, resourceTypeName, skinPackageName);
           // isDefaultSkin = skinResourceId == 0;
            return skinResourceId == 0 ? resourceId : skinResourceId;
        }
        return resourceId;
    }

    public boolean isDefaultSkin() {
        return isDefaultSkin;
    }

    public int getColor(int resourceId) {
        int ids = getSkinResourceIds(resourceId);
        return isDefaultSkin ? appResources.getColor(ids) : skinResources.getColor(ids);
    }

    public Drawable getDrawableOrMipMap(int resourceId) {
        int ids = getSkinResourceIds(resourceId);
        return isDefaultSkin ? appResources.getDrawable(ids) : skinResources.getDrawable(ids);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值