輕鬆實現Android 更換皮膚(主題),輕鬆實現android

來源:互聯網
上載者:User

輕鬆實現Android 更換皮膚(主題),輕鬆實現android

目前很多app都具有換膚功能,可以根據使用者自己的喜好定製自己的介面,比如新浪微博,網易新聞等等。今天這裡我就是要介紹一種機制實現app換膚。

我找了幾款app換膚的應用,換膚基本都是更換了介面的Icon,背景圖片,背景色等等,基本沒有遇到更換布局的,其實布局也是可以更換的,但是覺得沒有必要。所以這篇文章講解的換膚也是指換icon,背景圖片等資源。

通過網路搜尋我發現網上上提供了大概這麼集中換膚機制:

1、直接將皮膚包放入apk中,這種方案實現非常簡單,但是不夠靈活,而且還將apk搞大了。

2、將皮膚做成一個獨立的apk檔案,並和項目工程公用一個shareUsedId,並擁有相同的簽名。這種方案較第一種方案就是靈活性比較大,缺點就是需要使用者安裝,新浪微博目前使用的就是這種方案。


我今天要介紹的這種方案和第二種比較類似,但是我的資源套件是不要安裝的,畢竟使用者一般願意裝一些亂七八糟的應用。

在學習這篇文章之前最好學習我的前一篇文章《Android資源管理機制分析》,因為皮膚管理其實就是資源的管理。下面開始學習如何換膚吧


1、首先我們需要準備一個皮膚包,這個皮膚包裡面不會包含任何Activity,裡面只有資源檔,這裡我為了簡單,僅僅加入一個color.xml(其實就相當於Android系統中的framework_res.apk)

<?xml version="1.0" encoding="utf-8"?><resources>    <color name="main_btn_color">#E61ABD</color>    <color name="main_background">#38F709</color>        <color name="second_btn_color">#000000</color>    <color name="second_background">#FFFFFF</color>    </resources>


2、將該資源打包成apk檔案,放入sd卡中(實際項目你可以從我網路下載)

3、將需要換膚的Activity實現ISkinUpdate(這個可以自己隨便定義名稱)介面


