본문 바로가기
  • 콩's 코딩노트
Android

Android Studio - Recyclerview 페이징 처리

by Cong_S 2022. 7. 20.

페이징 처리란? 어떤 데이터를 검색했을 때, 100개의 데이터가 검색되었다면

한번에 다 보여주는것이 아니고 10개씩 20개씩 나누어 보여주는 것을 의미한다. 

 

다음 페이지로 넘어가기위해서 번호로 다음 페이지를 넣어주는 방법 또한 있지만

스크롤하여 콘텐츠를 열람하는 Recyclerview 는 보통 페이지를 끝까지 내렸을 때 

다음 페이지를 더 불러오는 방식을 채택한다. 

 

Recyclerview에서 페이징 처리하는 방법에 대해 알아보자.

예제는 검색 키워드로 youtube API 를 이용해 데이터를 불러와 페이지에 20개씩 나열하는 앱이다.

 

아래는 일반적인 예제로 페이징 처리가 되지 않고 데이터를 불러오는 시점까지만 구현된 코드이다.

더보기
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        editSearch = findViewById(R.id.editSearch);
        imgSearch = findViewById(R.id.imgSearch);
        progressBar = findViewById(R.id.progressBar);
        recyclerView = findViewById(R.id.recyclerView);
        recyclerView.setHasFixedSize(true);
        recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this));
        // 리스트를 맨 밑에까지 가면, 알수 있는 방법!
        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }

            // todo 이제 기존 키워드를 지우고 새로운 키워드를 검색했을 때의 처리를 해주어야한다.

            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);


                int lastPosition = ((LinearLayoutManager)recyclerView.getLayoutManager()).findLastCompletelyVisibleItemPosition();
                int totalCount = recyclerView.getAdapter().getItemCount();
                // 스크롤을 맨 끝까지 한것!
                if(lastPosition + 1 == totalCount){

                    // 네트워크를 통해서 데이터를 가져온다.
                    if (pageToken == null) {
                        return;
                    }

                    // 1. 프로그레스바를 돌린다.
                    progressBar.setVisibility(View.VISIBLE);



                    // 2. URL을 조합한다.
                    // ?part=snippet&key=[자신의 API KEY]&q=축구&maxResults=20
                    String url = Config.BASE_URL + "?part=snippet&key="+
                            Config.GOOGLE_API_KEY + "&q="+keyword+"&maxResults=20&pageToken="+pageToken;

                    RequestQueue queue = Volley.newRequestQueue(MainActivity.this);
                    JsonObjectRequest request = new JsonObjectRequest(
                            Request.Method.GET,
                            url,
                            null,
                            new Response.Listener<JSONObject>() {
                                @Override
                                public void onResponse(JSONObject response) {
                                    // 데이터를 받아오면, 프로그레스바를 안보이게 한다.
                                    progressBar.setVisibility(View.INVISIBLE);

                                    try {
                                        if (response.has("nextPageToken")){
                                            pageToken = response.getString("nextPageToken");
                                        } else {
                                            pageToken = null;
                                        }

                                        JSONArray dataList = response.getJSONArray("items");

                                        for(int i = 0; i < dataList.length(); i++){
                                            String title = dataList.getJSONObject(i)
                                                    .getJSONObject("snippet").getString("title");
                                            String description = dataList.getJSONObject(i)
                                                    .getJSONObject("snippet").getString("description");

                                            String imgUrl = dataList.getJSONObject(i)
                                                    .getJSONObject("snippet").getJSONObject("thumbnails")
                                                    .getJSONObject("medium").getString("url");

                                            String videoId = dataList.getJSONObject(i)
                                                    .getJSONObject("id").getString("videoId");

                                            Video video = new Video(title, description, imgUrl, videoId);
                                            videoList.add(video);
                                        }

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

                                    adapter.notifyDataSetChanged();

                                }
                            },
                            new Response.ErrorListener() {
                                @Override
                                public void onErrorResponse(VolleyError error) {
                                    // 데이터를 받아오면, 프로그레스바를 안보이게 한다.
                                    progressBar.setVisibility(View.INVISIBLE);

                                }
                            }
                    );
                    queue.add(request);



                }

            }
        });


        progressBar.setVisibility(View.INVISIBLE);

        imgSearch.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 검색 버튼 누르면, 네트워크로 데이터 요청해서
                // 정보를 받아온다.

                // 1. 에디트텍스트로부터 검색어를 가져온다.
                keyword = editSearch.getText().toString().trim();

                // 2. 검색어가 없으면, 검색어를 넣으라고 한다.
                if(keyword.isEmpty()){
                    Toast.makeText(MainActivity.this, "검색어를 입력하세요.", Toast.LENGTH_SHORT).show();
                    return;
                }

                // 3. 프로그레스바를 돌린다.
                progressBar.setVisibility(View.VISIBLE);

                videoList.clear();
                if (adapter != null) {
                    adapter.notifyDataSetChanged();
                }

                // 4. URL을 조합한다.
                // ?part=snippet&key=[자신의 API KEY]&q=축구&maxResults=20
                String url = Config.BASE_URL + "?part=snippet&key="+
                        Config.GOOGLE_API_KEY + "&q="+keyword+"&maxResults=20";

                // 5. 네트워크 통신한다.
                RequestQueue queue = Volley.newRequestQueue(MainActivity.this);
                JsonObjectRequest request = new JsonObjectRequest(
                        Request.Method.GET,
                        url,
                        null,
                        new Response.Listener<JSONObject>() {
                            @Override
                            public void onResponse(JSONObject response) {
                                // 데이터를 받아오면, 프로그레스바를 안보이게 한다.
                                progressBar.setVisibility(View.INVISIBLE);

                                try {
                                    if (response.has("nextPageToken")){
                                        pageToken = response.getString("nextPageToken");
                                    } else {
                                        pageToken = null;
                                    }

                                    JSONArray dataList = response.getJSONArray("items");

                                    for(int i = 0; i < dataList.length(); i++){
                                        String title = dataList.getJSONObject(i)
                                                .getJSONObject("snippet").getString("title");
                                        String description = dataList.getJSONObject(i)
                                                .getJSONObject("snippet").getString("description");

                                        String imgUrl = dataList.getJSONObject(i)
                                                .getJSONObject("snippet").getJSONObject("thumbnails")
                                                .getJSONObject("medium").getString("url");

                                        String videoId = dataList.getJSONObject(i)
                                                .getJSONObject("id").getString("videoId");

                                        Video video = new Video(title, description, imgUrl, videoId);
                                        videoList.add(video);
                                    }

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

                                adapter = new VideoAdapter(MainActivity.this, videoList);
                                recyclerView.setAdapter(adapter);


                            }
                        },
                        new Response.ErrorListener() {
                            @Override
                            public void onErrorResponse(VolleyError error) {
                                // 데이터를 받아오면, 프로그레스바를 안보이게 한다.
                                progressBar.setVisibility(View.INVISIBLE);

                            }
                        }
                );
                queue.add(request);
            }
        });
    }
}

