android动态换肤

来源:互联网 发布:什么叫根域名 编辑:程序博客网 时间:2024/05/17 15:36

项目有一个功能是用户在信息页面更改了性别,返回页面要更换主题。


方案一:

1.onRestart的时候切换主题,调用recreate()


2.onCreate或者setContentView的时候(只在当前Activity里设置一次)设置主题 


ChildActivity.java


@Overrideprotected void onCreate(Bundle savedInstanceState) {    this.setTheme();    super.onCreate(savedInstanceState);
    setContentView(R.layout.layout);}
protected void setTheme() {    if (!this.mIsThemeSet) {        this.mIsThemeSet = true;        this.setTheme(getCurrentTheme());    }}

public static int getCurrentTheme() {    return  getGenderEnum() == GenderEnum.MALE ?            R.style.lechild_theme_boy :            R.style.lechild_theme_girl;}



theme_styles.xml

<?xml version="1.0" encoding="utf-8"?><resources>    <style name="lechild_theme_girl" parent="@style/LeChildAppTheme">        <item name="lechild_drawable_lechild_cal_bg">@drawable/lechild_cal_bg</item>
        ...
    </style>

    <style name="lechild_theme_boy" parent="@style/LeChildAppTheme">        <item name="lechild_drawable_lechild_cal_bg">@drawable/lechild_cal_bg_boy</item>
        ...
    </style>

</resources>



theme_attrs.xml

<?xml version="1.0" encoding="utf-8"?><resources>
    <attr name="lechild_drawable_lechild_cal_bg" format="reference"/>
...
</resources>




layout.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="?attr/lechild_drawable_lechild_cal_bg">


3.缺陷:这样因为重新调用activity的生命周期,会造成页面闪一下,显示效果不好。


改进方案二:


gitHub上有一些开源项目,解决了闪的问题,找了个比较典型的叫ChangeThemeForAndoird,



1.核心思想就是针对所有的布局view及其子view,利用build构建Setter,根据代码动态的设置view的backgroundDrawable、textColor等属性。


MainActivity.java

@Overrideprotected void onRestart() {   super.onRestart();   changeThemeWithColorful();}

