1. 程式人生 > >android ListView 動態分頁載入資料

android ListView 動態分頁載入資料

 用ListView顯示資料時,列表中資料量大時,載入和處理時間會明顯變長,如果等到把所有資料都處理載入完成再顯示出來,那程式的UI就非常糟糕了,很可能會隨資料量的變大而變慢,這樣的介面很不友好,我們無法接受。

 廢話說完了,那怎麼解決這個的問題呢?

 動態載入,分頁載入。我不能在UI執行緒中做大量的讀取、處理資料的動作,而是另開一個執行緒來做這件事。但是這樣並不能保證顯示及時,因為資料大時,時間仍會很長,怎麼辦,那我先處理一部分,先顯示出來它們,後臺再繼續處理後面的資料,處理一部分,載入一部分。

 這就要用到Adapter和loader 類。

    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 呢,
 
 

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); } } });
}
Adapter 需要loader 類來給它提供資料,它才給使listView 顯示出來。

所以下面是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);
}