Android 切换主题 (二)

背景

我原来写过一篇文章关于 android 切换主题的文章 -- Android 切换主题以及换肤的实现 , 里面介绍了如何使用 setTheme() 来切换主题,只不过使用这个函数有个缺点那就是你必须得重新启动当前的 Activity 才能生效!那么问题来了,有没有方法保证我们既使用了 setTheme() 又能不重启当前的 Activity 呢?我告诉这是有的,下面我就是详细介绍这个方法 (参考 github 上的一个开源项目进行介绍!文末给出这个项目的地址)。

===

原理

众所周知,setTheme() 之后需要重启当前的 Activity 的原因是:它要重新渲染当前的 当前的 ViewTree 。所以现在我们的做法就是我们来自己给他渲染不就行了!那样的话,就不需要重启当前的 Activity 了!下面我们就来看代码吧!

===

代码

代码的实现核心就是:在用户调用 setTheme() 之后,我们获取当前的 Theme ,之后我们在从中获取到我们用的属性,之后在设置到对应的控件上,这样就ok了!

第一步

我们先定义个接口:

public interface ColorUiInterface {    public View getView();    public void setTheme(Resources.Theme themeId);}

这样的话我们就可以重写所有的控件,让他继承该接口,并实现对应的函数,那么在 setTheme() 之后就可以直接调用每个控件都有的 setTheme() 方法了!

第二步

现在我们就来实现一个自定义的view

public class ColorTextView extends TextView implements ColorUiInterface {    private int attr_drawable = -1;    private int attr_textAppearance = -1;    private int attr_textColor = -1;    public ColorTextView(Context context) {        super(context);    }    public ColorTextView(Context context, AttributeSet attrs) {        super(context, attrs);        this.attr_drawable = ViewAttributeUtil.getBackgroundAttibute(attrs);        this.attr_textColor = ViewAttributeUtil.getTextColorAttribute(attrs);    }    public ColorTextView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        this.attr_drawable = ViewAttributeUtil.getBackgroundAttibute(attrs);        this.attr_textColor = ViewAttributeUtil.getTextColorAttribute(attrs);    }    @Override    public View getView() {        return this;    }    @Override    public void setTheme(Resources.Theme themeId) {        if (attr_drawable != -1) {            ViewAttributeUtil.applyBackgroundDrawable(this, themeId, attr_drawable);        }        if (attr_textColor != -1) {            ViewAttributeUtil.applyTextColor(this, themeId, attr_textColor);        }    }}

从以上代码中我们可以看到,我首先获取了一下一些常用的需要换肤的要素,比如:背景色,字体颜色,当让还有其他的,这个随用户定制!当然读者也可以实现其他的自定义 view 。

第三步

现在我们来看下 ViewAttributeUtil 这个类的具体实现!

public class ViewAttributeUtil {  public static int getAttributeValue(AttributeSet attr, int paramInt) {    int value = -1;    int count = attr.getAttributeCount();    for(int i = 0; i 

这个类比较简单,就是根据对应的 themeid 得到对应 themeid 的值!好了目前为止,我们还差一步就是,当我们调用了 Activity 的 setTheme() 方法之后,重新设置到对应的view就行了!

第四步

直接上代码

public class ColorUiUtil {/** * 切换应用主题 * * @param rootView */public static void changeTheme(View rootView, Resources.Theme theme) {    if (rootView instanceof ColorUiInterface) {        ((ColorUiInterface) rootView).setTheme(theme);        if (rootView instanceof ViewGroup) {            int count = ((ViewGroup) rootView).getChildCount();            for (int i = 0; i < count; i++) {                changeTheme(((ViewGroup) rootView).getChildAt(i), theme);            }        }        if (rootView instanceof AbsListView) {            try {                Field localField = AbsListView.class.getDeclaredField("mRecycler");                localField.setAccessible(true);                Method localMethod = Class.forName("android.widget.AbsListView$RecycleBin").getDeclaredMethod("clear", new Class[0]);                localMethod.setAccessible(true);                localMethod.invoke(localField.get(rootView), new Object[0]);            } catch (NoSuchFieldException e1) {                e1.printStackTrace();            } catch (ClassNotFoundException e2) {                e2.printStackTrace();            } catch (NoSuchMethodException e3) {                e3.printStackTrace();            } catch (IllegalAccessException e4) {                e4.printStackTrace();            } catch (InvocationTargetException e5) {                e5.printStackTrace();            }        }    } else {        if (rootView instanceof ViewGroup) {            int count = ((ViewGroup) rootView).getChildCount();            for (int i = 0; i < count; i++) {                changeTheme(((ViewGroup) rootView).getChildAt(i), theme);            }        }        if (rootView instanceof AbsListView) {            try {                Field localField = AbsListView.class.getDeclaredField("mRecycler");                localField.setAccessible(true);                Method localMethod = Class.forName("android.widget.AbsListView$RecycleBin").getDeclaredMethod("clear", new Class[0]);                localMethod.setAccessible(true);                localMethod.invoke(localField.get(rootView), new Object[0]);            } catch (NoSuchFieldException e1) {                e1.printStackTrace();            } catch (ClassNotFoundException e2) {                e2.printStackTrace();            } catch (NoSuchMethodException e3) {                e3.printStackTrace();            } catch (IllegalAccessException e4) {                e4.printStackTrace();            } catch (InvocationTargetException e5) {                e5.printStackTrace();            }        }    }}}

代码很简单,就是用了递归,从当前的 ViewTree 中找到 继承了我们在第一步定义的那个接口,之后再调用其 setTheme() 方法就行了!!

最后一步

那么我们现在来看下如何在 Activity 中是如何调用的!

   setTheme(R.style.theme_1);   ColorUiUtil.changeTheme(rootView, getTheme());       

是不是很简单呀!

总结

这个方法我感觉蛮好的,要说唯一的缺点吧!就是使用了递归!有点耗性能!但是这个我估计是可以忽略不计的!!

*** 现在给出这个开源项目的地址 MultipleTheme ***

更多相关文章

  1. Android:你要的WebView与 JS 交互方式 都在这里了
  2. android之eclipse下查看android系统源代码
  3. Android(安卓)实现文件上传功能(upload)
  4. Android从代码到apk的编译过程
  5. Android(安卓)4.0 Space和Gridlayout
  6. Android高手进阶教程(十七)之---Android中Intent传递对象的两种
  7. Android的selector背景选择器
  8. 【阿里云镜像】切换阿里巴巴开源镜像站镜像——Fedora镜像
  9. 【阿里云镜像】切换阿里巴巴开源镜像站镜像——Debian镜像

随机推荐

  1. 前端开发零基础好学吗?
  2. 技术分享:Java多线程学习处理高并发问题
  3. Phoenix实践 | Phoenix数据迁移
  4. 关于HashMap的一些思考
  5. SpringBoot+Vue前后端分离项目实战-cms项
  6. 五款Java开发工具,总有你用的上的
  7. HBase分享 | 云HBase之OpenTSDB时序引擎
  8. 技术分享:如何避免ajax重复请求?
  9. HBase分享 | 基于HBase和Spark构建企业级
  10. SumSwap震撼开启!势必能Uniswap和Sushiswa