【问题标题】:How to use Android Pagination with Volley when fetching JSON data获取 JSON 数据时如何使用带有 Volley 的 Android 分页
【发布时间】:2020-02-24 17:04:01
【问题描述】:

我正在构建一个 Android 应用程序,该应用程序的用户 cmets 页面可能包含多达 1,000 个 cmets。尝试一次获取所有 cmets 并将其加载到 recyclerView 中并不是一个好习惯。所以,我想要一种方法,可以分批加载 cmets,一次可能 10 个,当用户滚动 recyclerView 时,它应该再加载 10 个等。

问题是我使用 Volley 将 cmets 获取为 JSON 对象,我认为 Volley 没有批处理。我也不确定是否会在我的 PHP 文件上完成一些分页。我在 Youtube 上观看了几个教程,并在 Stack Overflow 上查看了答案,包括 ThisThis 但他们并没有真正解决我的问题。

这是我的 PHP 代码:

<?php

    define('DB_HOST','*******');
    define('DB_USER','********');
    define('DB_PASS','********');
    define('DB_NAME','***********');

    $conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);

    if(mysqli_connect_errno()){
        die('Unable to connect to database '.mysqli_connect_error());
    }

        $conn->set_charset("utf8mb4");

        $storyID = $_GET["storyid"];

    $stmt = $conn->prepare("SELECT comments_table.comment_id, comments_table.username, comments_table.comment, comments_table.date_time, comments_table.story_id, comments_table.imageURL, comments_table.number_of_likes, users.title, comments_table.is_reply, comments_table.reply_username, comments_table.reply_comment, comments_table.reply_id  FROM comments_table INNER JOIN users ON comments_table.username = users.username WHERE comments_table.story_id = '$storyID'");

    $stmt->execute();

    $stmt->bind_result($comment_id, $username, $comment, $date_time, $story_id, $imageURL, $number_of_likes, $title, $is_reply, $reply_username, $reply_comment, $reply_id);

    $comments = array();

    while($stmt->fetch()){
        $temp = array();
        $temp['comment_id'] = $comment_id;
        $temp['username'] = $username;
        $temp['comment'] = $comment;
                $temp['date_time'] = $date_time;
                $temp['story_id'] = $story_id;
                $temp['imageURL'] = $imageURL;
                $temp['number_of_likes'] = $number_of_likes;
                $temp['title'] = $title;
                $temp['is_reply'] = $is_reply;
                $temp['reply_username'] = $reply_username;
                $temp['reply_comment'] = $reply_comment;
                $temp['reply_id'] = $reply_id;

        array_push($comments, $temp);

    }

    echo json_encode($comments);

这是我在使用 Volley 检索 cmets 时使用的代码 sn-p:

private void loadComments() {
        progressBar.setVisibility(View.VISIBLE);
        StringRequest stringRequest = new StringRequest(Request.Method.GET, URL_COMMENTS + String.valueOf(storyID),
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        try {
                            JSONArray array = new JSONArray(response);
                            for (int i = 0; i < array.length(); i++) {
                                JSONObject comment = array.getJSONObject(i);

                                if (comment.getInt("story_id") == storyID) {
                                    commentList.add(new GetComments(
                                            comment.getInt("comment_id"),
                                            comment.getString("username"),
                                            comment.getString("comment"),
                                            comment.getInt("story_id"),
                                            comment.getString("imageURL"),
                                            comment.getString("date_time"),
                                            comment.getInt("number_of_likes"),
                                            comment.getString("title"),
                                            comment.getInt("is_reply"),
                                            comment.getInt("reply_id"),
                                            comment.getString("reply_username"),
                                            comment.getString("reply_comment")
                                    ));

                                }

                            }

                            if (commentList.isEmpty()){
                                noCommentTextView.setVisibility(View.VISIBLE);
                            }
                            //creating adapter object and setting it to recyclerview
                            adapterJSON = new CommentAdapter(getApplicationContext(), commentList, Comment.this, rootView, storyID);
                            recyclerView.setAdapter(adapterJSON);

                        } catch (JSONException e) {
                            progressBar.setVisibility(View.GONE);
                            Toast.makeText(Comment.this,"Error loading comments: Check internet connection...",Toast.LENGTH_LONG).show();
                            e.printStackTrace();
                        }
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        progressBar.setVisibility(View.GONE);
                        Toast.makeText(Comment.this,"Error loading comments: Check internet connection...",Toast.LENGTH_LONG).show();
                    }
                });


        final RequestQueue requestQueue = Volley.newRequestQueue(Comment.this);
        requestQueue.add(stringRequest);
        requestQueue.start();

        requestQueue.addRequestFinishedListener(new RequestQueue.RequestFinishedListener<Object>() {
            @Override
            public void onRequestFinished(Request<Object> request) {
                progressBar.setVisibility(View.GONE);
            }
        });

    }

