Android 插件化開發-主題皮膚更換

參考 http://www.2cto.com/kf/201501/366859.html html

本項目是以插件化開發思想進行的,主要工做和代碼以下
java


資源文件,這裏以color資源爲例網絡

一、首先咱們須要準備一個皮膚包,這個皮膚包裏面不會包含任何Activity,裏面只有資源文件,這裏我爲了簡單,僅僅加入一個color.xml(其實就至關於Android系統中的framework_res.apk)app

<!--?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>

二、將該資源打包成apk文件,放入sd卡中(實際項目你能夠從我網絡下載)異步

三、將須要換膚的Activity實現ISkinUpdate(這個能夠本身隨便定義名稱)接口ide

public class MainActivity extends Activity implements ISkinUpdate,OnClickListener
{
    private Button btn_main;
    private View main_view;
 
    @Override
    protected 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");
      }
    }
 
    @Override
    public 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;
    }
 
    @Override
    public 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);
    }
 
    @Override
    public void updateTheme() 
    {
        // TODO Auto-generated method stub
        if(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 block
                e.printStackTrace();
            }
        }
    }
     
    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        SkinApplication.getInstance().mActivitys.remove(this);
        super.onDestroy();
    }
 
    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        if(v.getId()==R.id.btn_main)
        {
            Intent intent=new Intent(this,SecondActivity.class);
            this.startActivity(intent);
        }
    }
}

這段代碼裏面主要看onOptionsItemSelected,這個方法裏面,經過資源apk路徑,拿到該資源apk對應Resources對象。咱們直接看看SkinPacakgeManager裏面作了什麼吧this

/**
 * 解析皮膚資源包
 * 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方法,從而實現換膚。spa

public class SkinApplication extends Application 
{
    private static SkinApplication mInstance=null;
     
    public ArrayList<iskinupdate> mActivitys=new ArrayList<iskinupdate>();
     
    @Override
    public void onCreate() {
        // TODO Auto-generated method stub
        super.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;
    }
     
    @Override
    public void onTerminate() {
        // TODO Auto-generated method stub
        SkinBroadCastReceiver.unregisterBroadCastReceiver(this);
        super.onTerminate();
    }
     
    public void changeSkin()
    {
        for(ISkinUpdate skin:mActivitys)
        {
            skin.updateTheme();
        }
    }
}
相關文章
相關標籤/搜索