編輯:關於Android編程
今天群友希望寫一個關於插件的Blog,思來想去,插件也不是很懂,只是用大致的思路看看能不能模擬一個,思路還是比較重要的,如果你有興趣的話,也可以加群:555974449,你也可以說出你想看的Blog哦,嘿嘿!好的,不多說,我們進入正題:
關於QQ的換膚,他們的實現思路我不是很清楚,但是你可以看一下這張換膚的截圖
vc7Sw8e1xGFwa6Os1+6689Om08POqsakt/SjrMLfvK2089bCysfV4tH5tcTC37ytwcujrMTHztLDx8rHsrvKx9OmuMO2r7avytawobavtq/E1KO/PC9wPg0KPHA+ytfRoc7Sw8fQwr2o0ru49rmks8y6w8HLJm1kYXNoOyZtZGFzaDtQbHVnSW5TYW1wbGU8L3A+DQo8cD48aW1nIGFsdD0="這裡寫圖片描述" src="/uploadfile/Collfiles/20160918/2016091809204351.png" title="\" />
其實說起來,這個插件的實現思路,確實是比較的麻煩,思來想去,還是一種辦法比較靠譜,首先,我們刻意去獲取手機上所有的安裝的/未安裝的程序,過濾掉沒用的,留下我們的插件apk,我們的插件apk怎麼去辨別呢?我們可用通過設置sharedUserId,然後用實體類把插件名稱和包名保存下來,有了包名,就比較好說了,我們可用獲取插件的上下文,也就是createPackageContext,然後就可以做點壞事了,我們可以去剖析我們的R文件
因為R文件裡面都是靜態的原因,我們很容易聯想到反射機制,是的,我們可以再一次過濾掉無用的信息,通過我們的PathClassLoader去加載,訪問我們的內加載器反射到我們的圖片ID,也就是後面的那段數字,然後,嘿嘿,就可以使用了,是不是思路比較清晰了?這裡要注意的就是圖片命名統一,這樣就比較號過來,那具體我們應該怎麼做?
我們寫一個Spinner,每次切換就直接換膚怎麼樣?OK,每次換的時候就從插件APK裡加載我們的圖片資源,看起來是比較順暢的邏輯,那我們具體該怎麼做呢?
/** * 初始化View */ private void initView() { //初始化控件 mSpinner = (Spinner) findViewById(R.id.mSpinner); }
當然,我這剛應用就一個View,但是實際開發當中可不止,所以步驟一定要明了
/** * 獲取手機裡的插件 * * @return */ private ListfindPlugIn() { mList = new ArrayList<>(); //獲取相關信息 PackageManager mPackageManager = getPackageManager(); //獲取卸載/未安裝的安裝包信息 List mUninstallPackage = mPackageManager.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES); //遍歷拿到我們的信息 for (PackageInfo info : mUninstallPackage) { String pkgNmae = info.packageName; //獲取shareId,根據id判斷是否是我們的ID String shareUserId = info.sharedUserId; if (!TextUtils.isEmpty(shareUserId)) { //如果id相同 if (shareUserId.equals("com.liuguilin.share")) { //且排除自己的包名 if (!pkgNmae.equals(getPackageName())) { //這個就是我們的插件了 String lable = mPackageManager.getApplicationLabel(info.applicationInfo).toString(); PlugInBean bean = new PlugInBean(); bean.setLabelNmae(lable); bean.setPackagNmae(pkgNmae); mList.add(bean); } } } } return mList; }
這裡就是過濾了一下,通過sharedUserId去拿到我們的插件APK了,然後就可以拿到我們的包名和應用名,他返回給我們一個數據集
//所有的插件 ListallPlugIn = findPlugIn();
/** * 加載皮膚 * * @param allPlugIn */ private void LoadSkin(ListallPlugIn) { //遍歷 for (PlugInBean bean : allPlugIn) { HashMap mMap = new HashMap<>(); mMap.put("lable", bean.getLabelNmae()); mMap.put("package", bean.getPackagNmae()); mData.add(mMap); } //建立Adapter並且綁定數據源 mAdapter = new SimpleAdapter(this, mData, android.R.layout.simple_list_item_1, new String[]{"lable"}, new int[]{android.R.id.text1}); //設置數據 mSpinner.setAdapter(mAdapter); //設置監聽事件 mSpinner.setOnItemSelectedListener(this); }
我們通過剛才的數據集便可以把我們拿到的數據給直接顯示出來了,這裡其實可以判斷一下size是否為0,如果為0的話也就沒有插件,OK,我們設置adapter和監聽,做到這裡,其實你可以運行一下,雖然我們現在什麼都沒有,我們要做的還有很多
/** * 選中監聽事件 * * @param adapterView * @param view * @param i * @param l */ @Override public void onItemSelected(AdapterView adapterView, View view, int i, long l) { PlugInBean bean = mList.get(i); //插件的包名 String packageNmae = bean.getPackagNmae(); Context mContext = null; try { //無視警告 訪問代碼 mContext = createPackageContext(packageNmae, CONTEXT_IGNORE_SECURITY | CONTEXT_INCLUDE_CODE); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } //獲取圖片 getImg(packageNmae, mContext); //通過ID加載插件的圖片 getWindow().setBackgroundDrawable(mContext.getResources().getDrawable(mListId.get(i))); } @Override public void onNothingSelected(AdapterView adapterView) { }
這裡的代碼就比較有意思,一定要仔細看,我們首先拿到選中的item的包名,通過我們的createPackageContext拿到我們的上下文,通過這兩個我們可用拿到我們的資源ID,也就是R清單裡面的ID,然後直接設置window的背景,這裡為了好看才設置window的背景,實際上你要設置的是你根布局的背景,那好,我們來看一下如何通過插件的上下文和包名拿到R清單的資源ID
/** * 獲取插件圖片 / 返回圖片R文件ID / 反射R文件 * * @param packageNmae * @param mContext */ private void getImg(String packageNmae, Context mContext) { //類加載器反射插件 PathClassLoader pathClass = new PathClassLoader(mContext.getPackageResourcePath(), ClassLoader.getSystemClassLoader()); //反射 $ 訪問類加載器 try { Class forNmae = Class.forName(packageNmae + ".R$drawable", true, pathClass); //拿到所有圖片的id Field[] files = forNmae.getDeclaredFields(); for (Field id : files) { //過濾 / 這裡的命名可以注意一下 if (id.getName().startsWith("img")) { int drawId = 0; ////這就是我們圖片R下的ID drawId = id.getInt(R.drawable.class); mListId.add(drawId); } } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } }
這裡我們做了很多事情,首選是拿到我們的類加載器去反射我們的插件,然後通過Class去拿我們的資源,這裡注意packageNmae是我們的文件目錄,他下面的R文件,$代表類部類的意思,他下面的drawable子節點,然後再一次過濾,過濾之後我們可用遍歷一遍拿到我們的ID用List保存起來,也就有了我們選中的時候的設置,好的,到這裡主程序算是編寫完成了,不過要注意的是,記住要添加sharedUserId啊,至關重要!!!
android:sharedUserId="com.liuguilin.share"
我們現在運行也是空的,無意義,我們直接來寫我們的插件吧!
插件的編寫很簡單,我們新建一個PlugInApk的工程
工程裡要做的事情就三件
1.添加sharedUserId
android:sharedUserId="com.liuguilin.share"
2.更改name
這就取決於你了,比如我這裡是Angelababy的主題,我就把名字改成Angelababy
3.把圖片放在drawable文件夾下
好的,做完這三部,我們本能的把插件運行一下,運行之後,我們再次啟動主程序,你會看到….
其實我們主程序裡啥也沒有,對吧,但是的卻加載進來了,這就說明我們的插件化算是圓滿實現了,那我們多來點主題看看最終的效果是什麼樣子的?
通過這個思路確實可以加載到圖片,但是這個邏輯依舊有些不完美,不過最重要的,思考比實現更重要,對吧,後續的也就是一步步的優化了,希望大家和我一起探討一下!
當上完整的代碼
package com.liuguilin.pluginsample; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.text.TextUtils; import android.view.View; import android.widget.AdapterView; import android.widget.SimpleAdapter; import android.widget.Spinner; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import dalvik.system.PathClassLoader; public class MainActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener { //下拉 private Spinner mSpinner; //數據源 private SimpleAdapter mAdapter; //插件數據 private ListmList; //加載的皮膚數據 private List
這裡還有一個實體類哦,具體看Demo:
package com.liuguilin.pluginsample; /* * 項目名: PlugInSample * 包名: com.liuguilin.pluginsample * 文件名: PlugInBean * 創建者: LGL * 創建時間: 2016/9/17 4:18 * 描述: 插件實體類 */ public class PlugInBean { //包名 private String packagNmae; //應用名 private String labelNmae; public String getPackagNmae() { return packagNmae; } public void setPackagNmae(String packagNmae) { this.packagNmae = packagNmae; } public String getLabelNmae() { return labelNmae; } public void setLabelNmae(String labelNmae) { this.labelNmae = labelNmae; } }
前幾天,我們客戶端這邊收到了市場部的一個需求,需要在我們訂單成交後,我們的客戶端有一個上傳交易憑證的功能,那麼如何在Android實現上傳圖片的這個功能呢?在我進行編碼之
MediaRecorder/MediaPlayer在Android手機上面,音頻的處理比視頻還要復雜,這真是出人意料。在前面的博文《Android開發筆記(五十七)錄像錄
今天要實現的效果如下:1.彈幕垂直方向固定2.彈幕垂直方向隨機上面效果圖中白色的背景就是彈幕本身,是一個自定義的FrameLayout,我這裡是為了更好的展示彈幕的位置才
由於移動設備物理顯示空間一般有限,不可能一次性的把所有要顯示的內容都顯示在屏幕上。所以各大平台一般會提供一些可滾動的視圖來向用戶展示數據。Android平台框架中為我們提