endless scroll / scroll managing

똘이주인·2021년 8월 9일
0

infinite/endless scroll 이란?

무한스크롤, 즉 유튜브나 페이스북, 트위터 등에서 스크롤이 끊임없이 이어지는 것을 말한다.

브라우저 같은 경우 자바스크립트로 구현할 수 있겠고 안드로이드도 그런 식으로 만드는 것이 가능하겠으나 많이 사용되고 있으므로 여러가지 툴이 존재

RecyclerView 이용

  1. RecyclerView를 구현
  2. Scroll이 끝에 닿았을 경우 NULL 요소를 추가하고 Adapter에 알린다.
  3. 새로운 데이터 요소 집합을 가져온 후
  4. NULL요소를 제거 한 후 추가하고 Adapter에 알린다.
// RecyclerViewAdapter.java
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private final int VIEW_TYPE_ITEM = 0;
    private final int VIEW_TYPE_LOADING = 1;

    private List<String> items;

    public RecyclerViewAdapter(List<String> items) {
        this.items = items;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        if (viewType == VIEW_TYPE_ITEM) {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_row, parent, false);
            return new ItemViewHolder(view);
        } else {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_loading, parent, false);
            return new LoadingViewHolder(view);
        }
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        if (holder instanceof ItemViewHolder) {
            populateItemRows((ItemViewHolder) holder, position);
        } else if (holder instanceof LoadingViewHolder) {
            showLoadingView((LoadingViewHolder) holder, position);
        }
    }

    @Override
    public int getItemViewType(int position) {
        return items.get(position) == null ? VIEW_TYPE_LOADING : VIEW_TYPE_ITEM;
    }

    @Override
    public int getItemCount() {
        return items == null ? 0 : items.size();
    }

    private void showLoadingView(LoadingViewHolder holder, int position) {

    }

    private void populateItemRows(ItemViewHolder holder, int position) {
        String item = items.get(position);
        holder.setItem(item);
    }

    private class ItemViewHolder extends RecyclerView.ViewHolder {
        private TextView textView;

        public ItemViewHolder(@NonNull View itemView) {
            super(itemView);
            textView = itemView.findViewById(R.id.textView);
        }

        public void setItem(String item) {
            textView.setText(item);
        }
    }

    private class LoadingViewHolder extends RecyclerView.ViewHolder {
        private ProgressBar progressBar;

        public LoadingViewHolder(@NonNull View itemView) {
            super(itemView);
            progressBar = itemView.findViewById(R.id.progressBar);
        }
    }
}
// MainActivity.java
public class MainActivity extends AppCompatActivity {
    //..

    private void initScrollListener() {
        recyclerView.setOnScrollListener(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);

                LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();

                if (!isLoading) {
                    if (layoutManager != null && layoutManager.findLastCompletelyVisibleItemPosition() == items.size() - 1) {
                        //리스트 마지막
                        loadMore();
                        isLoading = true;
                    }
                }
            }
        });
    }

    private void loadMore() {
        items.add(null);
        adapter.notifyItemInserted(items.size() - 1);

        Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                items.remove(items.size() - 1);
                int scrollPosition = items.size();
                adapter.notifyItemRemoved(scrollPosition);
                int currentSize = scrollPosition;
                int nextLimit = currentSize + 10;

                while (currentSize - 1 < nextLimit) {
                    items.add("Item " + currentSize);
                    currentSize++;
                }

                adapter.notifyDataSetChanged();
                isLoading = false;
            }
        }, 2000);
    }
}

RecyclerViewAdapter에서 우리는 두 개의 ViewHolder를 사용한다.

  1. ItemViewHolder
  • RecyclerView 요소 ViewHolder
  1. LoadingViewHolder
  • NULL로 추가된 요소 ViewHolder

RecyclerView의 ScrollListener에서 리스트의 마지막을 감지하고 NULL요소를 추가한다.

Adapter에 알리게 되면 LoadingViewHolder를 통해 로딩 아이템을 볼 수 있다.

그 후, 추가되는 데이터 요소 집합을 가져오고 NULL로 추가되었던 아이템 제거 한 후 추가한다.

Adapter에 알리게 되면 다음 스크롤이 진행된다.

ScrollView

ScrollView: 수직(위아래)로 스크롤하는 기능.
HorizontalScrollView: 수평(좌우)으로 스크롤하는 기능.

주의할점 : 스크롤뷰에는 단 하나의 위젯만 넣을 수 있다
그래서, 스크롤뷰 안에 리니어레이아웃(LinearLayout)을 1개 넣고(일반적으로 LinearLayout을 사용, 다른 layout도 가능), 그 안에 자신이 원하는 위젯을 여러 개 넣는 방법을 사용

만약 스크롤뷰 내부에 둘 이상의 위젯을 넣게 된다면 아래와 같은 로그를 확인하게 될것이다.
java.lang.IllegalStateException: ScrollView can host only one direct child

0개의 댓글

관련 채용 정보