public class MainActivity extends Activity implements ISkinUpdate,OnClickListener{private Button btn_main;private View main_view;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);  this.setContentView(R.layout.activity_main);SkinApplication.getInstance().mActivitys.add(this);btn_main=(Button)this.findViewById(R.id.btn_main);    btn_main.setOnClickListener(this);main_view=this.findViewById(R.id.main_view);}        @Override    protected void onResume() {      super.onResume();      if(SkinPackageManager.getInstance(this).mResources!=null)      {        updateTheme();        Log.d("yzy", "onResume-->updateTheme");      }    }@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {int id = item.getItemId();if (id == R.id.action_settings) {//Toast.makeText(this, "change skin", 1000).show();File dir=new File(Environment.getExternalStorageDirectory(),"plugins");File skin=new File(dir,"SkinPlugin.apk");if(skin.exists()){  SkinPackageManager.getInstance(MainActivity.this).loadSkinAsync(skin.getAbsolutePath(), new loadSkinCallBack() {          @Override          public void startloadSkin()           {            Log.d("yzy", "startloadSkin");          }                    @Override          public void loadSkinSuccess() {            Log.d("yzy", "loadSkinSuccess");            MainActivity.this.sendBroadcast(new Intent(SkinBroadCastReceiver.SKIN_ACTION));          }                    @Override          public void loadSkinFail() {            Log.d("yzy", "loadSkinFail");          }        });}return true;}return super.onOptionsItemSelected(item);}@Overridepublic void updateTheme() {// TODO Auto-generated method stubif(btn_main!=null){try {Resources mResource=SkinPackageManager.getInstance(this).mResources;Log.d("yzy", "start and mResource is null-->"+(mResource==null));int id1=mResource.getIdentifier("main_btn_color", "color", "com.skin.plugin");btn_main.setBackgroundColor(mResource.getColor(id1));int id2=mResource.getIdentifier("main_background", "color","com.skin.plugin");main_view.setBackgroundColor(mResource.getColor(id2));//img_skin.setImageDrawable(mResource.getDrawable(mResource.getIdentifier("skin", "drawable","com.skin.plugin")));} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}}@Overrideprotected void onDestroy() {// TODO Auto-generated method stubSkinApplication.getInstance().mActivitys.remove(this);super.onDestroy();}@Overridepublic void onClick(View v) {// TODO Auto-generated method stubif(v.getId()==R.id.btn_main){Intent intent=new Intent(this,SecondActivity.class);this.startActivity(intent);}}}

這段代碼裡面主要看onOptionsItemSelected,這個方法裡面,通過資源apk路徑,拿到該資源apk對應Resources對象。我們直接看看SkinPacakgeManager裡面做了什麼吧

/** * 解析皮膚資源套件 * com.skin.demo.SkinPackageManager * @author yuanzeyao <br/> * create at 2015年1月3日 下午3:24:16 */public class SkinPackageManager {  private static SkinPackageManager mInstance;  private Context mContext;  /**   * 當前資源套件名   */  public String mPackageName;    /**   * 皮膚資源   */  public Resources mResources;    private SkinPackageManager(Context mContext)  {    this.mContext=mContext;  }    public static SkinPackageManager getInstance(Context mContext)  {    if(mInstance==null)    {      mInstance=new SkinPackageManager(mContext);    }        return mInstance;  }      /**   * 非同步載入皮膚資源   * @param dexPath   *        需要載入的皮膚資源   * @param callback   *        回調介面   */  public void loadSkinAsync(String dexPath,final loadSkinCallBack callback)  {    new AsyncTask<String,Void,Resources>()    {      protected void onPreExecute()       {        if(callback!=null)        {          callback.startloadSkin();        }      };         @Override      protected Resources doInBackground(String... params)       {        try {          if(params.length==1)          {            String dexPath_tmp=params[0];            PackageManager mPm=mContext.getPackageManager();            PackageInfo mInfo=mPm.getPackageArchiveInfo(dexPath_tmp,PackageManager.GET_ACTIVITIES);            mPackageName=mInfo.packageName;                                    AssetManager assetManager = AssetManager.class.newInstance();            Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);            addAssetPath.invoke(assetManager, dexPath_tmp);                        Resources superRes = mContext.getResources();            Resources skinResource=new Resources(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration());            SkinConfig.getInstance(mContext).setSkinResourcePath(dexPath_tmp);            return skinResource;          }          return null;        } catch (Exception e) {          return null;        }               };            protected void onPostExecute(Resources result)       {        mResources=result;               if(callback!=null)        {          if(mResources!=null)          {            callback.loadSkinSuccess();          }else          {            callback.loadSkinFail();          }        }      };          }.execute(dexPath);  }    /**   * 載入資源的回調介面   * com.skin.demo.loadSkinCallBack   * @author yuanzeyao <br/>   * create at 2015年1月4日 下午1:45:48   */  public static interface loadSkinCallBack  {    public void startloadSkin();        public void loadSkinSuccess();        public void loadSkinFail();  }     }
調用loadSkinAsync後,如果成功,就會發送一個換膚廣播,並將當前皮膚apk的路徑儲存到sp中,便於下次啟動app是直接載入該皮膚資源。


接受換膚廣播是在SkinApplication中註冊的,當接收到此廣播後,隨即調用所有已經啟動,並且需要換膚的Activity的updateTheme方法,從而實現換膚。

public class SkinApplication extends Application {private static SkinApplication mInstance=null;public ArrayList<ISkinUpdate> mActivitys=new ArrayList<ISkinUpdate>();@Overridepublic void onCreate() {// TODO Auto-generated method stubsuper.onCreate();mInstance=this;String skinPath=SkinConfig.getInstance(this).getSkinResourcePath();if(!TextUtils.isEmpty(skinPath)){  //如果已經換皮膚,那麼第二次進來時,需要載入該皮膚  SkinPackageManager.getInstance(this).loadSkinAsync(skinPath, null);}SkinBroadCastReceiver.registerBroadCastReceiver(this);}public static SkinApplication getInstance(){return mInstance;}@Overridepublic void onTerminate() {// TODO Auto-generated method stubSkinBroadCastReceiver.unregisterBroadCastReceiver(this);super.onTerminate();}public void changeSkin(){for(ISkinUpdate skin:mActivitys){skin.updateTheme();}}}

由於這裡換膚僅僅是更換icon,背景色之類的,所以比較簡單,如果要更換布局檔案,那就稍微要複雜一些,這裡就不再介紹了,有興趣的可以自己去研究..

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.