該文章是基於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不須要重刷(由於它每次都是新的)
完成以上步驟之後主應用完成了,但這還不夠由於咱們尚未開發主題包(插件)呢。下一節將講解怎麼開發主題包,它一樣簡單快捷