【问题标题】:How to load more items in a ListView using AsyncTask or any other method如何使用 AsyncTask 或任何其他方法在 ListView 中加载更多项目
【发布时间】:2016-03-22 14:17:20
【问题描述】:

我对 android 开发非常陌生,我知道这个问题之前可能已经回答过了,但我似乎找不到适合我情况的答案。我正在创建一个具有ListView 以显示项目列表的android 应用程序。当用户到达ListView 的页脚时,我需要显示更多项目(比如 10 个更多项目)。我已经实现了setOnScrollListener()。我唯一需要您指导的问题是,当用户到达ListView 的底部时,我怎样才能获得更多物品。我应该为它创建另一个AsyncTask 吗?如果是,那么我怎么能做到这一点...我目前正在展示 10 个项目,并且我正在使用 AsyncTaskJSON 格式的 API 获取这些项目。下面是那个AsyncTask的代码。

public class GetRecipeData extends AsyncTask<Object, Void, JSONObject> {
        public final int NUMBER_OF_POSTS = 10;

        int responseCode = -1;
        JSONObject recipeJsonResponse = null;

        @Override
        protected JSONObject doInBackground(Object... params) {


            try {
                URL blogFeedUrl = new URL("http://www.bestfoodrecipesever.com/api/get_category_posts/?slug="+RECIPE_CAT+"&count="+NUMBER_OF_POSTS);
                HttpURLConnection connection = (HttpURLConnection) blogFeedUrl.openConnection();
                connection.setRequestMethod("GET");
                connection.connect();

                responseCode = connection.getResponseCode();
                if (responseCode == HttpURLConnection.HTTP_OK){
                    InputStream inputStream = connection.getInputStream();
                    StringBuffer buffer = new StringBuffer();
                    if (inputStream == null) {
                        // Nothing to do.
                        return null;
                    }
                    BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));

                    String line;
                    while ((line = reader.readLine()) != null) {
                        // Since it's JSON, adding a newline isn't necessary (it won't affect parsing)
                        // But it does make debugging a *lot* easier if you print out the completed
                        // buffer for debugging.
                        buffer.append(line + "\n");
                    }

                    if (buffer.length() == 0) {
                        // Stream was empty.  No point in parsing.
                        return null;
                    }
                    String recipeDataJsonStr = buffer.toString();

                    recipeJsonResponse = new JSONObject(recipeDataJsonStr);

                }else {
                    Log.i(LOG_TAG, "Unsuccessful HTTP Response Code: " + responseCode);
                }
            }
            catch (MalformedURLException e){
                Log.e(LOG_TAG,"Exception Caught: ",e);
            }
            catch (IOException e) {
                Log.e(LOG_TAG, "IO Exception Caught: ",e);
            }
            catch (Exception e) {
                Log.e(LOG_TAG,"Exception Caught: ",e);
            }
            return recipeJsonResponse;
        }

        @Override
        protected void onPostExecute(JSONObject result) {
            super.onPostExecute(result);
            mRecipeData = result;
            handleRecipeData();
        }
    }

这是handleRecipeData()方法的代码:

private void handleRecipeData() {
        mProgressBar.setVisibility(View.INVISIBLE);
        if(mRecipeData == null){
            handleErrors();

        }else {
            try {
                getRecipeData();

            } catch (JSONException e) {
                Log.e(LOG_TAG,"Exception Caught: ",e);
            }
        }
    }

下面是getRecipeData() 方法的代码,它在handleRecipeData() 方法中使用:

private void getRecipeData() throws JSONException {
        JSONArray jsonPosts = mRecipeData.getJSONArray("posts");
        mRecipePostData = new ArrayList<>();
        for (int i = 0; i < jsonPosts.length(); i++){
            JSONObject post = jsonPosts.getJSONObject(i);
            String title = post.getString(KEY_TITLE);
            title = Html.fromHtml(title).toString();
            String author = post.getJSONObject(KEY_AUTHOR).getString("name");
            author = Html.fromHtml(author).toString();
            String imgUrl = post.getJSONObject(KEY_IMG_URL).getJSONObject("full").getString("url");
            String recipeContent = post.getString(KEY_CONTENT);
            recipeContent = Html.fromHtml(recipeContent).toString();
            String recipeUrl = post.getString(KEY_RECIPE_URL);

            HashMap<String, String> singleRecipePost = new HashMap<>();
            singleRecipePost.put(KEY_TITLE, title);
            singleRecipePost.put(KEY_AUTHOR, author);
            singleRecipePost.put(KEY_IMG_URL, imgUrl);
            singleRecipePost.put(KEY_CONTENT, recipeContent);
            singleRecipePost.put(KEY_RECIPE_URL, recipeUrl);

            mRecipePostData.add(singleRecipePost);
        }

        String[] keys = {KEY_TITLE};
        int[] ids = {R.id.list_recipe_title};

        mRecipeAdapter = new ExtendedSimpleAdapter(getContext(), mRecipePostData, R.layout.itemlistrow, keys, ids);
        listView.setAdapter(mRecipeAdapter);
        mRecipeAdapter.notifyDataSetChanged();
    }

我真的被这个问题困住了......有人可以帮我解决这个问题......我会很感激你。

我还创建了一个自定义适配器 ExtendedSimpleAdapter。这是此适配器的代码。如果有人可能想查看它:

public class ExtendedSimpleAdapter extends SimpleAdapter {
    Context context2;

    public ExtendedSimpleAdapter(Context context, List<? extends Map<String, String>> data, int resource, String[] from, int[] to){
        super(context, data, resource, from, to);
        context2=context;
    }

    public View getView(int position, View convertView, ViewGroup parent){
        // here you let SimpleAdapter built the view normally.
        View v = super.getView(position, convertView, parent);

        // Then we get reference for Picasso
        //TextView recipeTitle = (TextView) v.getTag();
        ImageView recipeThumb = (ImageView) v.getTag();
        if(recipeThumb == null){
            //recipeTitle = (TextView) v.findViewById(R.id.list_recipe_title);
            recipeThumb = (ImageView) v.findViewById(R.id.list_recipe_thumb);
            //v.setTag(recipeTitle);
            v.setTag(recipeThumb); // <<< THIS LINE !!!!
        }
            // get the url from the data you passed to the `Map`
            String TAG_IMAGE = "thumbnail_images";
            String url = ((Map<String,String>) getItem(position)).get(TAG_IMAGE);

        // do Picasso
        // maybe you could do that by using many ways to start

        Picasso.with(context2).load(url).resize(300, 200).centerCrop().into(recipeThumb);

        // return the view

        return v;
    }

}

提前致谢

【问题讨论】:

  • 你需要再次调用你的异步任务,当响应到来时,你需要添加你提供给适配器的同一个列表,并且需要在适配器对象上调用 notifydatasetchange。您也可以使用第三方列表,github.com/codepath/android_guides/wiki/…
  • 我试图调用我当前的 AsyncTask 但它在列表视图中显示的结果与以前相同
  • 已经在做 mRecipeAdapter.notifyDataSetChanged(); 可能做错了吗?
  • 请检查您是否从 api 获取新数据,假设在我的 api 中我有 page_limit,所以滚动时我将传递 page_limit=10,因此 api 返回第 10 页数据。所以你还需要检查你的 api 是返回相同的数据还是不同的数据。
  • API 正在显示 10 个最近的项目...我的意思是新添加的项目在顶部 .. 所以首先 api 将显示最新的项目 .. 如果我添加一个新项目,那么最后一个项目(第 10 项会消失)你认为这可能是因为 API 的原因吗?

标签: android listview android-asynctask


【解决方案1】:

不要在每次调用数据时都设置适配器,它会一直显示新数据。只需第一次设置它,然后仅在收到来自 JSON 的新数据时通知适配器。

或者您可以使用另一个列表来存储新数据并将此列表添加到您的主列表中 -

 yourMainList.addAll(anotherList);
 adapter.notifyDataSetChanged();

更新--

1- 取一个布尔值来检查列表是否滚动 boolean iScrolling = false 并使其 trueonScroll 内()-

listView.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {

            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                if(firstVisibleItem+visibleItemCount == totalItemCount && totalItemCount!=0) {
                    isScrolling = true;
                    mFooter.setVisibility(View.VISIBLE);
                    /*GetRecipeData getRecipeData = new GetRecipeData();
                    getRecipeData.execute();*/
                    GetRecipeData getRecipeData = new GetRecipeData();
                    getRecipeData.execute(yourCounts); // update
                }
            }
        });

现在 getRecipeData()-

