前段時間做了個九宮格文件管理器(GridView實現,1920*1080分辨率,定製Android,目前只在MTK和MSTAR平臺上測試及完善過),由於此工程爲公司項目中的子項目,代碼就不公開了(不過,實現不難,以下只提供主要代碼處理和實現考慮情況)
/storage爲根路徑
靜默安裝應用(過程中會有類ProgressBar滾動直到完成操作或操作失敗。靜默安裝過程放在線程中以防止UI卡頓或者因爲權限等原因造成ANR使進程被Kill(原生安卓只有在極端情況下才會kill進程,相關源碼:ActivityManagerService.java中管理Activity的代碼))
靜默卸載應用(同上)
以上僅供參考學習!其實文件管理器實現起來很簡單,只不過過多的細節需要進行考慮而已①。還是得一邊做一邊在腦海中與原生的文件管理器模擬對比,逐漸完善。
①細節包括:
1. 文件管理器中安裝應用後,返回到文件管理器時焦點位置是否要記錄還原,如果不還原文件過多時可能要翻好多下才能找到安裝的apk文件。
2.在正在使用的存儲介質拔出時,提示並只能退出。提示控件對話框(以下叫dialog)截獲onKeyDown事件致使點一次返回響應兩次返回操作的問題:先dialog消失,接着Activity響應back事件,也就是事件衝突和截獲,可以瞭解一下onKeyDown返回值代表的意思。而真實的路徑已經不存在,所以即使按返回,dialog要消失且程序要有“待機”界面或不友好點直接退出。
3.文件或文件夾可能會有權限問題要處理,在清單文件AndroidManifest.xml中配置android:sharedUserId=”android.uid.system”讓應用程序成爲系統應用,但最好的做法還是區分文件和文件夾進行相同的提示,但給予不同的操作。
其實,一分析,有很多的…還是要以用戶的角度去做東西。
好了,直接上代碼,在這裏只放上寫的Demo的Adaper部分供大家學習:
BaseAdapter.java
public class GridAdapter extends BaseAdapter {
private LayoutInflater inflater;
private Bitmap directory, file_notinstall, file_hasinstall,backBitmap;
private ArrayList<String> names = null;
private ArrayList<String> paths = null;
private ArrayList<String> states = null;
public Context getmContext() {
return mContext;
}
public void setmContext(Context mContext) {
this.mContext = mContext;
}
public ArrayList<String> getNames() {
return names;
}
public void setNames(ArrayList<String> names) {
this.names = names;
}
public ArrayList<String> getPaths() {
return paths;
}
public void setPaths(ArrayList<String> paths) {
this.paths = paths;
}
public ArrayList<String> getStates() {
return states;
}
public void setStates(ArrayList<String> states) {
this.states = states;
}
public GridAdapter(Context context, ArrayList<String> name,
ArrayList<String> path, ArrayList<String> state) {
names = name;
paths = path;
states = state;
if (this.mContext == null)
this.mContext = context;
BitmapFactory.Options opts = new BitmapFactory.Options();
//縮放比例,縮放比爲原圖的1/n。
opts.inSampleSize = 1;
//每個像素佔用2byte內存(565沒有透明度屬性),默認ARGB_8888一個像素4byte
opts.inPreferredConfig = Bitmap.Config.RGB_565;
//要真正做到大數量的列表,可以採用分頁式的數據顯示,或者使用LruCache緩存圖片
//(分頁的原理也就是隻顯示和加載當前屏幕中顯示到的數據,即滑動事件中監聽並對邊緣的判斷和處理),
//其實這裏可以不用每個都判斷是否爲空,
//而是這四種圖片之一拿出來判斷即可,因爲它們都是同時初始化和同時釋放資源的,且不是多線程運行。
if (directory == null)
directory = BitmapFactory.decodeResource(context.getResources(),
R.drawable.directory, opts);
if (file_notinstall == null)
file_notinstall = BitmapFactory.decodeResource(
context.getResources(), R.drawable.file_nodownload, opts);
if (file_hasinstall == null)
file_hasinstall = BitmapFactory.decodeResource(
context.getResources(), R.drawable.filehasdownload, opts);
if (backBitmap == null)
backBitmap = BitmapFactory.decodeResource(context.getResources(),
R.drawable.back, opts);
if (inflater == null)
inflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return names.size();
}
@Override
public Object getItem(int position) {
return names.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (null == convertView) {
convertView = inflater.inflate(R.layout.file, null);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.ItemText);
holder.image = (ImageView) convertView.findViewById(R.id.ItemImage);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
File f = new File(paths.get(position).toString());
if (names.get(position).equals("@HOME")) {//保留
holder.text.setText("/");
holder.image.setImageBitmap(directory);
} else if (names.get(position).equals("@BACK")) {//返回上一頁
holder.text.setText("..");
holder.image.setImageBitmap(backBitmap);
} else {
holder.text.setText(f.getName());
if (f.isDirectory()) {
holder.image.setImageBitmap(directory);
} else if (f.isFile()) {
if (states.get(position).equals(KeyStringConst.APK_HASINSTALL)) {
holder.image.setImageBitmap(file_hasinstall);
} else if (states.get(position).equals(
KeyStringConst.APK_NOINSTALL)) {
holder.image.setImageBitmap(file_notinstall);
}
} else {
System.out.println(f.getName());
}
}
return convertView;
}
//緩存每個item的模版類
private class ViewHolder {
private TextView text;
private ImageView image;
}
private Bitmap small(Bitmap map, float num) {
Matrix matrix = new Matrix();
matrix.postScale(num, num);
return Bitmap.createBitmap(map, 0, 0, map.getWidth(), map.getHeight(),
matrix, true);
}
}
如上Adapter部分的代碼所示,圖片的inflate只產生一次。而在Activity中需要更新界面時代碼如下:
private void showFileDir(String path) {
...
if (adapter != null) {//避免多次new GridAdapter(),而直接修改參數並通過adapter適配界面。
//而先判斷不爲空,因爲大多情況下adapter都不爲空,如果是if-else if-...-else結構,主體部分放在靠前的位置判斷,
//能夠省去不必要的判斷。雖然這對於程序執行而言沒有明顯改善,但要是從機器語言執行順序角度思考這絕對是好的習慣。
adapter.setNames(names);
adapter.setPaths(paths);
adapter.setStates(states);
adapter.notifyDataSetChanged();//Notifies the attached observers that the underlying data has been changed and any View reflecting the data set should refresh itself.
} else {
adapter = new GridAdapter(getApplicationContext(), names, paths,
states);
gridView.setAdapter(adapter);
}
...
}
以上就是我的項目的文件結構。
以上是我覺得要寫出來與大家學習的部分,其實文件管理器實現不難,多的是細節的處理,用戶體驗和優化得做好。最後來一張最終效果圖: