android ListView 動態分頁載入資料
阿新 • • 發佈:2019-01-08
用ListView顯示資料時,列表中資料量大時,載入和處理時間會明顯變長,如果等到把所有資料都處理載入完成再顯示出來,那程式的UI就非常糟糕了,很可能會隨資料量的變大而變慢,這樣的介面很不友好,我們無法接受。
廢話說完了,那怎麼解決這個的問題呢?
動態載入,分頁載入。我不能在UI執行緒中做大量的讀取、處理資料的動作,而是另開一個執行緒來做這件事。但是這樣並不能保證顯示及時,因為資料大時,時間仍會很長,怎麼辦,那我先處理一部分,先顯示出來它們,後臺再繼續處理後面的資料,處理一部分,載入一部分。
這就要用到Adapter和loader 類。
怎麼用這個Adapter 呢,static class AppsListItem{ public Drawable icon = null; public String appLabel = ""; public String summary = ""; public String packageName = ""; public int uid = -1; public int permCount = 0; public String key = ""; } public static class AppListAdapter extends BaseAdapter { Activity mActivity; LayoutInflater mLayoutInflater; PackageManager mPackageManager; List<AppsListItem> mAppList; public AppListAdapter(Activity activity){ mActivity = activity; mLayoutInflater = activity.getLayoutInflater(); mPackageManager = activity.getPackageManager(); mAppList = new ArrayList<AppsListItem>(); } @Override public int getCount() { return (mAppList == null)? 0 : mAppList.size(); } @Override public Object getItem(int position) { return mAppList.get(position); } public Object getItem(String packageName){ for(AppsListItem appInfo : mAppList){ if(appInfo.packageName.equals(packageName)){ return appInfo; } } return null; } @Override public long getItemId(int position) { return position; } class ViewHolder{ ImageView appIcon; TextView appName; TextView summary; } @Override public View getView(int position, View convertView, ViewGroup parent) { final ViewHolder viewHolder; if(convertView == null) { convertView = mLayoutInflater.inflate(R.layout.permission_preference_layout2, parent, false); viewHolder = new ViewHolder(); viewHolder.appIcon = ((ImageView) convertView.findViewById(R.id.appIcon)); viewHolder.appName = ((TextView) convertView.findViewById(R.id.appName)); viewHolder.summary = ((TextView) convertView.findViewById(R.id.summary)); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } final AppsListItem appInfo = mAppList.get(position); viewHolder.appIcon.setImageDrawable(appInfo.icon); viewHolder.appName.setText(appInfo.appLabel); viewHolder.summary.setText(appInfo.summary); return convertView; } public void setData(List<AppsListItem> selectedAppList) { mAppList.clear(); if (selectedAppList != null) { Log.w(TAG,"APP list size "+selectedAppList.size()); mAppList = selectedAppList; } notifyDataSetChanged(); } public void addData(List<AppsListItem> selectedAppList) { if (selectedAppList != null) { Log.w(TAG,"APP list size "+selectedAppList.size()); mAppList.addAll(selectedAppList); } notifyDataSetChanged(); } public void removeItem(String packageName){ AppsListItem item = (AppsListItem)getItem(packageName); if(item != null){ mAppList.remove(item); notifyDataSetChanged(); } } public void removeItem(int position){ if(position < mAppList.size()){ mAppList.remove(position); notifyDataSetChanged(); } } public void addData(AppsListItem item) { if (item != null) { Log.w(LISTTAG,"add package "+item.packageName+" Label: "+item.appLabel); mAppList.add(item); } notifyDataSetChanged(); } }
Adapter 需要loader 類來給它提供資料,它才給使listView 顯示出來。onStart(){ ... listview.setAdapter(mAdapter); listview.setSelectionFromTop(mSaveIndex, mtop); listview.setOnScrollChangeListener(new View.OnScrollChangeListener() { @Override public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) { //mAdapter.notifyDataSetChanged(); } }); listview.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { mSaveIndex = listview.getFirstVisiblePosition(); View v = listview.getChildAt(0); mtop = (v == null) ? 0 : (v.getTop() - listview.getPaddingTop()); AppsListItem appinfo = (AppsListItem)mAdapter.getItem(position); if(appinfo != null){ final String mPackageName = appinfo.packageName; final int mUid = appinfo.uid; int permCnt = appinfo.permCount; Intent sendIntent = new Intent(); sendIntent.setAction("android.intent.action.MANAGE_APP_PERMISSIONS"); sendIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, mPackageName); sendIntent.putExtra(Intent.EXTRA_UID, mUid); sendIntent.putExtra("hideInfoButton", true); startActivity(sendIntent); } } }); }
所以下面是loader 類,這個類是在後臺讀取並處理資料,處理完成一部分,給Adapter 來顯示。但是分步處理這個過程需要藉助
LoaderManager的。
/** * A custom Loader that loads all of the installed applications. */ public static class AppListLoader extends AsyncTaskLoader<List<AppsListItem>> { private int mCount = 0; private static int mOldCount = 0; private static int mSize = 0; private List<AppsListItem> items = null; private static List<ApplicationInfo> apps = new ArrayList<>(); Boolean second = false; String SHAREDPREFERENCE_INSTALLED_PERMISSION_INFO = "PermissionInfos"; public AppListLoader(Context context,int count) { super(context); mCount = count; Log.w(LISTTAG,"mCount "+mCount+" mOldCount "+mOldCount); if(items == null) { items = new ArrayList<AppsListItem>(); } //You Should confirm FIRSTLOAD (11) num diff from others (5) if(mCount ==FIRSTLOAD ) { mOldCount = 0; } } @Override public List<AppsListItem> loadInBackground() { Log.w(LISTTAG,"===============loadInBackground ================="); return buildList(); } @Override protected void onStartLoading() { Log.w(LISTTAG,"===============onStartLoading ================="); forceLoad(); } public List<AppsListItem> buildList() { int permcount = 0; ApplicationInfo appInfo; //Context context = getContext(); PackageManager pm = mContext.getPackageManager(); int index = 0; CtaUtils mCtaUtils = new CtaUtils(mContext); if(apps.size()==0 ) { apps = pm.getInstalledApplications(PackageManager.MATCH_UNINSTALLED_PACKAGES);//Utils.getAllInstalledApplications(context); } mSize = apps.size(); //Log.w(LISTTAG,"mCount "+mCount+" mOldCount "+mOldCount+" mSize "+mSize); SharedPreferences permissionInfos = mContext.getSharedPreferences(SHAREDPREFERENCE_INSTALLED_PERMISSION_INFO, 0); SharedPreferences.Editor editor = permissionInfos.edit(); for(int i=0;i<mSize;i++){ index = mOldCount+i; if(index <mSize) { appInfo = apps.get(index); if(shouldSkipFirst(pm,appInfo)){ //Log.w(LISTTAG, "shouldSkipFirst I " + i+" index" +index); continue; } String preKey = appInfo.packageName + "|" + appInfo.uid; permcount = permissionInfos.getInt(preKey, -1); if (permcount < 0) { //Log.w(LISTTAG, "getCount I " + i+" index "+index); permcount = getPermissionCountInner(appInfo.packageName); permcount = permcount + mCtaUtils.getCTARecords(appInfo.uid, appInfo.packageName); editor.putInt(preKey, permcount); editor.commit(); } if (shouldSkip( permcount)) { //Log.w(LISTTAG, "shouldSkip I " + i+" index" +index); continue; } AppsListItem item = new AppsListItem(); item.appLabel = appInfo.loadLabel(pm).toString(); item.packageName = appInfo.packageName; item.uid = appInfo.uid; item.icon = appInfo.loadIcon(pm); item.permCount = permcount; item.summary = "" + permcount + mContext.getResources().getString(R.string.permission_count); item.key = appInfo.packageName + "|" + appInfo.uid; if (item.icon == null) { item.icon = mContext.getResources().getDrawable(android.R.drawable.sym_def_app_icon); } items.add(item); mCount--; if(mCount==0) break; }else{ Log.e(LISTTAG, "Out of index! mOldCount " + mOldCount+" i "+i+" mSize "+mSize); loadcomplete = true; apps.clear(); break; } } //Log.w(LISTTAG,"index "+index+" mCount "+mCount); mOldCount = (index+1); /* try { Collections.sort(items, APP_COMPARATOR); } catch (Exception e) { }*/ return items; } private int getPermissionCountInner(String packageName ){ int cnt = 0; mPermSet.clear(); PackageInfo mPackageInfo = getPackageInfo(getContext(), packageName); if(mPackageInfo ==null || mPackageInfo.requestedPermissions == null) { return cnt; } for (String requestedPerm : mPackageInfo.requestedPermissions) { //Log.w(TAG,"requestedPerm "+requestedPerm); PermissionInfo permInfo = null; try { permInfo = mContext.getPackageManager().getPermissionInfo(requestedPerm, 0); } catch (PackageManager.NameNotFoundException e) { //Log.w(TAG, "NameNotFoundException "+e.getLocalizedMessage()); } if (permInfo != null && permInfo.group != null) { mPermSet.add(permInfo.group); } } for(String groupName : mPermSet){ switch(groupName){ case PERMISSION_CALENDER_GROUP_NAME: case PERMISSION_STORAGE_GROUP_NAME: case PERMISSION_BODY_SENSORS_GROUP_NAME: case PERMISSION_SMS_GROUP_NAME: case PERMISSION_CONTACTS_GROUP_NAME: case PERMISSION_PHONE_GROUP_NAME: case PERMISSION_CAMERA_GROUP_NAME: case PERMISSION_MICROPHONE_GROUP_NAME: case PERMISSION_LOCATION_GROUP_NAME: cnt++; break; } } return cnt; } /* public static final Comparator<AppsListItem> APP_COMPARATOR = new Comparator<AppsListItem>() { @Override public int compare(AppsListItem item1, AppsListItem item2) { String temp = "" + item2.switchState; int res = temp.compareTo("" + item1.switchState); if(res == 0){ if(item2.suggestOpenText.compareTo(item1.suggestOpenText) == 0) { res = 0; }else if(item2.suggestOpenText.compareTo(item1.suggestOpenText) > 0){ res = 1; }else{ res = -1; } } return res; } };*/ }
所以我們需要實現LoaderManager 的幾個callback: onCreateLoader, onLoadFinished, onLoaderReset
在這幾個callback中來把loader
public class MyActivity extends FrameFrgment implements
LoaderManager.LoaderCallbacks<List<MyActivity.AppsListItem>> {
onCreate{
...
mAdapter= new AppListAdapter(getActivity());
Bundle args = new Bundle();
args.putInt(loadcount,FIRSTLOAD);
getLoaderManager().initLoader(0, args, this);
...}
...
private void restartLoader(){
Bundle args = new Bundle();
args.putInt("count",LOADNUM);
getLoaderManager().restartLoader(0,args,this);
Log.w(TAG,"===============onLoaderReset =================");
}
@Override
public Loader<List<AppsListItem>> onCreateLoader(int id, Bundle args) {
int count = 0;
if(args != null) {
count = args.getInt(loadcount, 10);
}
mAppListLoader = new AppListLoader(getActivity(),count);
return mAppListLoader;
}
@Override
public void onLoadFinished(Loader<List<AppsListItem>> loader, List<AppsListItem> data) {
try {
if(mAdapter.getCount()==0) {
mAdapter.setData(data);
if(!loadcomplete) {
//You Should confirm FIRSTLOAD (11) num diff from others (5)
restartLoader();
}
}
else {
mAdapter.addData(data);
if(!loadcomplete) {
//You Should confirm FIRSTLOAD (11) num diff from others (5)
restartLoader();
}
}
setLoading(false,true);
}catch (Exception err){
Log.d(LISTTAG, "AppAutoStartList-->onLoadFinished", err);
}
}
@Override
public void onLoaderReset(Loader<List<AppsListItem>> loader) {
// Clear the data in the adapter.
//mAdapter.setData(null);
}