Android 5.0 - Recycleer View에 머리글/바닥글 추가
헤더를 추가할 방법을 찾느라 잠시 시간을 할애했습니다.RecyclerView
하다.
지금까지 알아낸 건 다음과 같습니다.
@Override
protected void onCreate(Bundle savedInstanceState) {
...
layouManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layouManager);
LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
headerPlaceHolder = inflater.inflate(R.layout.view_header_holder_medium, null, false);
layouManager.addView(headerPlaceHolder, 0);
...
}
LayoutManager
인 것 .RecyclerView
수 addHeaderView(View view)
방법, , 방법, 방법, 방법, 방법, 방법, 방법, 방법, ,LayoutManager
의 »addView(View view, int position)
method(메서드) my header view(헤더 뷰)를 선택합니다.
아, 그리고 여기가 상황이 더 나빠지는 곳입니다.
java.lang.NullPointerException: Attempt to read from field 'android.support.v7.widget.RecyclerView$ViewHolder android.support.v7.widget.RecyclerView$LayoutParams.mViewHolder' on a null object reference
at android.support.v7.widget.RecyclerView.getChildViewHolderInt(RecyclerView.java:2497)
at android.support.v7.widget.RecyclerView$LayoutManager.addViewInt(RecyclerView.java:4807)
at android.support.v7.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:4803)
at com.mathieumaree.showz.fragments.CategoryFragment.setRecyclerView(CategoryFragment.java:231)
at com.mathieumaree.showz.fragments.CategoryFragment.access$200(CategoryFragment.java:47)
at com.mathieumaree.showz.fragments.CategoryFragment$2.success(CategoryFragment.java:201)
at com.mathieumaree.showz.fragments.CategoryFragment$2.success(CategoryFragment.java:196)
at retrofit.CallbackRunnable$1.run(CallbackRunnable.java:41)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5221)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
개의 몇 several several several several several 를 얻은 후NullPointerExceptions
를 걸려고 addView(View view)
액티비티 작성의 다양한 순간(어댑터의 데이터까지 모두 설정되면 뷰 추가도 시도)에, 이것이 올바른 방법인지 전혀 알 수 없었습니다(그런 것 같지도 않습니다).
PS를 할 수 : ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★」GridLayoutManager
에 in in LinearLayoutManager
★★★★★★★★★★★★★★★★★★★★★★★★★★!
나는 내 신발에 바닥글을 추가해야 했다.RecyclerView
도움이 될 것 같아서 코드 스니펫을 공유합니다.전체적인 흐름을 보다 잘 이해하기 위해 코드 내의 코멘트를 확인해 주세요.
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
public class RecyclerViewWithFooterAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int FOOTER_VIEW = 1;
private ArrayList<String> data; // Take any list that matches your requirement.
private Context context;
// Define a constructor
public RecyclerViewWithFooterAdapter(Context context, ArrayList<String> data) {
this.context = context;
this.data = data;
}
// Define a ViewHolder for Footer view
public class FooterViewHolder extends ViewHolder {
public FooterViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Do whatever you want on clicking the item
}
});
}
}
// Now define the ViewHolder for Normal list item
public class NormalViewHolder extends ViewHolder {
public NormalViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Do whatever you want on clicking the normal items
}
});
}
}
// And now in onCreateViewHolder you have to pass the correct view
// while populating the list item.
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v;
if (viewType == FOOTER_VIEW) {
v = LayoutInflater.from(context).inflate(R.layout.list_item_footer, parent, false);
FooterViewHolder vh = new FooterViewHolder(v);
return vh;
}
v = LayoutInflater.from(context).inflate(R.layout.list_item_normal, parent, false);
NormalViewHolder vh = new NormalViewHolder(v);
return vh;
}
// Now bind the ViewHolder in onBindViewHolder
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
try {
if (holder instanceof NormalViewHolder) {
NormalViewHolder vh = (NormalViewHolder) holder;
vh.bindView(position);
} else if (holder instanceof FooterViewHolder) {
FooterViewHolder vh = (FooterViewHolder) holder;
}
} catch (Exception e) {
e.printStackTrace();
}
}
// Now the critical part. You have return the exact item count of your list
// I've only one footer. So I returned data.size() + 1
// If you've multiple headers and footers, you've to return total count
// like, headers.size() + data.size() + footers.size()
@Override
public int getItemCount() {
if (data == null) {
return 0;
}
if (data.size() == 0) {
//Return 1 here to show nothing
return 1;
}
// Add extra view to show the footer view
return data.size() + 1;
}
// Now define getItemViewType of your own.
@Override
public int getItemViewType(int position) {
if (position == data.size()) {
// This is where we'll add footer.
return FOOTER_VIEW;
}
return super.getItemViewType(position);
}
// So you're done with adding a footer and its action on onClick.
// Now set the default ViewHolder for NormalViewHolder
public class ViewHolder extends RecyclerView.ViewHolder {
// Define elements of a row here
public ViewHolder(View itemView) {
super(itemView);
// Find view by ID and initialize here
}
public void bindView(int position) {
// bindView() method to implement actions
}
}
}
의 해 주세요.RecyclerView
이 GitHub 저장소는 헤더와 바닥글을 모두 추가할 수 있는지 확인할 수 있습니다.
매우 간단하게 해결할 수 있습니다.
뷰를 반환하기 전에 매번 뷰 타입을 체크하기 때문에 어댑터 내부에 다른 뷰 타입으로 논리를 두는 것은 좋지 않습니다.이하의 솔루션에서는, 추가의 체크가 불필요합니다.
Android 내부에 Linear Layout(수직) 헤더 뷰 + recycerview + footer 뷰만 추가하면 됩니다.support.v4.199.Nested Scroll View.
이것 좀 봐.
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<View
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layoutManager="LinearLayoutManager"/>
<View
android:id="@+id/footer"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
부드러운 스크롤을 위해 이 코드 줄 추가
RecyclerView v = (RecyclerView) findViewById(...);
v.setNestedScrollingEnabled(false);
하면 RV 는 RV에 뷰.layout_height
RVRV†의
네비게이션 드로어나 설정 등 작은 사이즈 리스트의 사용을 권장합니다.
하여 2개의 하여 롤리팝을 .Recyclerview
하나는 꽤 사용하기 쉽지만, 데이터 세트를 바꾸면 어떻게 동작할지는 잘 모르겠습니다..notifyDataSetChanged
오른쪽 어댑터 오브젝트에 있습니다.
상대방에게 그런 문제가 있으면 안 돼요.일반 어댑터로 클래스를 확장하고 추상적인 메서드를 구현하면 준비가 완료됩니다.다음은 그 예입니다.
동작
- HeaderRecyclerViewAdapterV1.java 사용 현황
new HeaderRecyclerViewAdapterV1(new RegularAdapter());
- HeaderRecyclerViewAdapterV2.java 사용 현황
RegularAdapter extends HeaderRecyclerViewAdapterV2
HeaderRecyclerViewAdapterV1
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
/**
* Created by sebnapi on 08.11.14.
* <p/>
* This is a Plug-and-Play Approach for adding a Header or Footer to
* a RecyclerView backed list
* <p/>
* Just wrap your regular adapter like this
* <p/>
* new HeaderRecyclerViewAdapterV1(new RegularAdapter())
* <p/>
* Let RegularAdapter implement HeaderRecyclerView, FooterRecyclerView or both
* and you are ready to go.
* <p/>
* I'm absolutely not sure how this will behave with changes in the dataset.
* You can always wrap a fresh adapter and make sure to not change the old one or
* use my other approach.
* <p/>
* With the other approach you need to let your Adapter extend HeaderRecyclerViewAdapterV2
* (and therefore change potentially more code) but possible omit these shortcomings.
* <p/>
* TOTALLY UNTESTED - USE WITH CARE - HAVE FUN :)
*/
public class HeaderRecyclerViewAdapterV1 extends RecyclerView.Adapter {
private static final int TYPE_HEADER = Integer.MIN_VALUE;
private static final int TYPE_FOOTER = Integer.MIN_VALUE + 1;
private static final int TYPE_ADAPTEE_OFFSET = 2;
private final RecyclerView.Adapter mAdaptee;
public HeaderRecyclerViewAdapterV1(RecyclerView.Adapter adaptee) {
mAdaptee = adaptee;
}
public RecyclerView.Adapter getAdaptee() {
return mAdaptee;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_HEADER && mAdaptee instanceof HeaderRecyclerView) {
return ((HeaderRecyclerView) mAdaptee).onCreateHeaderViewHolder(parent, viewType);
} else if (viewType == TYPE_FOOTER && mAdaptee instanceof FooterRecyclerView) {
return ((FooterRecyclerView) mAdaptee).onCreateFooterViewHolder(parent, viewType);
}
return mAdaptee.onCreateViewHolder(parent, viewType - TYPE_ADAPTEE_OFFSET);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (position == 0 && holder.getItemViewType() == TYPE_HEADER && useHeader()) {
((HeaderRecyclerView) mAdaptee).onBindHeaderView(holder, position);
} else if (position == mAdaptee.getItemCount() && holder.getItemViewType() == TYPE_FOOTER && useFooter()) {
((FooterRecyclerView) mAdaptee).onBindFooterView(holder, position);
} else {
mAdaptee.onBindViewHolder(holder, position - (useHeader() ? 1 : 0));
}
}
@Override
public int getItemCount() {
int itemCount = mAdaptee.getItemCount();
if (useHeader()) {
itemCount += 1;
}
if (useFooter()) {
itemCount += 1;
}
return itemCount;
}
private boolean useHeader() {
if (mAdaptee instanceof HeaderRecyclerView) {
return true;
}
return false;
}
private boolean useFooter() {
if (mAdaptee instanceof FooterRecyclerView) {
return true;
}
return false;
}
@Override
public int getItemViewType(int position) {
if (position == 0 && useHeader()) {
return TYPE_HEADER;
}
if (position == mAdaptee.getItemCount() && useFooter()) {
return TYPE_FOOTER;
}
if (mAdaptee.getItemCount() >= Integer.MAX_VALUE - TYPE_ADAPTEE_OFFSET) {
new IllegalStateException("HeaderRecyclerViewAdapter offsets your BasicItemType by " + TYPE_ADAPTEE_OFFSET + ".");
}
return mAdaptee.getItemViewType(position) + TYPE_ADAPTEE_OFFSET;
}
public static interface HeaderRecyclerView {
public RecyclerView.ViewHolder onCreateHeaderViewHolder(ViewGroup parent, int viewType);
public void onBindHeaderView(RecyclerView.ViewHolder holder, int position);
}
public static interface FooterRecyclerView {
public RecyclerView.ViewHolder onCreateFooterViewHolder(ViewGroup parent, int viewType);
public void onBindFooterView(RecyclerView.ViewHolder holder, int position);
}
}
HeaderRecyclerViewAdapterV2
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
/**
* Created by sebnapi on 08.11.14.
* <p/>
* If you extend this Adapter you are able to add a Header, a Footer or both
* by a similar ViewHolder pattern as in RecyclerView.
* <p/>
* If you want to omit changes to your class hierarchy you can try the Plug-and-Play
* approach HeaderRecyclerViewAdapterV1.
* <p/>
* Don't override (Be careful while overriding)
* - onCreateViewHolder
* - onBindViewHolder
* - getItemCount
* - getItemViewType
* <p/>
* You need to override the abstract methods introduced by this class. This class
* is not using generics as RecyclerView.Adapter make yourself sure to cast right.
* <p/>
* TOTALLY UNTESTED - USE WITH CARE - HAVE FUN :)
*/
public abstract class HeaderRecyclerViewAdapterV2 extends RecyclerView.Adapter {
private static final int TYPE_HEADER = Integer.MIN_VALUE;
private static final int TYPE_FOOTER = Integer.MIN_VALUE + 1;
private static final int TYPE_ADAPTEE_OFFSET = 2;
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_HEADER) {
return onCreateHeaderViewHolder(parent, viewType);
} else if (viewType == TYPE_FOOTER) {
return onCreateFooterViewHolder(parent, viewType);
}
return onCreateBasicItemViewHolder(parent, viewType - TYPE_ADAPTEE_OFFSET);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (position == 0 && holder.getItemViewType() == TYPE_HEADER) {
onBindHeaderView(holder, position);
} else if (position == getBasicItemCount() && holder.getItemViewType() == TYPE_FOOTER) {
onBindFooterView(holder, position);
} else {
onBindBasicItemView(holder, position - (useHeader() ? 1 : 0));
}
}
@Override
public int getItemCount() {
int itemCount = getBasicItemCount();
if (useHeader()) {
itemCount += 1;
}
if (useFooter()) {
itemCount += 1;
}
return itemCount;
}
@Override
public int getItemViewType(int position) {
if (position == 0 && useHeader()) {
return TYPE_HEADER;
}
if (position == getBasicItemCount() && useFooter()) {
return TYPE_FOOTER;
}
if (getBasicItemType(position) >= Integer.MAX_VALUE - TYPE_ADAPTEE_OFFSET) {
new IllegalStateException("HeaderRecyclerViewAdapter offsets your BasicItemType by " + TYPE_ADAPTEE_OFFSET + ".");
}
return getBasicItemType(position) + TYPE_ADAPTEE_OFFSET;
}
public abstract boolean useHeader();
public abstract RecyclerView.ViewHolder onCreateHeaderViewHolder(ViewGroup parent, int viewType);
public abstract void onBindHeaderView(RecyclerView.ViewHolder holder, int position);
public abstract boolean useFooter();
public abstract RecyclerView.ViewHolder onCreateFooterViewHolder(ViewGroup parent, int viewType);
public abstract void onBindFooterView(RecyclerView.ViewHolder holder, int position);
public abstract RecyclerView.ViewHolder onCreateBasicItemViewHolder(ViewGroup parent, int viewType);
public abstract void onBindBasicItemView(RecyclerView.ViewHolder holder, int position);
public abstract int getBasicItemCount();
/**
* make sure you don't use [Integer.MAX_VALUE-1, Integer.MAX_VALUE] as BasicItemViewType
*
* @param position
* @return
*/
public abstract int getBasicItemType(int position);
}
이치노사용하겠습니다.HeaderRecyclerViewAdapterV2
스스로 진화시키고, 테스트하고, 장래의 변화를 투고합니다.
편집: @OvidiuLatcu 네, 몇 가지 문제가 있었습니다.실제로 나는 헤더의 오프셋을 암묵적으로 중지했다.position - (useHeader() ? 1 : 0)
'인 방법'을 .int offsetPosition(int position)
네, 네, 네. 왜 an an an an an OnItemTouchListener
Recycleerview에서 터치 가로채기, 터치 x, y 좌표 가져오기, 해당 아이 보기 찾기 및 전화하기recyclerView.getChildPosition(...)
어댑터에서 항상 비접촉 위치가 됩니다!이것은 RecycleerView코드의 쇼트커밍입니다.이 문제를 해결하는 방법은 쉽지 않습니다.이것이 내가 내 코드로 명시적 위치를 상쇄해야 하는 이유이다.
아직 시도하지 않았지만 어댑터의 getItemCount에서 반환되는 정수에 1(헤더와 바닥글 모두)을 추가합니다. '보다 낫다'를 덮어쓸 수 있습니다.getItemViewType
에서 adapter가 다른 할 때i==0
: https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#getItemViewType(int)
createViewHolder
됩니다.getItemViewType
헤더 뷰의 뷰 홀더를 다른 방법으로 작성 또는 설정할 수 있습니다.https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#createViewHolder(android.viewView Group, int)
마세요.bindViewHolder
.
이 GitHub 라이브러리를 사용하면 RecycleerView에 머리글 및/또는 바닥글을 추가할 수 있습니다.
프로젝트에 HFRecycler 뷰 라이브러리를 추가하거나 Gradle에서 가져올 수도 있습니다.
compile 'com.mikhaellopez:hfrecyclerview:1.0.0'
다음은 이미지의 결과입니다.
편집:
이 라이브러리의 맨 위 또는 맨 아래에 여백을 추가하는 경우: SimpleItemDecoration:
int offsetPx = 10;
recyclerView.addItemDecoration(new StartOffsetItemDecoration(offsetPx));
recyclerView.addItemDecoration(new EndOffsetItemDecoration(offsetPx));
다른 어댑터를 랩하고 헤더 뷰와 바닥글 뷰를 추가하는 방법을 제공하기 위해 자체 어댑터를 구현했습니다.
HeaderView에 Gist를 작성했습니다.RecycleerAdapter.java
의 뷰를 할 수 를 원했습니다.RecyclerView
에onCreateView
이 작업은 다음 명령어를 작성함으로써 이루어집니다.HeaderViewRecyclerAdapter
래핑할 어댑터를 전달하고 호출합니다.addHeaderView
그리고.addFooterView
당신의 과장된 견해를 넘겨주죠.그 후 를 설정합니다.HeaderViewRecyclerAdapter
인스턴스는 의 어댑터로RecyclerView
.
또한 헤더와 바닥글을 유지하면서 어댑터를 쉽게 교환할 수 있어야 한다는 점도 요구되었습니다.이러한 헤더와 바닥글의 인스턴스가 여러 개 있는 어댑터를 여러 개 사용하고 싶지 않았습니다.전화할 수 있도록setAdapter
머리글과 바닥글을 그대로 둔 채 랩된 어댑터를 변경하려면RecyclerView
변경 통지를 받다.
recyclerview:1.2.0
에는 여러 어댑터를 하나의 어댑터에 연결하는 ConcatAdapter 클래스가 도입되어 있습니다.따라서 별도의 헤더/풋어 어댑터를 생성하여 여러 목록에 걸쳐 재사용할 수 있습니다.
myRecyclerView.adapter = ConcatAdapter(headerAdapter, listAdapter, footerAdapter)
발표 기사를 보세요.이 문서에는 다음을 사용하여 머리글 및 바닥글의 로드 진행률을 표시하는 샘플이 포함되어 있습니다.ConcatAdapter
.
제가 이 글을 올리면 제가 대답할 버전은1.2.0
라이브러리가 알파 단계이기 때문에 API가 변경될 수 있습니다.여기서 상태를 확인할 수 있습니다.
내 "간단하게 유지하라"는 방법은... 자원을 낭비하는 건 알지만, 내 코드가 단순하게 유지되기 때문에 나는 상관하지 않아.먼저 가시성이 있는 바닥글을 item_layout에 추가합니다.
<LinearLayout
android:id="@+id/footer"
android:layout_width="match_parent"
android:layout_height="80dp"
android:orientation="vertical"
android:visibility="gone">
</LinearLayout>
그런 다음 마지막 항목에 표시되도록 설정합니다.
public void onBindViewHolder(ChannelAdapter.MyViewHolder holder, int position) {
boolean last = position==data.size()-1;
//....
holder.footer.setVisibility(View.GONE);
if (last && showFooter){
holder.footer.setVisibility(View.VISIBLE);
}
}
헤더의 경우는 반대로 한다.
@seb의 솔루션을 기반으로 RecycleerView 서브클래스를 만들었습니다.임의의 수의 헤더 및 바닥글을 지원하는 어댑터입니다.
https://gist.github.com/mheras/0908873267def75dc746
해결책인 것 같습니다만, 이것도 Layout Manager가 관리해야 한다고 생각합니다.유감스럽게도 지금 당장 필요한 상태이며, 처음부터 StaggeredGridLayoutManager를 구현할 시간도 없습니다(또한 처음부터 확장할 수도 없습니다).
아직 테스트 중이지만, 원하시면 시험해 보세요.혹시 문제가 있으면 알려주세요.
뷰 타입을 사용하여 이 문제를 해결할 수 있습니다.여기 데모입니다.https://github.com/yefengfreedom/RecyclerViewWithHeaderFooterLoadingEmptyViewErrorView
다음과 같이 리사이클러 뷰 표시 모드를 정의할 수 있습니다.
퍼블릭 스태틱 최종 int MODE_DATA = 0, MODE_LOADING = 1, MODE_ERROR = 2, MODE_EMPTY = 3, MODE_HEADER_VIEW = 4, MODE_FOOTER_VIEW = 5;
2. getItemView 덮어쓰기나방목
@Override
public int getItemViewType(int position) {
if (mMode == RecyclerViewMode.MODE_LOADING) {
return RecyclerViewMode.MODE_LOADING;
}
if (mMode == RecyclerViewMode.MODE_ERROR) {
return RecyclerViewMode.MODE_ERROR;
}
if (mMode == RecyclerViewMode.MODE_EMPTY) {
return RecyclerViewMode.MODE_EMPTY;
}
//check what type our position is, based on the assumption that the order is headers > items > footers
if (position < mHeaders.size()) {
return RecyclerViewMode.MODE_HEADER_VIEW;
} else if (position >= mHeaders.size() + mData.size()) {
return RecyclerViewMode.MODE_FOOTER_VIEW;
}
return RecyclerViewMode.MODE_DATA;
}
3. getItemCount 메서드 덮어쓰기
@Override
public int getItemCount() {
if (mMode == RecyclerViewMode.MODE_DATA) {
return mData.size() + mHeaders.size() + mFooters.size();
} else {
return 1;
}
}
4. onCreateView 덮어쓰기홀더 방법. 뷰별 뷰 홀더 만들기유형
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == RecyclerViewMode.MODE_LOADING) {
RecyclerView.ViewHolder loadingViewHolder = onCreateLoadingViewHolder(parent);
loadingViewHolder.itemView.setLayoutParams(
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, parent.getHeight() - mToolBarHeight)
);
return loadingViewHolder;
}
if (viewType == RecyclerViewMode.MODE_ERROR) {
RecyclerView.ViewHolder errorViewHolder = onCreateErrorViewHolder(parent);
errorViewHolder.itemView.setLayoutParams(
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, parent.getHeight() - mToolBarHeight)
);
errorViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
if (null != mOnErrorViewClickListener) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mOnErrorViewClickListener.onErrorViewClick(v);
}
}, 200);
}
}
});
return errorViewHolder;
}
if (viewType == RecyclerViewMode.MODE_EMPTY) {
RecyclerView.ViewHolder emptyViewHolder = onCreateEmptyViewHolder(parent);
emptyViewHolder.itemView.setLayoutParams(
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, parent.getHeight() - mToolBarHeight)
);
emptyViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
if (null != mOnEmptyViewClickListener) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mOnEmptyViewClickListener.onEmptyViewClick(v);
}
}, 200);
}
}
});
return emptyViewHolder;
}
if (viewType == RecyclerViewMode.MODE_HEADER_VIEW) {
RecyclerView.ViewHolder headerViewHolder = onCreateHeaderViewHolder(parent);
headerViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
if (null != mOnHeaderViewClickListener) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mOnHeaderViewClickListener.onHeaderViewClick(v, v.getTag());
}
}, 200);
}
}
});
return headerViewHolder;
}
if (viewType == RecyclerViewMode.MODE_FOOTER_VIEW) {
RecyclerView.ViewHolder footerViewHolder = onCreateFooterViewHolder(parent);
footerViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
if (null != mOnFooterViewClickListener) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mOnFooterViewClickListener.onFooterViewClick(v, v.getTag());
}
}, 200);
}
}
});
return footerViewHolder;
}
RecyclerView.ViewHolder dataViewHolder = onCreateDataViewHolder(parent);
dataViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
if (null != mOnItemClickListener) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mOnItemClickListener.onItemClick(v, v.getTag());
}
}, 200);
}
}
});
dataViewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(final View v) {
if (null != mOnItemLongClickListener) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mOnItemLongClickListener.onItemLongClick(v, v.getTag());
}
}, 200);
return true;
}
return false;
}
});
return dataViewHolder;
}
5. onBindView 덮어쓰기홀더 메서드. 뷰별 데이터 바인딩유형
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (mMode == RecyclerViewMode.MODE_LOADING) {
onBindLoadingViewHolder(holder, position);
} else if (mMode == RecyclerViewMode.MODE_ERROR) {
onBindErrorViewHolder(holder, position);
} else if (mMode == RecyclerViewMode.MODE_EMPTY) {
onBindEmptyViewHolder(holder, position);
} else {
if (position < mHeaders.size()) {
if (mHeaders.size() > 0) {
onBindHeaderViewHolder(holder, position);
}
} else if (position >= mHeaders.size() + mData.size()) {
if (mFooters.size() > 0) {
onBindFooterViewHolder(holder, position - mHeaders.size() - mData.size());
}
} else {
onBindDataViewHolder(holder, position - mHeaders.size());
}
}
}
라이브러리 SectionedRecyclerViewAdapter를 사용하여 다음 이미지와 같이 항목을 섹션으로 그룹화하고 각 섹션에 헤더를 추가할 수 있습니다.
먼저 섹션 클래스를 만듭니다.
class MySection extends StatelessSection {
String title;
List<String> list;
public MySection(String title, List<String> list) {
// call constructor with layout resources for this Section header, footer and items
super(R.layout.section_header, R.layout.section_item);
this.title = title;
this.list = list;
}
@Override
public int getContentItemsTotal() {
return list.size(); // number of items of this section
}
@Override
public RecyclerView.ViewHolder getItemViewHolder(View view) {
// return a custom instance of ViewHolder for the items of this section
return new MyItemViewHolder(view);
}
@Override
public void onBindItemViewHolder(RecyclerView.ViewHolder holder, int position) {
MyItemViewHolder itemHolder = (MyItemViewHolder) holder;
// bind your view here
itemHolder.tvItem.setText(list.get(position));
}
@Override
public RecyclerView.ViewHolder getHeaderViewHolder(View view) {
return new SimpleHeaderViewHolder(view);
}
@Override
public void onBindHeaderViewHolder(RecyclerView.ViewHolder holder) {
MyHeaderViewHolder headerHolder = (MyHeaderViewHolder) holder;
// bind your header view here
headerHolder.tvItem.setText(title);
}
}
그런 다음 섹션과 함께 RecycleerView를 설정하고 GridLayoutManager를 사용하여 헤더의 SpanSize를 변경합니다.
// Create an instance of SectionedRecyclerViewAdapter
SectionedRecyclerViewAdapter sectionAdapter = new SectionedRecyclerViewAdapter();
// Create your sections with the list of data
MySection section1 = new MySection("My Section 1 title", dataList1);
MySection section2 = new MySection("My Section 2 title", dataList2);
// Add your Sections to the adapter
sectionAdapter.addSection(section1);
sectionAdapter.addSection(section2);
// Set up a GridLayoutManager to change the SpanSize of the header
GridLayoutManager glm = new GridLayoutManager(getContext(), 2);
glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
switch(sectionAdapter.getSectionItemViewType(position)) {
case SectionedRecyclerViewAdapter.VIEW_TYPE_HEADER:
return 2;
default:
return 1;
}
}
});
// Set up your RecyclerView with the SectionedRecyclerViewAdapter
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(glm);
recyclerView.setAdapter(sectionAdapter);
이 모든 HeaderRecyclerViewAdapter 구현에 대한 대안을 추가합니다.복합 어댑터:
https://github.com/negusoft/CompoundAdapter-android
어댑터에서 Adapter Group을 생성할 수 있으므로 보다 유연한 접근 방식입니다.헤더 예에서는, 어댑터를 그대로 사용하고, 헤더에 1개의 항목을 포함한 어댑터를 사용합니다.
AdapterGroup adapterGroup = new AdapterGroup();
adapterGroup.addAdapter(SingleAdapter.create(R.layout.header));
adapterGroup.addAdapter(new MyAdapter(...));
recyclerView.setAdapter(adapterGroup);
그것은 꽤 간단하고 읽기 쉽다.같은 원리로 더 복잡한 어댑터를 쉽게 구현할 수 있습니다.
@reaz-mured @reaz-mured그러나 데이터 사이즈가 +1로 추가되어 종료에 도달하면 Footer View가 반환되는 부분은 마음에 들지 않습니다.
마지막 요소는 모두 바닥글 보기이며 바닥글 보기를 제거하는 데 어려움이 있었습니다.
대신 내 사건을 위해 이런 짓을 했어
private List<RealResponse> addEmptyLoaderResponse(List<RealResponse> originalList){
if(originalList == null){
originalList= new ArrayList<>();
}
originalList.add(new EmptyRealResponse());
return originalList;
}
private class EmptyRealResponse extends RealResponse{
/**Just an Empty class as placeholder for loader at Footer View
*
*/
}
public void setItems(List<InconcurPostResponse> items) {
this.items = addEmptyLoaderResponse(items);
}
@Override
public int getItemCount() {
return items.size();
}
@Override
public int getItemViewType(int position){
if(this.items.get(position) instanceof EmptyRealResponse){
return ViewTypes.FOOTER_VIEW_TYPE.getViewType();
}
return super.getItemViewType(position);
}
이것은 저에게 훨씬 더 깔끔하고 실제 객체를 리사이클러 뷰에 로드합니다.또한 Footer View가 필요하지 않거나 Placeholder Footer View를 더 추가하고 싶을 때 Footer View를 제거할 수 있습니다.
늦은 것은 알지만, 최근에서야 어댑터에 이러한 「addheader」를 실장할 수 있었습니다.FlexibleAdapter 프로젝트에서는setHeader
[섹션 가능] 항목에서 호출합니다.showAllHeaders
헤더가 1개만 필요한 경우 첫 번째 항목에 헤더가 있어야 합니다.이 항목을 삭제하면 헤더는 자동으로 다음 항목으로 연결됩니다.
안타깝게도 보따리는 아직 커버되지 않았습니다.
FlexibleAdapter를 사용하면 머리글/섹션을 작성하는 것 이상의 작업을 수행할 수 있습니다.https://github.com/davideas/FlexibleAdapter 를 꼭 참조해 주세요.
언급URL : https://stackoverflow.com/questions/26448717/android-5-0-add-header-footer-to-a-recyclerview
'sourcecode' 카테고리의 다른 글
MySQL 테이블을 변경하여 열에 주석을 추가합니다. (0) | 2022.11.16 |
---|---|
Flask 뷰에서 JSON 응답 반환 (0) | 2022.11.16 |
Python Anaconda - 안전하게 제거하는 방법 (0) | 2022.11.07 |
쿼리별 MySQL 덤프 (0) | 2022.11.07 |
현재 JBoss 또는 Glassfish(또는 다른)를 새 프로젝트의 Java EE 서버로 사용하시겠습니까? (0) | 2022.11.07 |