Android 切换主题 (二)
背景
我原来写过一篇文章关于 android 切换主题的文章 -- Android 切换主题以及换肤的实现 , 里面介绍了如何使用 setTheme() 来切换主题,只不过使用这个函数有个缺点那就是你必须得重新启动当前的 Activity 才能生效!那么问题来了,有没有方法保证我们既使用了 setTheme() 又能不重启当前的 Activity 呢?我告诉这是有的,下面我就是详细介绍这个方法 (参考 github 上的一个开源项目进行介绍!文末给出这个项目的地址)。
===
原理
众所周知,setTheme() 之后需要重启当前的 Activity 的原因是:它要重新渲染当前的 当前的 ViewTree 。所以现在我们的做法就是我们来自己给他渲染不就行了!那样的话,就不需要重启当前的 Activity 了!下面我们就来看代码吧!
===
代码
代码的实现核心就是:在用户调用 setTheme() 之后,我们获取当前的 Theme ,之后我们在从中获取到我们用的属性,之后在设置到对应的控件上,这样就ok了!
第一步
我们先定义个接口:
publicinterfaceColorUiInterface{publicViewgetView();publicvoidsetTheme(Resources.Theme themeId);}
这样的话我们就可以重写所有的控件,让他继承该接口,并实现对应的函数,那么在 setTheme() 之后就可以直接调用每个控件都有的 setTheme() 方法了!
第二步
现在我们就来实现一个自定义的view
publicclassColorTextViewextendsTextViewimplementsColorUiInterface{privateintattr_drawable = -1;privateintattr_textAppearance = -1;privateintattr_textColor = -1;publicColorTextView(Context context){super(context); }publicColorTextView(Context context, AttributeSet attrs){super(context, attrs);this.attr_drawable = ViewAttributeUtil.getBackgroundAttibute(attrs);this.attr_textColor = ViewAttributeUtil.getTextColorAttribute(attrs); }publicColorTextView(Context context, AttributeSet attrs,intdefStyleAttr){super(context, attrs, defStyleAttr);this.attr_drawable = ViewAttributeUtil.getBackgroundAttibute(attrs);this.attr_textColor = ViewAttributeUtil.getTextColorAttribute(attrs); }@OverridepublicViewgetView(){returnthis; }@OverridepublicvoidsetTheme(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 这个类的具体实现!
publicclassViewAttributeUtil{publicstaticintgetAttributeValue(AttributeSet attr,intparamInt){intvalue=-1;intcount = attr.getAttributeCount();for(inti =0; i
这个类比较简单,就是根据对应的 themeid 得到对应 themeid 的值!好了目前为止,我们还差一步就是,当我们调用了 Activity 的 setTheme() 方法之后,重新设置到对应的view就行了!
第四步
直接上代码
publicclassColorUiUtil{/** * 切换应用主题 * *@paramrootView */publicstaticvoidchangeTheme(View rootView, Resources.Theme theme) {if(rootViewinstanceofColorUiInterface) { ((ColorUiInterface) rootView).setTheme(theme);if(rootViewinstanceofViewGroup) {intcount = ((ViewGroup) rootView).getChildCount();for(inti =0; i < count; i++) { changeTheme(((ViewGroup) rootView).getChildAt(i), theme); } }if(rootViewinstanceofAbsListView) {try{ Field localField = AbsListView.class.getDeclaredField("mRecycler"); localField.setAccessible(true); Method localMethod = Class.forName("android.widget.AbsListView$RecycleBin").getDeclaredMethod("clear",newClass[0]); localMethod.setAccessible(true); localMethod.invoke(localField.get(rootView),newObject[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(rootViewinstanceofViewGroup) {intcount = ((ViewGroup) rootView).getChildCount();for(inti =0; i < count; i++) { changeTheme(((ViewGroup) rootView).getChildAt(i), theme); } }if(rootViewinstanceofAbsListView) {try{ Field localField = AbsListView.class.getDeclaredField("mRecycler"); localField.setAccessible(true); Method localMethod = Class.forName("android.widget.AbsListView$RecycleBin").getDeclaredMethod("clear",newClass[0]); localMethod.setAccessible(true); localMethod.invoke(localField.get(rootView),newObject[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 ***
网友评论