内部发生了一些变化
 private void getRecipeData() throws JSONException {
    JSONArray jsonPosts = mRecipeData.getJSONArray("posts");
    mRecipePostData = new ArrayList<>();
    for (int i = 0; i < jsonPosts.length(); i++){
        JSONObject post = jsonPosts.getJSONObject(i);
        String title = post.getString(KEY_TITLE);
        title = Html.fromHtml(title).toString();
        String author = post.getJSONObject(KEY_AUTHOR).getString("name");
        author = Html.fromHtml(author).toString();
        String imgUrl = post.getJSONObject(KEY_IMG_URL).getJSONObject("full").getString("url");
        String recipeContent = post.getString(KEY_CONTENT);
        recipeContent = Html.fromHtml(recipeContent).toString();
        String recipeUrl = post.getString(KEY_RECIPE_URL);

        HashMap<String, String> singleRecipePost = new HashMap<>();
        singleRecipePost.put(KEY_TITLE, title);
        singleRecipePost.put(KEY_AUTHOR, author);
        singleRecipePost.put(KEY_IMG_URL, imgUrl);
        singleRecipePost.put(KEY_CONTENT, recipeContent);
        singleRecipePost.put(KEY_RECIPE_URL, recipeUrl);

        mRecipePostData.add(singleRecipePost);
    }

    String[] keys = {KEY_TITLE};
    int[] ids = {R.id.list_recipe_title};

    if (!isScrolling){
        mRecipeAdapter = new ExtendedSimpleAdapter(getContext(), mRecipePostData, R.layout.itemlistrow, keys, ids);
        listView.setAdapter(mRecipeAdapter);
        mRecipeAdapter.notifyDataSetChanged();
    }else{
        mRecipeAdapter.notifyDataSetChanged();
        isScrolling = false;
    }
}

2- 或者您可以借助另一个列表- 获取另一个列表并在其上添加数据,然后将此列表添加到您的主列表中,在 getRecipeData()-

 private void getRecipeData() throws JSONException {
        JSONArray jsonPosts = mRecipeData.getJSONArray("posts");
        if (!isScrolling) {
            mRecipePostData = new ArrayList<>();
        }else{
            yourSecondList = new ArrayList<>();
        }
        for (int i = 0; i < jsonPosts.length(); i++){
            JSONObject post = jsonPosts.getJSONObject(i);
            String title = post.getString(KEY_TITLE);
            title = Html.fromHtml(title).toString();
            String author = post.getJSONObject(KEY_AUTHOR).getString("name");
            author = Html.fromHtml(author).toString();
            String imgUrl = post.getJSONObject(KEY_IMG_URL).getJSONObject("full").getString("url");
            String recipeContent = post.getString(KEY_CONTENT);
            recipeContent = Html.fromHtml(recipeContent).toString();
            String recipeUrl = post.getString(KEY_RECIPE_URL);

            HashMap<String, String> singleRecipePost = new HashMap<>();
            singleRecipePost.put(KEY_TITLE, title);
            singleRecipePost.put(KEY_AUTHOR, author);
            singleRecipePost.put(KEY_IMG_URL, imgUrl);
            singleRecipePost.put(KEY_CONTENT, recipeContent);
            singleRecipePost.put(KEY_RECIPE_URL, recipeUrl);

            if (!isScrolling) {
                mRecipePostData.add(singleRecipePost);
            }else{
                yourSecondList.add(singleRecipePost);
            }

        }

        String[] keys = {KEY_TITLE};
        int[] ids = {R.id.list_recipe_title};

        if (!isScrolling){
            mRecipeAdapter = new ExtendedSimpleAdapter(getContext(), mRecipePostData, R.layout.itemlistrow, keys, ids);
            listView.setAdapter(mRecipeAdapter);
            mRecipeAdapter.notifyDataSetChanged();
        }else{
            mRecipePostData.addAll(yourSecondList);
            mRecipeAdapter.notifyDataSetChanged();
            isScrolling = false;
        }
    }

更新-

更改您的 AsyncTask 参数-

 public class GetRecipeData extends AsyncTask<String, Void, JSONObject> {

    // your code..

    @Override
    protected JSONObject doInBackground(String... params) {
        try {
            URL blogFeedUrl = new URL("http://www.bestfoodrecipesever.com/api/get_category_posts/?slug=" + RECIPE_CAT + "&count=" + params[0]);

            // your code...
        }
    }
}

并且在这里也做一些改变-

 if(isNetworkAvailable()) {
        mProgressBar.setVisibility(View.VISIBLE);
        GetRecipeData getRecipeData = new GetRecipeData();
        getRecipeData.execute(yourCount);
    } else {
        Toast.makeText(getContext(),getString(R.string.no_network), Toast.LENGTH_LONG).show();
    }

希望它会有所帮助。