/** * 设置各个视图与颜色属性的关联 */private void setupColorful() {   ViewGroupSetter listViewSetter = new ViewGroupSetter(mNewsListView);   // 绑定ListView的Item View中的news_title视图,在换肤时修改它的text_color属性   listViewSetter.childViewTextColor(R.id.news_title, R.attr.text_color);   listViewSetter.childViewBgDrawable(R.id.news_creator, R.attr.ic_launcher);   // 构建Colorful对象来绑定View与属性的对象关系   mColorful = new Colorful.Builder(this)         .backgroundDrawable(R.id.root_view, R.attr.root_view_bg)         // 设置view的背景图片         .backgroundColor(R.id.change_btn, R.attr.btn_bg)         // 设置背景色         .textColor(R.id.textview, R.attr.text_color)         .setter(listViewSetter) // 手动设置setter         .create(); // 设置文本颜色}/** * 切换主题 */private void changeThemeWithColorful() {   if (!isNight) {      mColorful.setTheme(R.style.NightTheme);   } else {      mColorful.setTheme(R.style.DayTheme);   }   isNight = !isNight;}



ColorFul.java

/** * 设置新的主题 *  * @param newTheme */public void setTheme(int newTheme) {   mBuilder.setTheme(newTheme);}/** *  * 构建Colorful的Builder对象 *  * @author mrsimple *  */public static class Builder {   /**    * 存储了视图和属性资源id的关系表    */   Set<ViewSetter> mElements = new HashSet<ViewSetter>();   /**    * 目标Activity    */   Activity mActivity;   /**    * @param activity    */   public Builder(Activity activity) {      mActivity = activity;   }   /**    *     * @param fragment    */   public Builder(Fragment fragment) {      mActivity = fragment.getActivity();   }   private View findViewById(int viewId) {      return mActivity.findViewById(viewId);   }   /**    * 将View id与存储该view背景色的属性进行绑定    *     * @param viewId    *            控件id    * @param colorId    *            颜色属性id    * @return    */   public Builder backgroundColor(int viewId, int colorId) {      mElements.add(new ViewBackgroundColorSetter(findViewById(viewId),            colorId));      return this;   }   /**    * 将View id与存储该view背景Drawable的属性进行绑定    *     * @param viewId    *            控件id    * @param colorId    *            Drawable属性id    * @return    */   public Builder backgroundDrawable(int viewId, int drawableId) {      mElements.add(new ViewBackgroundDrawableSetter(            findViewById(viewId), drawableId));      return this;   }   /**    * 将TextView id与存储该TextView文本颜色的属性进行绑定    *     * @param viewId    *            TextView或者TextView子类控件的id    * @param colorId    *            颜色属性id    * @return    */   public Builder textColor(int viewId, int colorId) {      TextView textView = (TextView) findViewById(viewId);      mElements.add(new TextColorSetter(textView, colorId));      return this;   }   /**    * 用户手动构造并且添加Setter    *     * @param setter    *            用户自定义的Setter    * @return    */   public Builder setter(ViewSetter setter) {      mElements.add(setter);      return this;   }   /**    * 设置新的主题    *     * @param newTheme    */   protected void setTheme(int newTheme) {      mActivity.setTheme(newTheme);      makeChange(newTheme);   }   /**    * 修改各个视图绑定的属性    */   private void makeChange(int themeId) {      Theme curTheme = mActivity.getTheme();      for (ViewSetter setter : mElements) {         setter.setValue(curTheme, themeId);      }   }   /**    * 创建Colorful对象    *     * @return    */   public Colorful create() {      return new Colorful(this);   }


各种setter.java

** * View的背景Drawabler Setter * @author mrsimple *  */public final class ViewBackgroundDrawableSetter extends ViewSetter {   public ViewBackgroundDrawableSetter(View targetView, int resId) {      super(targetView, resId);   }         public ViewBackgroundDrawableSetter(int viewId, int resId) {      super(viewId, resId);   }   @SuppressWarnings("deprecation")   @Override   public void setValue(Theme newTheme, int themeId) {      if ( mView == null ) {         return  ;      }      TypedArray a = newTheme.obtainStyledAttributes(themeId,            new int[] { mAttrResId });      int attributeResourceId = a.getResourceId(0, 0);      Drawable drawable = mView.getResources().getDrawable(            attributeResourceId);      a.recycle();      mView.setBackgroundDrawable(drawable);   }}


2.缺陷:需要把所有的view都用代码替换,繁琐。


方案三:


github上开源项目Android-Skin-Loader,可以直接拿来做换肤用。这个库的核心思想就是动态的去加载第三方包里面的包,获取到其Resources然后以获取到的这个Resources去获取第三方包里面的资源内容,最后设置到我们有需响应皮肤更改的View上。
覆盖application的getResource方法,实现自己的resource,优先加载本地皮肤包文件夹下的资源包.

尽管动态加载方案比较黑科技,可能因为系统API的更改而出问题,但相对来所
好处有

  • 灵活性高,后台可以随时更新皮肤包
  • 相对透明,开发者几乎不用关心有几套皮肤,不用去定义各种theme和attr,甚至连皮肤包的打包都可以交给设计或者专门的同学
  • apk体积节省
    存在的问题
    没有完善的开源项目,如果我们采用动态加载的第二种方案,需要的项目功能包括:
  • 自定义皮肤包结构
  • 换肤引擎,加载皮肤包资源并load,实时刷新。
  • 皮肤包打包工具
  • 对各种rom的兼容


这些是从别人的博客里看的(http://blog.zhaiyifan.cn/2015/09/10/Android换肤技术总结/),自己没有详细看,代码还是比较复杂的。


项目最终使用的方案:

基于简单的原则,最终解决方案是,在更改性别的页面,按返回键时,加上一个loading圈,在服务端请求返回后,通知需要更改皮肤的页面,并延迟500ms销毁更改性别页面,此时更改皮肤的页面的皮肤已重新recreate()并完成主题切换,因此已经是切换后的皮肤。
怎么样,还是比较机智的girl吧~实用第一哈。








0 0
原创粉丝点击