久久综合九色综合97婷婷-美女视频黄频a免费-精品日本一区二区三区在线观看-日韩中文无码有码免费视频-亚洲中文字幕无码专区-扒开双腿疯狂进出爽爽爽动态照片-国产乱理伦片在线观看夜-高清极品美女毛茸茸-欧美寡妇性猛交XXX-国产亚洲精品99在线播放-日韩美女毛片又爽又大毛片,99久久久无码国产精品9,国产成a人片在线观看视频下载,欧美疯狂xxxx吞精视频

有趣生活

當(dāng)前位置:首頁>職場>騰訊王者榮耀零基礎(chǔ)入職(說說王者榮耀里面的換膚原理和皮膚)

騰訊王者榮耀零基礎(chǔ)入職(說說王者榮耀里面的換膚原理和皮膚)

發(fā)布時間:2024-01-24閱讀(20)

導(dǎo)讀這段時間對換膚這個技術(shù)產(chǎn)生了濃厚的興趣,找到了一篇不錯的好文章,照著進行了嘗試,成功了,分享給大家,希望對大家學(xué)Android有幫助。大家如果正在準(zhǔn)備騰訊游....

這段時間對換膚這個技術(shù)產(chǎn)生了濃厚的興趣,找到了一篇不錯的好文章,照著進行了嘗試,成功了,分享給大家,希望對大家學(xué)Android有幫助。

大家如果正在準(zhǔn)備騰訊游戲部門面試的話,千萬要要看完呀,指不定就讓你說道說道呢。

點贊 轉(zhuǎn)發(fā),還可以獲得郭霖公眾號曾推薦過的面試真題 解析資料哦。

作者:Android征途鏈接:https://zhuanlan.zhihu.com/p/55041103

本文已收錄于開源項目 https://github.com/xieyuliang/Note-Android(海量資源)

騰訊王者榮耀零基礎(chǔ)入職(說說王者榮耀里面的換膚原理和皮膚)(1)

騰訊王者榮耀零基礎(chǔ)入職(說說王者榮耀里面的換膚原理和皮膚)(2)

騰訊王者榮耀零基礎(chǔ)入職(說說王者榮耀里面的換膚原理和皮膚)(3)

一 前言

Android的換膚技術(shù)已經(jīng)是很久之前就已經(jīng)被成熟使用的技術(shù)了,然而我最近才在學(xué)習(xí)和接觸熱修復(fù)的時候才看到,在看了一些換膚的方法之后,并且對市面上比較認可的Android的皮膚,裝載機換膚框架的源碼進行了分析總結(jié)。再次記錄一下祭奠自己逝去的時間。

二 換膚介紹

換膚本質(zhì)上是對資源的一種替換,包括,字體,顏色,背景,圖片,大小等等。

當(dāng)然這些我們都有成熟的API可以通過控制代碼邏輯做到。比如查看的修改背景顏色setBackgroundColor,TextView中的setTextSize。

修改字體等等但是作為程序員我們怎么能忍受對每個頁面的每個元素一個行行代碼做換膚處理呢?我們需要用最少的代碼實現(xiàn)最容易維護和使用效果完美(動態(tài)切換,及時生效)的換膚框架。

1.換膚方式一:切換使用主題主題

使用相同的資源ID,但在不同的主題下邊自定義不同的資源。我們通過主動切換到不同的主題從而切換界面元素創(chuàng)建時使用的資源。這種方案的代碼量不多發(fā),而且有個很明顯的缺點不支持已經(jīng)創(chuàng)建界面的換膚,必須重新加載界面元素.GitHub Demo

2. 換膚方式二:加載資源包

加載資源包是各種應(yīng)用程序都在使用的換膚方法,例如我們最常用的輸入法皮膚,瀏覽器皮膚等等。

我們可以將皮膚的資源文件放入安裝包內(nèi)部,也可以進行下載緩存到磁盤上.Android的應(yīng)用程序可以使用這種方式進行換膚。

GitHub上面有一個開始非常高的換膚框架Android的皮膚下載器就是通過加載資源包對應(yīng)用程序進行換膚。對這個框架的分析這個也是這篇文章主要的講述內(nèi)容。

對比一下發(fā)現(xiàn)切換主題可以進行小幅度的換膚設(shè)置(比如某個自定義組件的主題),而如果我們想要對整個應(yīng)用程序做主題切換那么通過加載資源包的這種方式目前應(yīng)該說是比較好的了。

