主题: 我从外部数据库下载了一堆数据,我想一次只显示其中一些数据(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为单位):
答案 0 :(得分:1)
您的第一个错误是使用AsyncTask。你应该使用Volley。每个人都应该使用Volley。如果你不知道我在说什么,check out the lecture at I/O 2013 here。只是google它找到官方教程。这本身可以解决你的滞后问题。由于AsyncTask在BG中完成,它还会修复你应用程序此部分之外的滞后,除非你真正取消它,我怀疑你做的(不是因为我认为你是一个糟糕的程序员,但因为没有人我曾经合作过,取消了它们。
除此之外,我认为你的psudocode很好。您正以正确的方式接近它以保存用户数据和加载时间。
看来,在你上一次的AsyncTask中,你正在doInBackground()
中进行所有数据解析,这不是应该处理的方式(如果我正确地读取你的代码而且它没有'之后用这些数据做一些奇怪的事情)。您应该在onPostExecute()
中解析数据,然后在ListView上调用notifyDataSetChanged()
,否则它不知道数据是新的。另外,请确保您有回调,以便您的自定义视图知道新数据何时到达。
老实说,如果你切换到Volley(你真的应该),它会让一切变得更容易,包括你的调试,因为有更少的样板代码。