Android App换肤实现 - 准备工作

2020-06-19  本文已影响0人  小名坎坎
从网易云音乐换肤开始

Android 模拟器或者Android手机(需要root权限)

  1. 安装网易云音乐并选择皮肤进行下载换肤

  2. 命令窗口中进入data/data/com.netease.cloudmusic/files/theme文件夹 皮肤包
  3. adb拉取文件到电脑端,改为zip文件解压 皮肤包

    从上述步骤我们可以看出,网易云音乐的皮肤包就是一个Apk资源包。我们的皮肤包也按照这种方式。

寻找切入点 - 从Avtivity的setContentView()开始
 public void setContentView(@LayoutRes int layoutResID) {
        //调用了window的->所以来到PhoneWindow的查找
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }
 public void setContentView(int layoutResID) {
 /*----------------------------省略------------------------------------*/
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
           // 调用了inflate方法加载资源
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
   /*----------------------------省略------------------------------------*/
    }
 public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
      //如果存在预编译,则可以通过tryInflatePrecompiled获取到View
        View view = tryInflatePrecompiled(resource, res, root, attachToRoot);
        if (view != null) {
            return view;
        }
// 获取xml解析器
        XmlResourceParser parser = res.getLayout(resource);
        try {
// 调用这个方法来加载view
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }
 public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
  /*----------------------------省略------------------------------------*/
                // 遍历获得根节点
                advanceToRootNode(parser);
                // 获得根节点名称
                final String name = parser.getName();
                //  == merge
                if (TAG_MERGE.equals(name)) {
                    if (root == null || !attachToRoot) {
                     rInflate(parser, root, inflaterContext, attrs, false);
                } else {
                      // 通过createViewFromTag创建View
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);
                }
        }
 /*----------------------------省略------------------------------------*/
    }
   View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,boolean ignoreThemeAttr) {
 /*----------------------------省略------------------------------------*/
// 通过这个方法来创建view
  View view = tryCreateView(parent, name, context, attrs);
// 如果view为null的话,那么才会调用LayoutInflater的onCreateView方法或着createView方法
            if (view == null) {
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                    // 自定义控件 / 系统控件
                    if (-1 == name.indexOf('.')) {
                        view = onCreateView(context, parent, name, attrs);
                    } else {
                        view = createView(context, name, null, attrs);
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;
                }
            }
 /*----------------------------省略------------------------------------*/
  }
 public final View tryCreateView(@Nullable View parent, @NonNull String name,@NonNull Context context,@NonNull AttributeSet attrs) {
// 调用Factory的onCreateView来实现创建
        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 && mPrivateFactory != null) {
            view = mPrivateFactory.onCreateView(parent, name, context, attrs);
        }
        return view;
    }

看到这里,我们考虑能不能我们实现一个Factory然后赋值进去,然后我们自己就能控制View的创建。
准备工作完成,下一篇实现颜色、图片等替换

上一篇 下一篇

猜你喜欢

热点阅读