如何在listview中正确进行无限加载

时间:2015-01-16 18:16:12

标签: android listview

主题: 我从外部数据库下载了一堆数据,我想一次只显示其中一些数据(10)。当用户向下滚动时,我想自动下载并显示另一个10(并在Listview下面显示一个加载进度条作为其FooterView,同时项目正在下载并加载到ListView中)。我想知道这是否是正确的方法,因为我遇到了一些问题:

问题

1.有时它会滞后,就像最后一个可见项目是第40项一样,我不能向下滚动,除非我先向上滚动一下然后向下滚动。 (FooterView没有显示)

2.有时加载进度条会保持打开状态,我必须先向上滚动然后向下滚动,这样它就会消失并显示更多项目。 (我等了,即使下载了这些项目,也不会删除FooterView)

如何运作: 首先下载10个项目,6个可见(取决于屏幕大小)。当我向下滚动并且我在项目#8时,下载了另外10个项目,因此我可以继续向下滚动或者如果项目尚未下载,则加载进度条显示在屏幕底部作为ListView的页脚直到这些项目已下载。加载项目后,FooterView将被删除,我可以继续向下滚动。

boolean all_items_downloaded = false;

    num_loadstart = 0;
    num_loadfinish = 10;

    new DownloadUsers3().execute(String.valueOf(num_loadstart), String.valueOf(num_loadfinish));

    lv.setOnScrollListener(new OnScrollListener() {

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

            int lastInScreen = firstVisibleItem + visibleItemCount;
            Log.i("FOLLOW Onscroll totalItemCount", totalItemCount + "");
            Log.i("FOLLOW Onscroll visibleItemCount", visibleItemCount + "");
            Log.i("FOLLOW Onscroll lastInScreen", lastInScreen + "");

            if (lastInScreen == totalItemCount) {     
                if (all_items_downloaded) {
                    lv.removeFooterView(loadMoreView);
                }
            }

            //when the last visible item is near the last downloaded item then start loading more
            if ((totalItemCount - lastInScreen == 2) && !(loadingMore) && (totalItemCount != 0)) {
                Log.i("FOLLOW LOADMORE", "NOW");
                if (!all_items_downloaded) {
                    new DownloadUsers3().execute(String.valueOf(totalItemCount), "10");
                }
            }

            lastPos = lv.getLastVisiblePosition();
        }

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {


        }

    });

    return view;
}


public class DownloadUsers3 extends AsyncTask<String, Void, Integer> {

    @Override
    protected void onPostExecute(Integer result) {
        Log.i("DownloadUsers3 onPost result", result + "");
        pb.setVisibility(View.GONE);
        loading_ll.setVisibility(View.GONE);
        llMain.setVisibility(View.VISIBLE);

        if (result != null) {

            if (result < 1) tv_noone.setVisibility(View.VISIBLE);
            else tv_noone.setVisibility(View.GONE);

            //set adapter only when items are downloaded the first time aka. no scrolling has been made
            if (result < 11) {
                myadapter = new MyAdapter(ctx, arr_users_id, arr_users_username, arr_users_photo, arr_users_followed, arr_users_numadded, arr_users_numcompleted, arr_users_fbuserid, arr_users_imagetype, arr_users_twuserid, arr_users_twphoto);
                lv.setAdapter(myadapter);
            } else {
                myadapter.notifyDataSetChanged();
            }
        }
        loadingMore = false;

    }


    @Override
    protected void onPreExecute() {
        //show progress dialog only the first time when items are being downloaded
//this is just an overlay progress dialog to show the users when they first open the fragment
        if (arr_users_id.size() < 10) {
            pb.setVisibility(View.VISIBLE);
            loading_ll.setVisibility(View.VISIBLE);
            llMain.setVisibility(View.GONE);
        }
    }

