編輯:關於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 List findPlugIn() {
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(List allPlugIn) {
//遍歷
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 List mList;
//加載的皮膚數據
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設置拍照或者上傳本地圖片的示例
前幾天,我們客戶端這邊收到了市場部的一個需求,需要在我們訂單成交後,我們的客戶端有一個上傳交易憑證的功能,那麼如何在Android實現上傳圖片的這個功能呢?在我進行編碼之
Android開發筆記(一百二十六)自定義音樂播放器
MediaRecorder/MediaPlayer在Android手機上面,音頻的處理比視頻還要復雜,這真是出人意料。在前面的博文《Android開發筆記(五十七)錄像錄
Android自制彈幕
今天要實現的效果如下:1.彈幕垂直方向固定2.彈幕垂直方向隨機上面效果圖中白色的背景就是彈幕本身,是一個自定義的FrameLayout,我這裡是為了更好的展示彈幕的位置才
Android中HorizontalScrollView使用方法詳解
由於移動設備物理顯示空間一般有限,不可能一次性的把所有要顯示的內容都顯示在屏幕上。所以各大平台一般會提供一些可滾動的視圖來向用戶展示數據。Android平台框架中為我們提