English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
사고
오늘 우리는 상단으로 더 많은 데이터를 로드하는 ListView를 구현해 보겠습니다. GitHub 링크:PulmListView, fork && star를 환영합니다.
먼저 우리가 상단으로 더 많은 데이터를 로드하는 ListView를 구현하려면 실현해야 할 기능을 정리해 보겠습니다:
1. 사용자 정의된 ListView, 그리고 이 ListView가 현재가 가장 아래에 있는지 확인할 수 있습니다.
2. ListView가 더 많은 데이터를 로드하는 과정에서 UI 표시를 위한 사용자 정의된 FooterView.
3. FooterView와 ListView를 연결하여 로드 시간�断정, FooterView의 표시 및 숨기기 기능을 포함합니다.
4. 사용자가 추가 데이터를 실제로 로드하는 기능을 구현하기 쉽게 해주는 추가 데이터 로드 인터페이스를 제공합니다.
5. 추가 데이터를 추가하고 관련 상태 표시 및 UI 표시를 업데이트하는 데 사용되는 추가 데이터 로드 완료 호출 메서드를 제공합니다.
위의5각 기능을 차례로 분석하여 해당 구현 방법을 확인하겠습니다.
기능1(사용자 정의 ListView)
우리는 ListView를 상속하여 사용자 정의된 PulmListView를 구현할 수 있습니다.
public class PulmListView extends ListView { public PulmListView(Context context) { this(context, null); } public PulmListView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public PulmListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // 초기화 init(); } }
ListView의 세 가지 생성자를 구현하는 것만으로는 충분하지 않습니다. ListView가 현재 ListView가 마지막 요소까지 스크롤되었는지�断별할 수 있도록 해야 합니다.
마지막 요소까지 스크롤되었는지 확인하려면, ListView에 OnScrollListener를 설정하여 구현할 수 있습니다. 코드는 다음과 같습니다:
private void init() { super.setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { // 사용자가 설정한 OnScrollListener를 호출합니다. if (mUserOnScrollListener != null) { mUserOnScrollListener.onScrollStateChanged(view, scrollState); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // 사용자가 설정한 OnScrollListener를 호출합니다. if (mUserOnScrollListener != null) { mUserOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } // firstVisibleItem은 현재 화면에 표시할 수 있는 첫 번째 요소의 위치입니다 // visibleItemCount는 현재 화면에 표시할 수 있는 요소의 개수입니다 // totalItemCount는 ListView에 포함된 요소 총 개수입니다 int lastVisibleItem = firstVisibleItem + visibleItemCount; if (!mIsLoading && !mIsPageFinished && lastVisibleItem == totalItemCount) { if (mOnPullUpLoadMoreListener != null) { mIsLoading = true; mOnPullUpLoadMoreListener.onPullUpLoadMore(); } } } }); }
코딩 주석을 통해 알 수 있듯이, (firstVisibleItem + visibleItemCount)는 현재 화면에 표시된 요소의 개수를 얻을 수 있습니다. 표시된 요소의 개수가 ListView의 요소 총 개수와 같다면, ListView가 아래로 스크롤되어 있는 것으로 판단할 수 있습니다.
기능2(사용자 정의 FooterView)
여기서는 간단한 FooterView를 구현할 수 있습니다. 즉, 데이터를 더 로드하는 UI 레이아웃입니다. 예를 들어, ProgressBar와 한 줄의 텍스트를 표시할 수 있습니다. 구체적인 코드는 다음과 같습니다:
/** * 데이터를 더 로드하는 View 레이아웃, 사용자 정의 가능. */ public class LoadMoreView extends LinearLayout { public LoadMoreView(Context context) { this(context, null); } public LoadMoreView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public LoadMoreView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { LayoutInflater.from(getContext()).inflate(R.layout.lv_load_more, this); } }
레이아웃 파일:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@"+id/id_load_more_layout" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center" android:layout_margin="@dimen/loading_view_margin_layout"> <ProgressBar android:id="@"+id/id_loading_progressbar" android:layout_width="@dimen/loading_view_progress_size" android:layout_height="@dimen/loading_view_progress_size" android:indeterminate="true" style="?android:progressBarStyleSmall"/> <TextView android:id="@"+id/id_loading_label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/page_loading"/> </LinearLayout>
기능3(ListView와 FooterView를 연결)
첫째로, ListView에서 FooterView를 저장하는 변수를 사용하고, 생성자에서其实例화해야 합니다.
private View mLoadMoreView; private void init() { mLoadMoreView = new LoadMoreView(getContext()); }
두 번째로, FooterView의 보이기와 숨기기를 제어해야 합니다. FooterView의 보이기와 숨기는 시기를 고려해 보세요:
•보이는 시기: ListView가 가장 아래에 있으며 더 많은 데이터를 로드해야 하는 경우.
•숨겨지는 시기: ListView가 더 이상 데이터를 로드하는 작업을 완료한 후.
현재 로드가 끝났는지 여부를�断하기 위해 mIsPageFinished라는 boolean 변수를 정의합니다.
한 번에 데이터 로드 과정이 한 번만 수행되도록 보장하기 위해, mIsLoading이라는 boolean 변수를 정의하여 현재 데이터 로드 상태가 있는지 여부를 나타냅니다.
FooterView의 표시 및 숨김 시기와 상태를 제어하는 변수가 명확해지면서, 코드가 쉽게 구현됩니다.
표시 시기:
private void init() { mIsLoading = false; // 초기화 시 로드 상태가 아닙니다. mIsPageFinished = false; // 초기화 시 더 많은 데이터가 로드되어야 할 기본 설정 mLoadMoreView = new LoadMoreView(getContext()); // FooterView를 인스턴스화합니다. super.setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { // 사용자가 설정한 OnScrollListener를 호출합니다. if (mUserOnScrollListener != null) { mUserOnScrollListener.onScrollStateChanged(view, scrollState); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // 사용자가 설정한 OnScrollListener를 호출합니다. if (mUserOnScrollListener != null) { mUserOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } int lastVisibleItem = firstVisibleItem + visibleItemCount; // ListView의 끝에 있을 때 더 많은 데이터가 로드되어야 하고 현재 로드 프로그램이 실행 중이지 않을 때, 더 많은 데이터 로드 작업을 수행합니다. if (!mIsLoading && !mIsPageFinished && lastVisibleItem == totalItemCount) { if (mOnPullUpLoadMoreListener != null) { mIsLoading = true; // 더 많은 데이터를 로드하는 중 상태를 true로 설정합니다 showLoadMoreView(); // 더 많은 데이터를 로드하는 레이아웃을 표시합니다 mOnPullUpLoadMoreListener.onPullUpLoadMore(); // 사용자가 설정한 더 많은 데이터를 로드하는 호출 인터페이스를 호출합니다 } } } }); } private void showLoadMoreView() { // 여기서 더 많은 데이터를 로드하는 루트 레이아웃의 id를 id_load_more_layout으로 설정하여 사용자가 더 많은 데이터를 로드하는 레이아웃을 직접 정의할 수 있도록 합니다. if (findViewById(R.id.id_load_more_layout) == null) { addFooterView(mLoadMoreView); } }
숨기는 시기:
/** * 데이터 더 로드가 끝나면 ListView 리컬백 메서드. * * @param isPageFinished 페이지 로드가 끝나지 않았는지 * @param newItems 페이지 로드 데이터 * @param isFirstLoad 데이터를 처음 로드하는지 여부(드롭다운 프레임워크 사용을 위해 설정, 페이지 플래시를 방지) */ public void onFinishLoading(boolean isPageFinished, List<63;> newItems, boolean isFirstLoad) { mIsLoading = false; // 현재 더 많은 데이터를 로드하는 작업이 실행 중인지 표시합니다 setIsPageFinished(isPageFinished); // 페이지 분할이 끝난지 여부를 설정하고 FooterView를 제거합니다 } private void setIsPageFinished(boolean isPageFinished) { mIsPageFinished = isPageFinished; removeFooterView(mLoadMoreView); }
기능4(상단 푸시 로드 더 많은 구현 호출 인터페이스)
이는 매우 간단합니다. 우리는 interface를 정의하여 사용자가 실제로 더 많은 데이터를 로드하는 구현 방법을 호출하기 쉽게 합니다.
/** * 상단 푸시 로드 더 많은 호출 인터페이스 */ public interface OnPullUpLoadMoreListener { void onPullUpLoadMore(); } private OnPullUpLoadMoreListener mOnPullUpLoadMoreListener; /** * 상단 푸시 로드 더 많은 호출 인터페이스를 설정합니다. * @param l 상단 푸시 로드 더 많은 호출 인터페이스 */ public void setOnPullUpLoadMoreListener(OnPullUpLoadMoreListener l) { this.mOnPullUpLoadMoreListener = l; }
기능5(더 많은 데이터 로드 완료 호출)
PulmListView에서 데이터 셋을 유지하기 위해, Adapter를 정의해야 하며, Adapter에서 List를 사용하여 데이터 셋을 저장하고, 추가 및 제거 메서드를 제공해야 합니다.
사용자 정의된 Adapter:
/** * 추상적인 Adapter. */ public abstract class PulmBaseAdapter<T> extends BaseAdapter { protected List<T> items; public PulmBaseAdapter() { this.items = new ArrayList<>(); } public PulmBaseAdapter(List<T> items) { this.items = items; } public void addMoreItems(List<T> newItems, boolean isFirstLoad) { if (isFirstLoad) { this.items.clear(); } this.items.addAll(newItems); notifyDataSetChanged(); } public void removeAllItems() { this.items.clear(); notifyDataSetChanged(); } }
addMoreItems 메서드에서 isFirstLoad 변수를 추가하는 이유는 무엇인가요?
이는 스크롤 아래로 더 로드하려면 일반적으로 드롭다운 리프레시와 함께 사용되기 때문입니다. 드롭다운 리프레시 과정에서 ListView 데이터 셋을 clear하고 addAll하는 작업이 필요합니다. isFirstLoad 매개변수가 없으면, 사용자가 ListView 데이터 셋을 업데이트하기 위해 드롭다운 리프레시를 호출하면, 데이터 셋을 업데이트하는 작업을 두 단계로 나누어야 합니다:
1.removeAllItems()를 호출하고 notifyDataSetChanged()를 호출합니다.
2.addMoreItems()를 호출하고 notifyDataSetChanged()를 호출합니다.
동시에 두 번 notifyDataSetChanged() 호출하면 스크린 플래시가 발생할 수 있으므로, 여기서 isFirstLoad 메서드를 제안합니다. 데이터를 처음 로드할 때, 모든 데이터를 clear하고 addAll한 후 notify()을 호출합니다.
사용자 정의된 adapter가 있으면, 데이터 더 로드가 끝난 리컬백 함수를 작성할 수 있습니다:
/** * 데이터 더 로드가 끝나면 ListView 리컬백 메서드. * * @param isPageFinished 페이지 로드가 끝나지 않았는지 * @param newItems 페이지 로드 데이터 * @param isFirstLoad 데이터를 처음 로드하는지 여부(드롭다운 프레임워크 사용을 위해 설정, 페이지 플래시를 방지) */ public void onFinishLoading(boolean isPageFinished, List<63;> newItems, boolean isFirstLoad) { mIsLoading = false; setIsPageFinished(isPageFinished); // 수정된 데이터 추가 if (newItems != null && newItems.size() > 0) { PulmBaseAdapter adapter = (PulmBaseAdapter) ((HeaderViewListAdapter) getAdapter()).getWrappedAdapter(); adapter.addMoreItems(newItems, isFirstLoad); } }
이 부분에서 주의해야 할 것은, FooterView나 HeaderView를 추가한 후에는 listview.getAdapter()를 통해 우리가 정의한 adapter를 가져올 수 없다는 것입니다. 아래 단계에 따라 해야 합니다:
PulmBaseAdapter adapter = (PulmBaseAdapter) ((HeaderViewListAdapter) getAdapter()).getWrappedAdapter();
참조
1.PagingListView
이上是 본 글의 전체 내용입니다. 많은 도움이 되었기를 바랍니다. 또한,呐喊 튜토리얼에 많은 지지를 부탁드립니다.
언급: 본 내용은 인터넷에서 수집되었으며, 저작권은 원 저작자에게 있으며, 인터넷 사용자가 자발적으로 기여하고 자체적으로 업로드한 내용입니다. 본 사이트는 소유권을 가지지 않으며, 인공 편집을 하지 않았으며, 관련 법적 책임을 부담하지 않습니다. 저작권 문제가 있는 내용을 발견하시면, 메일을 notice#w로 보내 주시기 바랍니다.3codebox.com(메일 보내는 경우, #을 @으로 변경해 주세요. 신고를 하시고 관련 증거를 제공하시면, 해당 내용이 사실로 확인되면 즉시 해당 내용을 삭제하겠습니다。)