    @Override
    protected Integer doInBackground(String... params) {

        loadingMore = true;

        try{
            HttpClient httpclient = new DefaultHttpClient();
            HttpPost httppost = null;
            httppost = new HttpPost(list_all_users);

            List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
            String start = params[0];
            String finish = params[1];

            Log.i("At session_userid", session_userid + "");
            nameValuePairs.add(new BasicNameValuePair("session_userid", session_userid));
            nameValuePairs.add(new BasicNameValuePair("start", start));
            nameValuePairs.add(new BasicNameValuePair("finish", finish));
            httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
            HttpResponse response = httpclient.execute(httppost);
            HttpEntity entity = response.getEntity();
            is = entity.getContent();
            }catch(Exception e){
                    Log.e("error", "Error in http connection "+e.toString());
            }

            try{
                BufferedReader reader = new BufferedReader(new InputStreamReader(is,"iso-8859-1"),8);
                StringBuilder sb = new StringBuilder();
                String line = null;
                while ((line = reader.readLine()) != null) {
                     sb.append(line + "\n");
                }
                is.close();
                Bresult=sb.toString();
                Log.i("Bresult", Bresult + "");
            } catch(Exception e){
                    Log.e("error", "Error converting result "+e.toString());
            }

            if (Bresult.length() > 10) {
                try {
                    jArray = new JSONArray(Bresult);
                    for(int i=0;i<jArray.length();i++){
                        JSONArray innerJsonArray = jArray.getJSONArray(i);
                        for(int j=0;j<innerJsonArray.length();j++){  
                            JSONObject jsonObject = innerJsonArray.getJSONObject(j);

                            arr_users_id.add(jsonObject.getString("ID"));
                            arr_users_username.add(jsonObject.getString("USERNAME"));
                            arr_users_photo.add(jsonObject.getString("PHOTO"));
                            arr_users_followed.add(jsonObject.getString("DO_I_FOLLOW_HIM"));
                            arr_users_numadded.add(jsonObject.getString("NUM_ALL"));
                            arr_users_numcompleted.add(jsonObject.getString("NUM_DONE"));
                            arr_users_recentbucket.add(jsonObject.getString("REC"));
                            arr_users_fbuserid.add(jsonObject.getString("FB_USERID"));
                            arr_users_imagetype.add(jsonObject.getString("IMAGE_TYPE"));
                            arr_users_twuserid.add(jsonObject.getString("TW_USERID"));
                            arr_users_twphoto.add(jsonObject.getString("TW_PHOTO"));

                            Log.i("arr_users_username.get(" + j + ")", arr_users_username.get(j));

                        }
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                }

            } else {
                all_items_downloaded = true;
                return null;
            }
        Log.i("arr_users_username.size()", arr_users_username.size() + "");
        return arr_users_id.size();
    }
}

查询的PHP代码如下:

SELECT ... FROM ... GROUP BY ... ORDER BY ... LIMIT $start, $finish

其中$ start和$ finish是从Java传递的变量。

与排球完成

首先我添加了volley作为库项目,然后在this教程中添加了类。

private void makeJsonArrayRequest(final String start, final String finish) {

    loadingMore = true;
    StringRequest postReq = new StringRequest(Method.POST, list_all_users2, new Response.Listener<String>() {

        @Override
        public void onResponse(String response) {

            Log.i("VOLLEYY", response);
            Log.i("VOLLEYY.length()", response.length() + "");
            if (response.length() > 10) {
                try {
                    jArray = new JSONArray(response);
                    for(int i=0;i<jArray.length();i++){
                        JSONArray innerJsonArray = jArray.getJSONArray(i);
                        for(int j=0;j<innerJsonArray.length();j++){  
                            JSONObject jsonObject = innerJsonArray.getJSONObject(j);

                            arr_users_id.add(jsonObject.getString("ID"));
                            arr_users_username.add(jsonObject.getString("USERNAME"));
                            arr_users_photo.add(jsonObject.getString("PHOTO"));
                            arr_users_followed.add(jsonObject.getString("DO_I_FOLLOW_HIM"));
                            arr_users_numadded.add(jsonObject.getString("NUM_ALL"));
                            arr_users_numcompleted.add(jsonObject.getString("NUM_DONE"));
                            arr_users_recentbucket.add(jsonObject.getString("REC"));
                            arr_users_fbuserid.add(jsonObject.getString("FB_USERID"));
                            arr_users_imagetype.add(jsonObject.getString("IMAGE_TYPE"));
                            arr_users_twuserid.add(jsonObject.getString("TW_USERID"));
                            arr_users_twphoto.add(jsonObject.getString("TW_PHOTO"));

                            Log.i("VOLLEY arr_users_username.get(" + j + ")", arr_users_username.get(j));
                            Log.i("VOLLEY arr_users_followed.get(" + j + ")", arr_users_followed.get(j));
                            Log.i("VOLLEY size", arr_users_followed.size() + "");


                            pb.setVisibility(View.GONE);
                            loading_ll.setVisibility(View.GONE);
                            llMain.setVisibility(View.VISIBLE);

                            if (arr_users_id.size() < 1) tv_noone.setVisibility(View.VISIBLE);
                            else tv_noone.setVisibility(View.GONE);

                            //set adapter only when users are download the first time aka. no scrolling has been made
                            if (arr_users_id.size() < 11) {
                                myadapter = new MyAdapter(ctx, arr_users_id, arr_users_username, arr_users_photo, arr_users_followed, arr_users_numadded, arr_users_numcompleted, arr_users_fbuserid, arr_users_imagetype, arr_users_twuserid, arr_users_twphoto);
                                lv.setAdapter(myadapter);
                            } else {
                                myadapter.notifyDataSetChanged();
                            }


                        }
                    }
                    loadingMore = false;
                    Log.i("TIMER", "FINISH");
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            } else {
                all_items_downloaded = true;
                lv.removeFooterView(loadMoreView);
            }

        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
           // System.out.println("Error ["+error+"]");
            Log.i("VOLLEY_ERROR", error.toString());
        }
    }) {

        @Override
        protected Map<String, String> getParams() {
            Map<String, String> params = new HashMap<String, String>();
            params.put("session_userid", session_userid);
            params.put("start", start);
            params.put("finish", finish);
            return params;
        }

    };

    postReq.setShouldCache(false);
    AppController.getInstance().addToRequestQueue(postReq);
}

加载速度有点快,但问题仍然存在。有时加载进度条(FooterView)没有消失所以我需要向上滚动一点然后再向下滚动。我发现这种情况发生在我以某种方式跳过相应的项目(应该已经开始加载下一个包的那个):

01-17 18:15:57.809: I/FOLLOW Onscroll totalItemCount(3230): 11
01-17 18:15:57.809: I/FOLLOW Onscroll lastInScreen(3230): 6
01-17 18:15:57.859: I/FOLLOW Onscroll totalItemCount(3230): 11
01-17 18:15:57.859: I/FOLLOW Onscroll lastInScreen(3230): 7
01-17 18:15:57.869: I/FOLLOW Onscroll totalItemCount(3230): 11
01-17 18:15:57.869: I/FOLLOW Onscroll lastInScreen(3230): 8
01-17 18:15:57.869: I/FOLLOW Onscroll totalItemCount(3230): 11
01-17 18:15:57.869: I/FOLLOW Onscroll lastInScreen(3230): 8
01-17 18:15:57.899: I/FOLLOW Onscroll totalItemCount(3230): 11
01-17 18:15:57.899: I/FOLLOW Onscroll lastInScreen(3230): 10
01-17 18:15:57.929: I/FOLLOW Onscroll totalItemCount(3230): 11
01-17 18:15:57.929: I/FOLLOW Onscroll lastInScreen(3230): 11

根据这个块,新的加载应该在最后一个可见项目为9时开始,但不知何故我在滚动过程中跳过它。

if ((totalItemCount - lastInScreen == 2) && !(loadingMore) && (totalItemCount != 0)) {
    if (!all_items_downloaded) {
    makeJsonArrayRequest(String.valueOf(totalItemCount), "10");
    }
}

我通过修改if (lastInScreen == totalItemCount) { }

解决了这个问题
if (lastInScreen == totalItemCount) {     
                    if (all_items_downloaded) {
                        //Log.i("all_items_downloaded", "yes");
                        lv.removeFooterView(loadMoreView);
                    } else {
                        Log.i("FOLLOW loadingMore", loadingMore + "");
                        if (!loadingMore) {
                            Log.i("TAG", "NOW IT STOPS SO I QUERY AGAIN");
                            makeJsonArrayRequest(String.valueOf(totalItemCount), "10");
                        }
                    }
                }

如果有人需要......

更新 自从我将大多数Asynctasks改为Volley以来,已经过去几周了。我刚刚检查了Volley与Async相比的下载时间。我从我的VPS下载了一堆数据,这是结果(以ms为单位):

enter image description here

1 个答案:

答案 0 :(得分:1)

您的第一个错误是使用AsyncTask。你应该使用Volley。每个人都应该使用Volley。如果你不知道我在说什么,check out the lecture at I/O 2013 here。只是google它找到官方教程。这本身可以解决你的滞后问题。由于AsyncTask在BG中完成,它还会修复你应用程序此部分之外的滞后,除非你真正取消它,我怀疑你做的(不是因为我认为你是一个糟糕的程序员,但因为没有人我曾经合作过,取消了它们。

除此之外,我认为你的psudocode很好。您正以正确的方式接近它以保存用户数据和加载时间。

看来,在你上一次的AsyncTask中,你正在doInBackground()中进行所有数据解析,这不是应该处理的方式(如果我正确地读取你的代码而且它没有'之后用这些数据做一些奇怪的事情)。您应该在onPostExecute()中解析数据,然后在ListView上调用notifyDataSetChanged(),否则它不知道数据是新的。另外,请确保您有回调,以便您的自定义视图知道新数据何时到达。

老实说,如果你切换到Volley(你真的应该),它会让一切变得更容易,包括你的调试,因为有更少的样板代码。