Android 使用 hook技术实现一键换肤

简言:

工作中我们经常会收到产品经理提出手机需要有更换皮肤的功能,现在市面上大多数都是以主题换肤为主的,今天我这里介绍另一种,利用hook技术来实现一键换肤功能。

hook简介:
Hook翻译过来是钩子的意思,我们都知道无论是手机还是电脑运行的时候都依赖系统各种各样的API,当某些API不能满足我们的要求时,我们就得去修改某些api,使之能满足我们的要求。这样api hook就自然而然的出现了。我们可以通过api hook,改变一个系统api的原有功能。基本的方法就是通过hook“接触”到需要修改的api函数入口点,改变它的地址指向新的自定义的函数。当然这种技术同样适用于Android系统,在Android开发中,我们同样能利用Hook的原理让系统某些方法运行时调用的是我们定义的方法,从而满足我们的要求。

哪些资源可以替换呢?
我们可以替换的资源有动画、背景图片、字体、字体颜色、字体大小、音频、视频等,总的来说,res目录下的所有资源都可以被替换掉。

换肤的步骤
在这里插入图片描述
Android是如何实例化布局的呢?
详细的流程这篇文章已经写的很详细了Android布局加载流程 这里就不做多的探讨了。下面直接到我们的主题。

实现一键换肤
第一步:创建一个SkinFactory类让它继承LayoutInflater.Factory2,该类的功能是:1、拦截系统创建view的过程,由我们自己创建view;2、收集需要换肤的view;3、换肤操作。下面贴出源码:

public class SkinFactory implements LayoutInflater.Factory2 {

    private AppCompatDelegate mDelegate;//预定义一个委托类,它负责按照系统的原有逻辑来创建view

    private List<SkinView> listCacheSkinView = new ArrayList<>();//我自定义的list,缓存所有可以换肤的View对象

    /**
     * 给外部提供一个set方法
     *
     * @param mDelegate
     */
    public void setDelegate(AppCompatDelegate mDelegate) {
        this.mDelegate = mDelegate;
    }


    /**
     * Factory2 是继承Factory的,所以,我们这次是主要重写Factory的onCreateView逻辑,就不必理会Factory的重写方法了
     *
     * @param name
     * @param context
     * @param attrs
     * @return
     */
    @Override
    public View onCreateView(String name, Context context, AttributeSet attrs) {
        return null;
    }

    /**
     * @param parent
     * @param name
     * @param context
     * @param attrs
     * @return
     */
    @Override
    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {

        // TODO: 关键点1:执行系统代码里的创建View的过程,我们只是想加入自己的思想,并不是要全盘接管
        View view = mDelegate.createView(parent, name, context, attrs);//系统创建出来的时候有可能为空,你问为啥?请全文搜索 “标记标记,因为” 你会找到你要的答案
        if (view == null) {//万一系统创建出来是空,那么我们来补救
            mConstructorArgs[0] = context;
            try {
                if (-1 == name.indexOf('.')) {//不包含. 说明不带包名,那么我们帮他加上包名
                    view = createViewByPrefix(context, name, prefixs, attrs);
                } else {//包含. 说明 是权限定名的view name,
                    view = createViewByPrefix(context, name, null, attrs);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        //TODO: 关键点2 收集需要换肤的View
        collectSkinView(context, attrs, view);

        return view;
    }

    /**
     * TODO: 收集需要换肤的控件
     * 收集的方式是:通过自定义属性isSupport,从创建出来的很多View中,找到支持换肤的那些,保存到map中
     */
    private void collectSkinView(Context context, AttributeSet attrs, View view) {
        // 获取我们自己定义的属性
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Skinable);
        boolean isSupport = a.getBoolean(R.styleable.Skinable_isSupport, false);
        if (isSupport) {//找到支持换肤的view
            final int Len = attrs.getAttributeCount();
            HashMap<String, String> a
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值