android动态换肤

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


方案一:

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


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


ChildActivity.java


 
@Override
protected 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

 

@Override
protected 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值