【讨论】:

  • 你能指导我怎么做吗?你是说我应该创建另一个列表视图并在 onScroll 事件中将第二个列表视图添加到第一个列表视图?
  • 谢谢 Deepanshu ...让我试试这个然后回复你
  • 试过了,但不知何故它不起作用....你认为这可能是问题http://www.bestfoodrecipesever.com/api/get_category_posts/?slug=alltimetoprecipes&amp;count=10,因为我刚刚从 API 获得了 10 个最近的结果
  • 当我向下滚动到最后一个项目时,滚动触发但没有加载新项目......只有最近的 10 个可见......我认为 api 有问题......
  • 我使用了第二种方法....但是当我滚动到最后一个项目时,该功能会启动并显示正在发生的事情,但它会加载相同的项目...当我点击项目以查看详细信息,然后返回列表视图我的应用程序崩溃...这里是 logcat 日志...pastebin.com/A9DXfWtn
【解决方案2】:

试试这样:

 //used for populate the listView
 private void populateListView(HashMap<String, String> datas){
    if(mRecipeAdapter ==null){
       String[] keys = {KEY_TITLE};
       int[] ids = {R.id.list_recipe_title};
       mRecipeAdapter = new ExtendedSimpleAdapter(getContext(), datas, R.layout.itemlistrow, keys, ids);
       listView.setAdapter(mRecipeAdapter);
    }else
    {
      mRecipeAdapter.notifyDataSetChanged();
    }
 }
//create ListView Data::: i have removed your five last line, and repleced them by return mRecipePostData 
private ArrayList<HashMap<String,String>> getRecipeData() throws JSONException {
        JSONArray jsonPosts = mRecipeData.getJSONArray("posts");
        mRecipePostData = new ArrayList<HashMap<String,String>>();
        for (int i = 0; i < jsonPosts.length(); i++){
            JSONObject post = jsonPosts.getJSONObject(i);
            String title = post.getString(KEY_TITLE);
            title = Html.fromHtml(title).toString();
            String author = post.getJSONObject(KEY_AUTHOR).getString("name");
            author = Html.fromHtml(author).toString();
            String imgUrl = post.getJSONObject(KEY_IMG_URL).getJSONObject("full").getString("url");
            String recipeContent = post.getString(KEY_CONTENT);
            recipeContent = Html.fromHtml(recipeContent).toString();
            String recipeUrl = post.getString(KEY_RECIPE_URL);

            HashMap<String, String> singleRecipePost = new HashMap<>();
            singleRecipePost.put(KEY_TITLE, title);
            singleRecipePost.put(KEY_AUTHOR, author);
            singleRecipePost.put(KEY_IMG_URL, imgUrl);
            singleRecipePost.put(KEY_CONTENT, recipeContent);
            singleRecipePost.put(KEY_RECIPE_URL, recipeUrl);

            mRecipePostData.add(singleRecipePost);
        }
       return mRecipePostData;
    } 

//after getData, i am populating ListView
private void handleRecipeData() {
        mProgressBar.setVisibility(View.INVISIBLE);
        if(mRecipeData == null){
            handleErrors();

        }else {
            try {
                HashMap<String, String> datas=getRecipeData();
                populateListView(datas);

            } catch (JSONException e) {
                Log.e(LOG_TAG,"Exception Caught: ",e);
            }
        }
    }

【讨论】:

  • 好的,我会调整我的代码,让你知道它是否有效
  • 试过你的代码......但是有些 AsyncTask 没有在 setOnScrollListener() 方法中运行......知道可能是什么问题......数据第一次加载正常。 ..
  • 好的,在某些地方,我可以看到这个 URL:“bestfoodrecipesever.com/api/get_category_posts/… 你确定要更改 RECIPE_CAT 吗?它似乎是页码,你需要 10 行=NUMBER_OF_POSTS
  • 我必须在每个 populateListView 增加 RECIPE_CAT 才能切换到下一个 recip_post_page。另外,要执行您要做什么,行 mRecipePostData = new ArrayList>();应该删除到 getRecipeData() 并设置在代码的另一部分,(例如在构造函数中)
  • RECIPE_CAT 是类别名称的常量...这是我们获取食谱的类别...NUMBER_OF_POSTS 是要检索的帖子数...。为什么我需要增加 RECIPE_CAT .. 它只是类别的名称... 我需要增加 NUMBER_OF_POSTS 吗? ...我会检查你的 pastebin 并让你知道
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-17
  • 2019-11-23
  • 2014-03-03
相关资源
最近更新 更多