이 때 Recyclerview 뷰의 메소드를 오버라이드 하기 위해

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        });

을 입력하여

맨 위 두 개의 메소드를 오버라이드 해주자.

이 때 오버라이드 하는 위치는 onCreate 바로 아래에서 Recyclerview 를 불러오자마자이다.

두 개 중 위의 함수는 당장 사용하지 않고 

아래의 

@Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
            }

함수에 코드를 작성한다.

 

int lastPosition = ((LinearLayoutManager)recyclerView.getLayoutManager()).findLastCompletelyVisibleItemPosition();
                int totalCount = recyclerView.getAdapter().getItemCount();
                // 스크롤을 맨 끝까지 한것!
                if(lastPosition + 1 == totalCount){

                    // 네트워크를 통해서 데이터를 가져온다.
                    if (pageToken == null) {
                        return;
                    }

스크롤을 맨 끝까지 내렸을 때의 수치와 item 갯수를 비교해 서로 맞았을 때

(페이지를 끝까지 내려서 모든 아이템이 노출되었을 때)

 

함수를 작동시키며 youtube api에선 pageToken 제공하므로 이를 이용하였다.

(없을 땐 멤버변수에 int count 를 만들어 사용)

 

그 후 먼저 네트워크와 통신해 데이터를 가져왔던 코드를 붙여넣어 몇 가지 코드를 수정해준다.

수정하여 추가된 코드는 다음과 같다.

더보기
recyclerView.addOnScrollListener(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);


                int lastPosition = ((LinearLayoutManager)recyclerView.getLayoutManager()).findLastCompletelyVisibleItemPosition();
                int totalCount = recyclerView.getAdapter().getItemCount();
                // 스크롤을 맨 끝까지 한것!
                if(lastPosition + 1 == totalCount){

                    // 네트워크를 통해서 데이터를 가져온다.
                    if (pageToken == null) {
                        return;
                    }

                    // 1. 프로그레스바를 돌린다.
                    progressBar.setVisibility(View.VISIBLE);



                    // 2. URL을 조합한다.
                    // ?part=snippet&key=[자신의 API KEY]&q=축구&maxResults=20
                    String url = Config.BASE_URL + "?part=snippet&key="+
                            Config.GOOGLE_API_KEY + "&q="+keyword+"&maxResults=20&pageToken="+pageToken;

                    RequestQueue queue = Volley.newRequestQueue(MainActivity.this);
                    JsonObjectRequest request = new JsonObjectRequest(
                            Request.Method.GET,
                            url,
                            null,
                            new Response.Listener<JSONObject>() {
                                @Override
                                public void onResponse(JSONObject response) {
                                    // 데이터를 받아오면, 프로그레스바를 안보이게 한다.
                                    progressBar.setVisibility(View.INVISIBLE);

                                    try {
                                        if (response.has("nextPageToken")){
                                            pageToken = response.getString("nextPageToken");
                                        } else {
                                            pageToken = null;
                                        }

                                        JSONArray dataList = response.getJSONArray("items");

                                        for(int i = 0; i < dataList.length(); i++){
                                            String title = dataList.getJSONObject(i)
                                                    .getJSONObject("snippet").getString("title");
                                            String description = dataList.getJSONObject(i)
                                                    .getJSONObject("snippet").getString("description");

                                            String imgUrl = dataList.getJSONObject(i)
                                                    .getJSONObject("snippet").getJSONObject("thumbnails")
                                                    .getJSONObject("medium").getString("url");

                                            String videoId = dataList.getJSONObject(i)
                                                    .getJSONObject("id").getString("videoId");

                                            Video video = new Video(title, description, imgUrl, videoId);
                                            videoList.add(video);
                                        }

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

                                    adapter.notifyDataSetChanged();

                                }
                            },
                            new Response.ErrorListener() {
                                @Override
                                public void onErrorResponse(VolleyError error) {
                                    // 데이터를 받아오면, 프로그레스바를 안보이게 한다.
                                    progressBar.setVisibility(View.INVISIBLE);

                                }
                            }
                    );
                    queue.add(request);



                }

            }
        });

 