这些是我的 recyclerView 和 layoutManager 实现:

        recyclerView = findViewById(R.id.recyclerView);
        linearLayoutManager = new LinearLayoutManager(this);
        linearLayoutManager.setReverseLayout(true);
        linearLayoutManager.setStackFromEnd(true);
        recyclerView.setLayoutManager(linearLayoutManager);

任何有关如何实现此目标或提供正确方法的解决方案都将受到高度赞赏。 谢谢。

【问题讨论】:

    标签: php android android-studio


    【解决方案1】:

    这是上面 steve Kamau 建议的修改后的代码。这就是我修改代码的方式:

    我修改后的 PHP 代码:

    <?php
    
        define('DB_HOST','*******');
        define('DB_USER','*******');
        define('DB_PASS','******');
        define('DB_NAME','*********');
    
        $conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
    
        if(mysqli_connect_errno()){
            die('Unable to connect to database '.mysqli_connect_error());
        }
    
            //mysqli_set_charset($conn,"utf8mb4_unicode_ci");
            $conn->set_charset("utf8mb4");
    
            $storyID = $_GET["storyid"];
            $limit = $_GET["limit"];
            $offset = $_GET["offset"];
    
        $stmt = $conn->prepare("SELECT comments_table.comment_id, comments_table.username, comments_table.comment, comments_table.date_time, comments_table.story_id, comments_table.imageURL, comments_table.number_of_likes, users.title, comments_table.is_reply, comments_table.reply_username, comments_table.reply_comment, comments_table.reply_id  FROM comments_table INNER JOIN users ON comments_table.username = users.username WHERE comments_table.story_id = '$storyID' ORDER BY comments_table.date_time DESC LIMIT $limit OFFSET $offset");
    
        $stmt->execute();
    
        $stmt->bind_result($comment_id, $username, $comment, $date_time, $story_id, $imageURL, $number_of_likes, $title, $is_reply, $reply_username, $reply_comment, $reply_id);
    
        $comments = array();
    
        while($stmt->fetch()){
            $temp = array();
            $temp['comment_id'] = $comment_id;
            $temp['username'] = $username;
            $temp['comment'] = $comment;
                    $temp['date_time'] = $date_time;
                    $temp['story_id'] = $story_id;
                    $temp['imageURL'] = $imageURL;
                    $temp['number_of_likes'] = $number_of_likes;
                    $temp['title'] = $title;
                    $temp['is_reply'] = $is_reply;
                    $temp['reply_username'] = $reply_username;
                    $temp['reply_comment'] = $reply_comment;
                    $temp['reply_id'] = $reply_id;
    
            array_push($comments, $temp);
    
        }
    
        echo json_encode($comments);
    

    然后我初始化了两个整数值:

    int limit = 10;
    int offset = 0;
    

    然后我为我的 recyclerView 创建了一个 OnscrollListener 并创建了另一个方法来测试用户是否滚动到最后一项:

    private RecyclerView.OnScrollListener endOnScrollListener = new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }
    
            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
    
                if(isLastItemDisplaying(recyclerView)){
                    Log.i("Reached end: ", "Load more");
                    loadMoreComments();
                }
            }
        };
    
        private boolean isLastItemDisplaying(RecyclerView recyclerView){
            //Check if the adapter item count is greater than 0
            if(recyclerView.getAdapter().getItemCount() != 0){
                //get the last visible item on screen using the layout manager
                int lastVisibleItemPosition = ((LinearLayoutManager)recyclerView.getLayoutManager()).findLastCompletelyVisibleItemPosition();
    
                if(lastVisibleItemPosition != RecyclerView.NO_POSITION && lastVisibleItemPosition == recyclerView.getAdapter().getItemCount()-1){
                    return true;
                }
    
            }
            return false;
    
        }
    

    最后,这是我的 loadMoreComments() 方法:

    private void loadMoreComments() {
            refreshLayout.setRefreshing(true);
            StringRequest stringRequest = new StringRequest(Request.Method.GET, URL_COMMENTS + String.valueOf(storyID)+"&limit="+String.valueOf(limit)
                    +"&offset="+String.valueOf(offset),
                    new Response.Listener<String>() {
                        @Override
                        public void onResponse(String response) {
                            try {
                                JSONArray array = new JSONArray(response);
                                for (int i = 0; i < array.length(); i++) {
                                    JSONObject comment = array.getJSONObject(i);
    
                                    if (comment.getInt("story_id") == storyID) {
    
                                        commentList.add(adapterJSON.getItemCount(), (new GetComments(
                                                comment.getInt("comment_id"),
                                                comment.getString("username"),
                                                comment.getString("comment"),
                                                comment.getInt("story_id"),
                                                comment.getString("imageURL"),
                                                comment.getString("date_time"),
                                                comment.getInt("number_of_likes"),
                                                comment.getString("title"),
                                                comment.getInt("is_reply"),
                                                comment.getInt("reply_id"),
                                                comment.getString("reply_username"),
                                                comment.getString("reply_comment")
                                        )));
                                        adapterJSON.notifyItemInserted(adapterJSON.getItemCount());
                                    }
    
    
                                }
    
    
                            } catch (JSONException e) {
                                progressBar.setVisibility(View.GONE);
                                Toast.makeText(Comment.this,"Error loading comments: Check internet connection...",Toast.LENGTH_LONG).show();
                                e.printStackTrace();
                                Log.i("Error:", e.getMessage());
                            }
                        }
                    },
                    new Response.ErrorListener() {
                        @Override
                        public void onErrorResponse(VolleyError error) {
                            progressBar.setVisibility(View.GONE);
                            Toast.makeText(Comment.this,"Error loading comments: Check internet connection...",Toast.LENGTH_LONG).show();
                            Log.i("Error:", error.getMessage());
                        }
                    });
    
    
            final RequestQueue requestQueue = Volley.newRequestQueue(Comment.this);
            requestQueue.add(stringRequest);
            requestQueue.start();
    
            requestQueue.addRequestFinishedListener(new RequestQueue.RequestFinishedListener<Object>() {
                @Override
                public void onRequestFinished(Request<Object> request) {
                    refreshLayout.setRefreshing(false);
                    offset = offset + 5;
                }
            });
    
        }
    

    【讨论】:

      【解决方案2】:

      我之前使用 REST api 处理过分页。 但是对于您的情况,最简单的方法是这样做:

      当您第一次调用获取前 10 个时,将初始值 10 附加到您的 api 参数查询中,如下所示 https://example.com/index.php?limit=10;

      在您的 php 代码中,接收值 'limit' 并将其附加到您的 sql 查询中,如下所示:

      SELECT * from cmets_table WHERE cmets_table.story_id = '$storyID' ORDER BY create_date DESC LIMIT 10 OFFSET '$limit'

      LIMIT and OFFSET查看更多

      这意味着您的 android 应用程序将保留各种计数器以用于限制。 您可以通过以下方式轻松实现:

      int limit = 10; //initial limit
      
      //after success result from your api, just append a count
      limit = limit + 10;
      //and use this new limit in subsequent api calls to fetch comments

      当然,最重要的是跟踪滚动事件以了解用户是否到达了 cmets 列表的末尾,这个用户可以轻松完成 https://stackoverflow.com/a/46342525/4209724

      【讨论】:

      • 好的,让我试试这个,我会回来的。感谢您的宝贵时间。
      • 史蒂夫,你这个摇滚人。它像魔术一样工作。我已经为此奋斗了很长时间,我可以继续做其他事情。非常感谢。
      猜你喜欢
      • 2019-03-19
      • 1970-01-01
      • 2017-03-01
      • 1970-01-01
      • 2018-01-29
      • 2021-12-11
      • 2020-05-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多