編輯:關於Android編程
目前很多app都具有換膚功能,可以根據用戶自己的喜好定制自己的界面,比如新浪微博,網易新聞等等。今天這裡我就是要介紹一種機制實現app換膚。
我找了幾款app換膚的應用,換膚基本都是更換了界面的Icon,背景圖片,背景色等等,基本沒有遇到更換布局的,其實布局也是可以更換的,但是覺得沒有必要。所以這篇文章講解的換膚也是指換icon,背景圖片等資源。
通過網絡搜索我發現網上上提供了大概這麼集中換膚機制:
1、直接將皮膚包放入apk中,這種方案實現非常簡單,但是不夠靈活,而且還將apk搞大了。
2、將皮膚做成一個獨立的apk文件,並和項目工程公用一個shareUsedId,並擁有相同的簽名。這種方案較第一種方案就是靈活性比較大,缺點就是需要用戶安裝,新浪微博目前使用的就是這種方案。
我今天要介紹的這種方案和第二種比較類似,但是我的資源包是不要安裝的,畢竟用戶一般願意裝一些亂七八糟的應用。
在學習這篇文章之前最好學習我的前一篇文章《Android資源管理機制分析》,因為皮膚管理其實就是資源的管理。下面開始學習如何換膚吧
1、首先我們需要准備一個皮膚包,這個皮膚包裡面不會包含任何Activity,裡面只有資源文件,這裡我為了簡單,僅僅加入一個color.xml(其實就相當於Android系統中的framework_res.apk)
#E61ABD #38F709 #000000 #FFFFFF
3、將需要換膚的Activity實現ISkinUpdate(這個可以自己隨便定義名稱)接口
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);
}
}
}
/** * 解析皮膚資源包 * com.skin.demo.SkinPackageManager * @author yuanzeyao調用loadSkinAsync後,如果成功,就會發送一個換膚廣播,並將當前皮膚apk的路徑保存到sp中,便於下次啟動app是直接加載該皮膚資源。
* 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() { 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
* create at 2015年1月4日 下午1:45:48 */ public static interface loadSkinCallBack { public void startloadSkin(); public void loadSkinSuccess(); public void loadSkinFail(); } }
接受換膚廣播是在SkinApplication中注冊的,當接收到此廣播後,隨即調用所有已經啟動,並且需要換膚的Activity的updateTheme方法,從而實現換膚。
public class SkinApplication extends Application
{
private static SkinApplication mInstance=null;
public ArrayList mActivitys=new ArrayList();
@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();
}
}
}
使用Android Studio打開eclipse項目以及打開網上下載的studio項目報錯解決辦法
下面來介紹一下標題的題目,之前剛接觸Android Studio也是一頭霧水,在此寫下來和大家分享:】(一) . Andriod Studio下載網址:http://ww
Android簡易實戰教程--第五話《開發一鍵鎖屏應用》
Device Administration對於這個應用,市場上很多,但是看一下評論就知道效果有多差了,因為99%一鍵鎖屏應用沒辦法卸載。今天就開發一個小應用,實現輕松點擊
Xpoded模塊開發教程
當然,你可以去學習如何創建一個Xposed模塊。所以你可以閱讀這篇教程(官方教程)去學習怎樣解決這個問題。這不僅僅講解如何新建模塊、如何編寫模塊,我們要往更深處思考,為什
android基礎總結篇之八:創建及調用自己的ContentProvider
今天我們來講解一下如何創建及調用自己的ContentProvider。在前面兩篇文章中我們分別講了如何讀寫聯系人和短消息,相信大家對於ContentProvider的操作