三 Android的換膚知識點1. 換膚相應(yīng)的API

我們先來看一下Android的提供的一些基本的API,通過使用這些API可以在應(yīng)用程序內(nèi)部進行資源對象的替換。

公共類資源{ public String getString(int id)throws NotFoundException { CharSequence res = mAssets.getResourceText(id); if(res!= null){ 返回資源; } 拋出新的NotFoundException(“字符串資源ID#0x” Integer.toHexString(id)); } public Drawable getDrawable(int id)throws NotFoundException { / ********部分代碼省略******* / } public int getColor(int id)throws NotFoundException {{ / ********部分代碼省略******* / } / ********部分代碼省略******* /}

這個是我們常用的資源類的API,我們通常可以使用在資源文件中定義的@ id字符串類型,然后在編譯出的R.java中對應(yīng)的資源文件生產(chǎn)的編號(INT類型),從而通過這個ID(INT類型)調(diào)用資源提供的這些API獲取到對應(yīng)的資源對象。這個在同一個應(yīng)用程序下沒有任何問題,但是在皮膚包中我們怎么獲取這個ID值呢。

公共類資源{ / ********部分代碼省略******* / / ***通過給的資源名稱返回一個資源的標(biāo)識id。* @paramname描述資源的名稱* @ paramdefType資源的類型* @paramdefPackage包名** @返回返回資源ID,0標(biāo)識未找到該資源* / public int getIdentifier(String name,String defType,String defPackage){ if(name == null){ 拋出新的NullPointerException(“name is null”); } 嘗試{ return Integer.parseInt(name); } catch(例外e){ // 忽視 } return mAssets.getResourceIdentifier(name,defType,defPackage); }}

資源提供了可以通過@ id,類型,PACKAGENAME這三個參數(shù)就可以在AssetManager中尋找相應(yīng)的軟件包名中有沒有輸入類型并且ID值都能與參數(shù)對應(yīng)上的ID,進行返回。然后我們可以通過這個ID再調(diào)用資源的獲取資源的API就可以得到相應(yīng)的資源。

我們這里需要注意的一點一的英文getIdentifier(String name, String defType, String defPackage)方法狀語從句:getString(int id)方法所調(diào)用資源對象的mAssets對象必須是同一個,并且包含有PACKAGENAME這個資源包。

2.AssetManager構(gòu)造

怎么構(gòu)造一個包含特定的packageName資源的AssetManager對象實例呢?

public final class AssetManagerimplements AutoCloseable { / ********部分代碼省略******* / / ***創(chuàng)建僅包含基本系統(tǒng)資產(chǎn)的新AssetManager。*應(yīng)用程序通常不會使用此方法,而是檢索* {@ linkResources#getAssets}的適當(dāng)資產(chǎn)經(jīng)理。不是為了*由應(yīng)用程序使用。* {@hide}* / public AssetManager(){ synchronized(this){ if(DEBUG_REFS){ mNumRefs = 0; incRefsLocked(this.hashCode()); } INIT(假); if(localLOGV)Log.v(TAG,“新資產(chǎn)經(jīng)理:” 這個); ensureSystemAssets(); } }

從AssetManager構(gòu)造的函數(shù)來看有{@hide}的注解,所以在其他類里面是直接創(chuàng)建AssetManager實例。但是不要忘記的Java中還有反射機制可以創(chuàng)建類對象。

1AssetManager assetManager = AssetManager.class.newInstance();

讓創(chuàng)建的assetManager包含特定的PACKAGENAME的資源信息,怎么辦?我們在AssetManager中找到相應(yīng)的API可以調(diào)用。

public final class AssetManagerimplements AutoCloseable { / ********部分代碼省略******* / / ***向資產(chǎn)經(jīng)理添加一組額外資產(chǎn)。這可以*目錄或ZIP文件。不適用于應(yīng)用程序。返回*添加資產(chǎn)的cookie,或失敗時為0。* {@hide}* / public final int addAssetPath(String path){ synchronized(this){ int res = addAssetPathNative(path); if(mStringBlocks!= null){ makeStringBlocks(mStringBlocks); } 返回資源; } }}

同樣該方法也不支持外部調(diào)用,我們只能通過反射的方法來調(diào)用。

/ *** apk路徑* /String apkPath = Environment.getExternalStorageDirectory() “/ skin.apk”;AssetManager assetManager = null;嘗試{ AssetManager assetManager = AssetManager.class.newInstance(); AssetManager.class.getDeclaredMethod(“addAssetPath”,String.class).invoke(assetManager,apkPath);} catch(Throwable th){ th.printStackTrace();}

至此我們可以構(gòu)造屬于自己換膚的資源了。

3.換膚資源構(gòu)造

public Resources getSkinResources(Context context){ / ***插件apk路徑* / String apkPath = Environment.getExternalStorageDirectory() “/ skin.apk”; AssetManager assetManager = null; 嘗試{ AssetManager assetManager = AssetManager.class.newInstance(); AssetManager.class.getDeclaredMethod(“addAssetPath”,String.class).invoke(assetManager,apkPath); } catch(Throwable th){ th.printStackTrace(); } 返回新資源(assetManager,context.getResources()。getDisplayMetrics(),context.getResources()。getConfiguration());}

4.使用資源包中的資源換膚

我們將上述所有的代碼組合在一起就可以實現(xiàn),使用資源包中的資源對應(yīng)用程序進行換膚。

public Resources getSkinResources(Context context){ / ***插件apk路徑* / String apkPath = Environment.getExternalStorageDirectory() “/ skin.apk”; AssetManager assetManager = null; 嘗試{ AssetManager assetManager = AssetManager.class.newInstance(); AssetManager.class.getDeclaredMethod(“addAssetPath”,String.class).invoke(assetManager,apkPath); } catch(Throwable th){ th.printStackTrace(); } 返回新資源(assetManager,context.getResources()。getDisplayMetrics(),context.getResources()。getConfiguration());}@覆蓋protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); 的setContentView(R.layout.activity_main); ImageView imageView =(ImageView)findViewById(R.id.imageView); TextView textView =(TextView)findViewById(R.id.text); / ***插件資源對象* / Resources resources = getSkinResources(this); / ***獲取圖片資源* / Drawable drawable = resources.getDrawable(resources.getIdentifier(“night_icon”,“drawable”,“com.tzx.skin”)); / ***獲取文本資源* / int color = resources.getColor(resources.getIdentifier(“night_color”,“color”,“com.tzx.skin”)); imageView.setImageDrawable(繪制); textView.setText(文本);}

通過上述介紹,我們可以簡單的對當(dāng)前頁面進行換膚了。但是想要做出一個一個成熟換膚框架那么僅僅這些還是不夠的,提高一下我們的思維高度,如果我們在查看創(chuàng)建的時候就直接使用皮膚資源包中的資源文件,那么這無疑就使換膚更加的簡單易維護。

5. LayoutInflater.Factory

我看過一篇前遇見LayoutInflater及工廠文章的這部分可以省略掉。

很幸運的Android給我們在查看生產(chǎn)的時候做修改提供了法門。

公共抽象類LayoutInflater { / ***部分代碼省略**** / 公共接口工廠{ public View onCreateView(String name,Context context,AttributeSet attrs); } public interface Factory2extends Factory { public View onCreateView(查看父級,字符串名稱,上下文上下文,AttributeSet attrs); } / ***部分代碼省略**** /}

我們可以給當(dāng)前的頁面的窗口對象在創(chuàng)建的時候設(shè)置工廠,那么在窗口中的視圖進行創(chuàng)建的時候就會先通過自己設(shè)置的工廠進行創(chuàng)建.Factory方式使用相關(guān)狀語從句:注意事項請移位到遇見LayoutInflater及工廠,關(guān)于工廠的相關(guān)知識點盡在其中。

四 Android的皮膚,裝載機解析1. 初始化
  • 初始化換膚框架,導(dǎo)入需要換膚的資源包(當(dāng)前為一個APK文件,其中只有資源文件)。

公共類SkinApplicationextends Application { public void onCreate(){ super.onCreate(); initSkinLoader(); } / ***必須先調(diào)用init* / private void initSkinLoader(){ 。SkinManager.getInstance()的init(本); SkinManager.getInstance()負載(); }}

2.構(gòu)造換膚對象

導(dǎo)入需要換膚的資源包,并構(gòu)造換膚的資源實例。

/ ***在asyc任務(wù)中從apk加載資源* @ paramskinPackagePath皮膚路徑apk* @paramcallback回調(diào)通知用戶* /public void load(String skinPackagePath,final ILoaderListener callback){ 新的AsyncTask(){ protected void onPreExecute(){ if(callback!= null){ callback.onStart(); } }; @覆蓋 protected資源doInBackground(String ... params){ 嘗試{ if(params.length == 1){ String skinPkgPath = params [0]; File file = new File(skinPkgPath); if(file == null ||!file.exists()){ return null; } PackageManager mPm = context.getPackageManager(); //檢索程序外的一個安裝包文件 PackageInfo mInfo = mPm.getPackageArchiveInfo(skinPkgPath,PackageManager.GET_ACTIVITIES); //獲取安裝包報名 skinPackageName = mInfo.packageName; //構(gòu)建換膚的AssetManager實例 AssetManager assetManager = AssetManager.class.newInstance(); 方法addAssetPath = assetManager.getClass()。getMethod(“addAssetPath”,String.class); addAssetPath.invoke(assetManager,skinPkgPath); //構(gòu)建換膚的資源實例 資源superRes = context.getResources(); 資源skinResource = new Resources(assetManager,superRes.getDisplayMetrics(),superRes.getConfiguration()); //存儲當(dāng)前皮膚路徑 SkinConfig.saveSkinPath(context,skinPkgPath); skinPath = skinPkgPath; isDefaultSkin = false; return skinResource; } return null; } catch(例外e){ e.printStackTrace(); return null; } }; protected void onPostExecute(參考資料結(jié)果){ mResources =結(jié)果; if(mResources!= null){ if(callback!= null)callback.onSuccess(); //更新多有可換膚的界面 notifySkinUpdate(); }其他{ isDefaultSkin = true; if(callback!= null)callback.onFailed(); } }; } .execute(skinPackagePath);}

定義基類換膚頁面的基類的通用代碼實現(xiàn)基本換膚功能。

public class BaseFragmentActivityextends FragmentActivityimplements ISkinUpdate,IDynamicNewView { / ***部分代碼省略**** / //自定義LayoutInflater.Factory private SkinInflaterFactory mSkinInflaterFactory; @覆蓋 protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); 嘗試{ //設(shè)置LayoutInflater的mFactorySet為真,表示還未設(shè)置mFactory,否則會拋出異常。 Field field = LayoutInflater.class.getDeclaredField(“mFactorySet”); field.setAccessible(真); field.setBoolean(getLayoutInflater(),false); //設(shè)置LayoutInflater的MFactory mSkinInflaterFactory = new SkinInflaterFactory(); getLayoutInflater()setFactory(mSkinInflaterFactory)。 } catch(NoSuchFieldException e){ e.printStackTrace(); } catch(IllegalArgumentException e){ e.printStackTrace(); } catch(IllegalAccessException e){ e.printStackTrace(); } } @覆蓋 protected void onResume(){ super.onResume(); //注冊皮膚管理對象 。SkinManager.getInstance()連接(本); } @覆蓋 protected void onDestroy(){ super.onDestroy(); //反注冊皮膚管理對象 。SkinManager.getInstance()分離(本); } / ***部分代碼省略**** /}

3.SkinInflaterFactory

SkinInflaterFactory進行查看的創(chuàng)建并對視圖進行換膚。

構(gòu)造查看

公共類SkinInflaterFactoryimplements Factory { / ***部分代碼省略**** / public View onCreateView(String name,Context context,AttributeSet attrs){ //讀取查看的皮膚:使屬性,假的為不需要換膚 //如果不允許進行優(yōu)化,請簡單地跳過它 boolean isSkinEnable = attrs.getAttributeBooleanValue(SkinConfig.NAMESPACE,SkinConfig.ATTR_SKIN_ENABLE,false); if(!isSkinEnable){ return null; } //創(chuàng)建視圖 View view = createView(context,name,attrs); if(view == null){ return null; } //如果視圖創(chuàng)建成功,對視圖進行換膚 parseSkinAttr(context,attrs,view); 返回視圖; } //創(chuàng)建視圖,類比可以查看LayoutInflater的createViewFromTag方法 private View createView(Context context,String name,AttributeSet attrs){ View view = null; 嘗試{ if(-1 == name.indexOf(。)){ if(“查看”.equals(name)){ view = LayoutInflater.from(context).createView(name,“android.view。”,attrs); } if(view == null){ view = LayoutInflater.from(context).createView(name,“android.widget。”,attrs); } if(view == null){ view = LayoutInflater.from(context).createView(name,“android.webkit。”,attrs); } } else { view = LayoutInflater.from(context).createView(name,null,attrs); } 李(“即將創(chuàng)造” 名稱); } catch(例外e){ Le(“創(chuàng)建時出錯”“ 名 ”:“ e.getMessage()); view = null; } 返回視圖; }}

4.對生產(chǎn)的景觀進行換膚

公共類SkinInflaterFactoryimplements Factory { //存儲當(dāng)前活動中的需要換膚的查看 private List mSkinItems = new ArrayList(); / ***部分代碼省略**** / private void parseSkinAttr(Context context,AttributeSet attrs,View view){ //當(dāng)前查看的所有屬性標(biāo)簽 List viewAttrs = new ArrayList(); for(int i = 0; i <attrs.getAttributeCount(); i ){ String attrName = attrs.getAttributeName(i); String attrValue = attrs.getAttributeValue(i); 如果(!AttrFactory.isSupportedAttr(attrName)){ 繼續(xù); } //過濾視圖屬性標(biāo)簽中屬性的值的值為引用類型 如果(attrValue.startsWith( “@”)){ 嘗試{ int id = Integer.parseInt(attrValue.substring(1)); String entryName = context.getResources()。getResourceEntryName(id); String typeName = context.getResources()。getResourceTypeName(id); //構(gòu)造SkinAttr實例,attrname,ID,entryName參數(shù)typeName //屬性的名稱(背景),屬性的ID值(INT類型),屬性的ID值(@ ID,串類型),屬性的值類型(顏色) SkinAttr mSkinAttr = AttrFactory.get(attrName,id,entryName,typeName); if(mSkinAttr!= null){ viewAttrs.add(mSkinAttr); } } catch(NumberFormatException e){ e.printStackTrace(); } catch(NotFoundException e){ e.printStackTrace(); } } } //如果當(dāng)前視圖需要換膚,那么添加在mSkinItems中 如果(!ListUtils.isEmpty(viewAttrs)){ SkinItem skinItem = new SkinItem(); skinItem.view = view; skinItem.attrs = viewAttrs; mSkinItems.add(skinItem); //是否是使用外部皮膚進行換膚 如果(SkinManager.getInstance()。isExternalSkin()){ skinItem.apply(); } } }}

5.資源獲取

通過當(dāng)前的資源ID,找到對應(yīng)的資源名稱。再從皮膚包中找到該資源名稱所對應(yīng)的資源ID。

公共類SkinManagerimplements ISkinLoader { / ***部分代碼省略**** / public int getColor(int resId){ int originColor = context.getResources()。getColor(resId); //是否沒有下載皮膚或者當(dāng)前使用默認皮膚 if(mResources == null || isDefaultSkin){ return originColor; } //根據(jù)渣油值獲取對應(yīng)的XML的的@ ID的字符串類型的值 String resName = context.getResources()。getResourceEntryName(resId); //更具resName在皮膚包的mResources中獲取對應(yīng)的渣油 int trueResId = mResources.getIdentifier(resName,“color”,skinPackageName); int trueColor = 0; 嘗試{ //根據(jù)渣油獲取對應(yīng)的資源值 trueColor = mResources.getColor(trueResId); } catch(NotFoundException e){ e.printStackTrace(); trueColor = originColor; } return trueColor; } public Drawable getDrawable(int resId){...}}

其他

除此之外再增加以下對于皮膚的管理API(下載,監(jiān)聽回調(diào),應(yīng)用,取消,異常處理,擴展模塊等等)。

五 總結(jié)

換膚就是這么簡單?!?!

本文在開源項目:https://github.com/xieyuliang/Note-Android中已收錄,里面包含不同方向的自學(xué)編程路線、面試題集合/面經(jīng)、及系列技術(shù)文章等,還有郭霖公號曾推薦過的Android整套體系的學(xué)習(xí)資料 字節(jié)跳動、騰訊、阿里巴巴等大廠面試真題及解析,資源持續(xù)更新中...

目前已經(jīng)更新的部分資料,復(fù)制上面鏈接到瀏覽器打開,需要的自己取:

騰訊王者榮耀零基礎(chǔ)入職(說說王者榮耀里面的換膚原理和皮膚)(4)

騰訊王者榮耀零基礎(chǔ)入職(說說王者榮耀里面的換膚原理和皮膚)(5)

騰訊王者榮耀零基礎(chǔ)入職(說說王者榮耀里面的換膚原理和皮膚)(6)

歡迎分享轉(zhuǎn)載→http://www.avcorse.com/read-254950.html

Copyright ? 2024 有趣生活 All Rights Reserve吉ICP備19000289號-5 TXT地圖HTML地圖XML地圖