腾讯新闻Android高工二面:说说Android动态换肤实现原理吧

换肤分为动态换肤和静态换肤

静态换肤

这种换肤的方式,也就是我们所说的内置换肤,就是在APP内部放置多套相同的资源。进行资源的切换。
这种换肤的方式有很多缺点,比如, 灵活性差,只能更换内置的资源、apk体积太大,在我们的应用Apk中等一般图片文件能占到apk大小的一半左右。
当然了,这种方式也并不是一无是处, 比如我们的应用内,只是普通的 日夜间模式 的切换,并不需要图片等的更换,只是更换颜色,那这样的方式就很实用。

动态换肤

适用于大量皮肤,用户选择下载,像QQ、网易云音乐这种。它是将皮肤包下载到本地,皮肤包其实是个APK。

换肤包括替换图片资源、布局颜色、字体、文字颜色、状态栏和导航栏颜色。

动态换肤步骤包括:

  • 采集需要换肤的控件
  • 加载皮肤包
  • 替换资源

实现原理

首先Activity的onCreate()方法里面我们都要去调用setContentView(int id) 来指定当前Activity的布局文件:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

再往里看:

    @Override
    public void setContentView(int resId) {
        ensureSubDecor();
        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        LayoutInflater.from(mContext).inflate(resId, contentParent);//这里实现view布局的加载
        mOriginalWindowCallback.onContentChanged();
    }
    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
        return inflate(resource, root, root != null);
    }
    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
        if (DEBUG) {
            Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
                    + Integer.toHexString(resource) + ")");
        }

        final XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }
 public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
 			...
            final String name = parser.getName();
            final View temp = createViewFromTag(root, name, inflaterContext, attrs);
            ...
            return temp;
    }

可以看到inflate会返回具体的View对象出去,那么我们的关注焦点就放在createViewFromTag中了

    /**
     * Creates a view from a tag name using the supplied attribute set.
     * <p>
     * <strong>Note:</strong> Default visibility so the BridgeInflater can
     * override it.
     *
     * @param parent the parent view, used to inflate layout params
     * @param name the name of the XML tag used to define the view
     * @param context the inflation context for the view, typically the
     *                {@code parent} or base layout inflater context
     * @param attrs the attribute set for the XML tag used to define the view
     * @param ignoreThemeAttr {@code true} to ignore the {@code android:theme}
     *                        attribute (if set) for the view being inflated,
     *                        {@code false} otherwise
     */
    View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
            boolean ignoreThemeAttr) {
        try {
            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;
            }
            return view;
        
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值