전체 코드는 다음과 같다.

더보기
public class MainActivity extends AppCompatActivity {

    EditText editSearch;
    ImageView imgSearch;
    ProgressBar progressBar;

    // 리사이클러뷰 관련 멤버변수
    RecyclerView recyclerView;
    // 어댑터, 어레이리스트 필요
    VideoAdapter adapter;
    ArrayList<Video> videoList = new ArrayList<Video>();

    // 페이징에 필요한 변수
    String pageToken;
    String keyword;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        editSearch = findViewById(R.id.editSearch);
        imgSearch = findViewById(R.id.imgSearch);
        progressBar = findViewById(R.id.progressBar);
        recyclerView = findViewById(R.id.recyclerView);
        recyclerView.setHasFixedSize(true);
        recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this));

        // 리스트를 맨 밑에까지 가면, 알수 있는 방법!
        recyclerView.addOnScrollListener(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);


                int lastPosition = ((LinearLayoutManager)recyclerView.getLayoutManager()).findLastCompletelyVisibleItemPosition();
                int totalCount = recyclerView.getAdapter().getItemCount();
                // 스크롤을 맨 끝까지 한것!
                if(lastPosition + 1 == totalCount){

                    // 네트워크를 통해서 데이터를 가져온다.
                    if (pageToken == null) {
                        return;
                    }

                    // 1. 프로그레스바를 돌린다.
                    progressBar.setVisibility(View.VISIBLE);



                    // 2. URL을 조합한다.
                    // ?part=snippet&key=[자신의 API KEY]&q=축구&maxResults=20
                    String url = Config.BASE_URL + "?part=snippet&key="+
                            Config.GOOGLE_API_KEY + "&q="+keyword+"&maxResults=20&pageToken="+pageToken;

                    RequestQueue queue = Volley.newRequestQueue(MainActivity.this);
                    JsonObjectRequest request = new JsonObjectRequest(
                            Request.Method.GET,
                            url,
                            null,
                            new Response.Listener<JSONObject>() {
                                @Override
                                public void onResponse(JSONObject response) {
                                    // 데이터를 받아오면, 프로그레스바를 안보이게 한다.
                                    progressBar.setVisibility(View.INVISIBLE);

                                    try {
                                        if (response.has("nextPageToken")){
                                            pageToken = response.getString("nextPageToken");
                                        } else {
                                            pageToken = null;
                                        }

                                        JSONArray dataList = response.getJSONArray("items");

                                        for(int i = 0; i < dataList.length(); i++){
                                            String title = dataList.getJSONObject(i)
                                                    .getJSONObject("snippet").getString("title");
                                            String description = dataList.getJSONObject(i)
                                                    .getJSONObject("snippet").getString("description");

                                            String imgUrl = dataList.getJSONObject(i)
                                                    .getJSONObject("snippet").getJSONObject("thumbnails")
                                                    .getJSONObject("medium").getString("url");

                                            String videoId = dataList.getJSONObject(i)
                                                    .getJSONObject("id").getString("videoId");

                                            Video video = new Video(title, description, imgUrl, videoId);
                                            videoList.add(video);
                                        }

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

                                    adapter.notifyDataSetChanged();

                                }
                            },
                            new Response.ErrorListener() {
                                @Override
                                public void onErrorResponse(VolleyError error) {
                                    // 데이터를 받아오면, 프로그레스바를 안보이게 한다.
                                    progressBar.setVisibility(View.INVISIBLE);

                                }
                            }
                    );
                    queue.add(request);



                }

            }
        });


        progressBar.setVisibility(View.INVISIBLE);

        imgSearch.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 검색 버튼 누르면, 네트워크로 데이터 요청해서
                // 정보를 받아온다.

                // 1. 에디트텍스트로부터 검색어를 가져온다.
                keyword = editSearch.getText().toString().trim();

                // 2. 검색어가 없으면, 검색어를 넣으라고 한다.
                if(keyword.isEmpty()){
                    Toast.makeText(MainActivity.this, "검색어를 입력하세요.", Toast.LENGTH_SHORT).show();
                    return;
                }

                // 3. 프로그레스바를 돌린다.
                progressBar.setVisibility(View.VISIBLE);

                videoList.clear();
                if (adapter != null) {
                    adapter.notifyDataSetChanged();
                }

                // 4. URL을 조합한다.
                // ?part=snippet&key=[자신의 API KEY]&q=축구&maxResults=20
                String url = Config.BASE_URL + "?part=snippet&key="+
                        Config.GOOGLE_API_KEY + "&q="+keyword+"&maxResults=20";

                // 5. 네트워크 통신한다.
                RequestQueue queue = Volley.newRequestQueue(MainActivity.this);
                JsonObjectRequest request = new JsonObjectRequest(
                        Request.Method.GET,
                        url,
                        null,
                        new Response.Listener<JSONObject>() {
                            @Override
                            public void onResponse(JSONObject response) {
                                // 데이터를 받아오면, 프로그레스바를 안보이게 한다.
                                progressBar.setVisibility(View.INVISIBLE);

                                try {
                                    if (response.has("nextPageToken")){
                                        pageToken = response.getString("nextPageToken");
                                    } else {
                                        pageToken = null;
                                    }

                                    JSONArray dataList = response.getJSONArray("items");

                                    for(int i = 0; i < dataList.length(); i++){
                                        String title = dataList.getJSONObject(i)
                                                .getJSONObject("snippet").getString("title");
                                        String description = dataList.getJSONObject(i)
                                                .getJSONObject("snippet").getString("description");

                                        String imgUrl = dataList.getJSONObject(i)
                                                .getJSONObject("snippet").getJSONObject("thumbnails")
                                                .getJSONObject("medium").getString("url");

                                        String videoId = dataList.getJSONObject(i)
                                                .getJSONObject("id").getString("videoId");

                                        Video video = new Video(title, description, imgUrl, videoId);
                                        videoList.add(video);
                                    }

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

                                adapter = new VideoAdapter(MainActivity.this, videoList);
                                recyclerView.setAdapter(adapter);


                            }
                        },
                        new Response.ErrorListener() {
                            @Override
                            public void onErrorResponse(VolleyError error) {
                                // 데이터를 받아오면, 프로그레스바를 안보이게 한다.
                                progressBar.setVisibility(View.INVISIBLE);

                            }
                        }
                );
                queue.add(request);
            }
        });
    }
}

 

댓글