该文章是基于apkplug V1.6.8 进行说明的 java
咱们提供了一个demo作为参考,项目下载地址为:http://git.oschina.net/plug/IMThemeDemo android
该 IMThemeDemo 实现了简单的聊天泡泡切换功能,同时主题包(插件)是托管在apkplug的云服务器上的。因此要阅读项目的完整代码能够先看插件托管服务开发系列文章 <插件托管服务开发指南> git
demo运行效果图: 服务器
一,apkplug的主题说明 app
apkplug主题解决方案摒弃了以往以图片替换的形式切换效果的方式,转而使用android原生资源和素材来切换UI样式和效果。因此从体验效果上是一次极大的提高。 框架
二,apkplug主题(皮肤)切换适用范围 ide
apkplug主题解决方案适用安卓界面的整个层次。其包括通用主题样式,和自定义控件样式切换两个层面 布局
1.通用主题样式 post
与系统主题类似(Activity.setTheme()) 咱们在系统与app(插件)之间可新增一层主题,三层主题已叠加的形式造成最终结果 this
2.自定义控件样式
大部分状况下咱们的app都会本身设计主题,或者针对某写控件设计本身的样式,在这种模式下以上的通用主题便缺乏
其存在的意义了。所以apkplug也提供一种简单有效的方案,容许开发者根据本身的需求定义自定义UI控件样式的替换规则
在不失通用性兼容性简洁性的状况下达到最好的体验。
三,主题(皮肤)切换流程
1.通用主题样式
通用主题样式有apkplug框架在底层适配,app开发过程当中无需过多考虑,只要主题包设置了通用主题样式,app中的Activity 主题将自定替换(最前面的Activity需刷新后才能体现)
2.自定义控件样式
自定义控件样式的替换因为每一个app的特殊性的缘由,无法作统一的规范。但apkplug通过几个月的努力研发出一套简单有效的实施方案,容许开发者自由定制适合本身app的替换接口。而主题包设计者根据该接口简单配置便可。
四,自定义控件样式替换接口举例
1.定义替换接口
以 IMThemeDemo项目为例,咱们须要替换聊天界面的聊天泡泡,因此自定义了如下的这样一个接口
com.apkplug.imthemedemo.themeinterface.chatstyles
public interface chatstyles { //咱们协定若是 返回值小于0为未设置状态 //主题样式版本,若是主题样式接口有所改变就利用这来判断 public int Version(); //聊天界面右侧Item 背景样式 public int chat_right_msg_background(); //聊天界面左侧Item 背景样式 public int chat_left_msg_background(); //聊天界面背景 public int chatbackground(); }
以上接口返回值是一个资源ID,它多是一张图片或者一个xml布局样式。但它将由主题包(插件)提供。
2.主程序监听主题切换事件
以上咱们定义了一套UI样式的切换接口,但咱们主应用并不知道合适会有主题包来替换咱们现有的样式。因此咱们须要一个监听器来监听这个接口事件,一下是模板代码 (此处设计一些OSGI服务方面的知识,不明白的同窗可看 OSGI服务基本原理),但咱们提供模板代码,开发者能够先实如今研究其原理。
定义一个专门监听接口事件变化的单例类
com.apkplug.imthemedemo.ThemeChengFactory
mcontext.addServiceListener( new ServiceListener(){ @Override public void serviceChanged(ServiceEvent event) { switch (event.getType()) { case ServiceEvent.REGISTERED: if (ref == null ) { //当有主题包注册了该接口,意味着它为咱们提供样式切换的具体资源了 ref = event.getServiceReference(); service = (chatstyles) mcontext.getService(ref); } break ; case ServiceEvent.UNREGISTERING: if (ref == event.getServiceReference()) { //主题包注销了该接口,意味着它再也不提供切换的具体资源了 mcontext.ungetService(ref); service = null ; ref = null ; } break ; } } },String.format("(objectclass=%s)",chatstyles.class.getCanonicalName()) );
上一步咱们已经实现了监听接口注册与注销事件,但咱们尚未将切换的样式绑定到具体的控件上来。这一步咱们将完成这一块工做
根据上一段代码咱们得知 ThemeChengFactory类中的chatstyles service 成员变量在主题包注册之后不为空,没有注册时为空。这样咱们便经过判断 serice是否为空来判断是否有主题样式可切换。同时咱们判断返回值是否为-1来判断主题包提供的样式是否完整
注:咱们约定主题包可能只提供了接口的部分控件样式,其余样式返回-1 表示没有提供相应资源
4.在建立控件时设置样式
1)聊天泡泡样式
com.apkplug.imthemedemo.adapter.ChatMsgViewAdapter
chatstyles cs=ThemeChengFactory.getInstance(null).getService(); if(cs!=null){ //chatstyles 不为空表示有主题包提供替换的资源 ViewHolder v= (ViewHolder)convertView.getTag(); if(isComMsg){ if(cs.chat_left_msg_background()!=-1){ //主题包提供的该控件资源ID v.tvContent.setBackgroundResource(cs.chat_left_msg_background()); } }else{ if(cs.chat_right_msg_background()!=-1){ //主题包提供的该控件资源ID v.tvContent.setBackgroundResource(cs.chat_right_msg_background()); } } }
2)聊天界面背景图片样式
com.apkplug.imthemedemo.activity.ChatActivity
chatstyles cs=ThemeChengFactory.getInstance(null).getService(); if(cs!=null){ //有主题包提供接口样式 if(cs.chatbackground()!=-1){ //提供该控件样式资源ID mListView.setBackgroundResource(cs.chatbackground()); } }
5.监听主题切换事件,重刷UI
第四步咱们完成了在UI建立刷新时判断并替换新的样式,但目前为止咱们还不仅到何时应该刷新UI。如下将解决这个问题
当主题包设置了可替换主题之后,apkplug会给监听器发送主题切换事件,咱们可注册这样一个监听器实时监听主题切换事件刷新activity从而实现实时换肤的功能。
com.apkplug.imthemedemo.activity.MainActivity
public void ListenerTheme(){ BundleContext context=frame.getSystemBundleContext(); ServiceReference reference=context.getServiceReference(RegThemeChengeListener.class.getName()); if(null!=reference){ //查询主题切换事件监听器注册服务 final RegThemeChengeListener service=(RegThemeChengeListener) context.getService(reference); if(service!=null){ //找到服务注册一个监听器,实时监听主题切换动态 service.registerOnThemeListener( new OnThemeChengeListener(){ @SuppressLint("NewApi") @Override public void afterChenged(org.osgi.framework.Bundle arg0, int arg1) { //主题切换之后咱们重刷Activity MainActivity.this.recreate(); System.out.println("afterChenged"); //把上一个监听器注销,由于界面被重刷之后咱们又注册了一个新的监听器 service.unregisterOnThemeListener(this); } @Override public void beforeChenge( org.osgi.framework.Bundle bb, int arg1,org.osgi.framework.Bundle tob, int arg2) { } }); } context.ungetService(reference); } }
咱们没有在ChatActivity界面设置监听器重刷的缘由是,每次设置主题咱们都会到主题包的Activity中,而且返回之后每次都从新进入ChatActivity界面,因此ChatActivity不须要重刷(由于它每次都是新的)
完成以上步骤之后主应用完成了,但这还不够由于咱们尚未开发主题包(插件)呢。下一节将讲解怎么开发主题包,它一样简单快捷