At present, many apps have a skin-changing function, can be based on the user's own preferences to customize their own interface, such as Sina Weibo, NetEase news and so on. I'm here today to introduce a mechanism to make apps skin-changing.
I found several applications for skin changes, the basic change is the interface of the icon, background pictures, back color, etc., basically did not encounter the replacement layout, in fact, the layout can be replaced, but feel no need. So this article explains the change of skin is also refers to change icon, background pictures and other resources.
Through the web search I found on the internet that there is probably such a focus on the skin-changing mechanism:
1, the skin pack directly into the APK, this solution is very simple, but not flexible, but also the APK is big.
2, the skin into a separate apk file, and project Engineering common one shareusedid, and have the same signature. This scheme is more flexible than the first one, the disadvantage is that users need to install, Sina Weibo is currently using this program.
The scenario I'm going to present today is similar to the second one, but my resource pack is not installed, and users are generally willing to install some sort of messy app.
Before you learn this article, it is best to study my previous article, "Android Resource management mechanism analysis", because skin management is actually the management of resources. Let's start learning how to change the skin.
1, first we need to prepare a skin bag, this skin package will not contain any activity, there is only resource files, here I simply to add a color.xml (in fact, the equivalent of the Android system 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. Package the resource into an apk file and put it into the SD card (actual item you can download from My network)
3, will need to change the skin activity implementation Iskinupdate (this can be defined by their own name) interface
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", +). 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);}}
This code inside the main look onoptionsitemselected, this method inside, through the resource apk path, get the resource apk corresponding resources object. Let's just see what Skinpacakgemanager's doing in there.
/** * Parsing Skin Resource Pack * Com.skin.demo.SkinPackageManager * @author Yuanzeyao <br/> * Create at January 3, 2015 PM 3:24:16 */public Class Skinpackagemanager {private static Skinpackagemanager minstance; Private Context Mcontext; /** * Current Resource Bundle name */public String mpackagename; /** * Skin Resource * * Public resources mresources; Private Skinpackagemanager (Context mcontext) {this.mcontext=mcontext; } public static Skinpackagemanager getinstance (Context mcontext) {if (minstance==null) {minstance=new Skin Packagemanager (Mcontext); } return minstance; /** * Load Skin Resources Asynchronously * @param dexpath * need to load skin resources * @param callback * Callback interface */public void load Skinasync (String dexpath,final Loadskincallback callback) {new asynctask<string,void,resources> () {Pro tected 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); }/** * Callback interface for loading resources * Com.skin.demo.loadSkinCallBack * @author Yuanzeyao <br/> * Create at January 4, 2015 1:45 : */public static interface Loadskincallback {public void Startloadskin (); public void loadskinsuccess (); public void Loadskinfail (); } }
After calling Loadskinasync, if it succeeds, it sends a skin-changing broadcast and saves the path of the current skin apk to the SP, so that the next time the app is launched, the skin resource is loaded directly.
Accept the change of skin broadcast is registered in the Skinapplication, when received this broadcast, then call all started, and need to change the skin of the activity of the Updatetheme method, so as to achieve skin change.
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 (); Textutils.isempty (Skinpath)) { //If the skin has been changed, then the second time you come in, you need to load the skin 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 ()}}}
Because here to change the skin is only to change the icon, background color and so on, so relatively simple, if you want to change the layout of files, it is a little more complex, here is no longer introduced, interested can own to study.
Easy to implement Android skin replacement (theme)