/*
* Copyright (C) 2016 Haruki Hasegawa
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.h6ah4i.android.widget.advrecyclerview.headerfooter;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
import com.h6ah4i.android.widget.advrecyclerview.adapter.AdapterPathSegment;
import com.h6ah4i.android.widget.advrecyclerview.adapter.ItemIdComposer;
import com.h6ah4i.android.widget.advrecyclerview.adapter.ItemViewTypeComposer;
import com.h6ah4i.android.widget.advrecyclerview.composedadapter.ComposedAdapter;
import com.h6ah4i.android.widget.advrecyclerview.composedadapter.ComposedChildAdapterTag;
import java.util.List;
/**
* A simplified version of ComposedAdapter for creating headers and footers.
*/
public abstract class AbstractHeaderFooterWrapperAdapter<HeaderVH extends RecyclerView.ViewHolder, FooterVH extends RecyclerView.ViewHolder> extends ComposedAdapter {
/**
* Segment type: header items
*/
public static final int SEGMENT_TYPE_HEADER = 0;
/**
* Segment type: normal items
*/
public static final int SEGMENT_TYPE_NORMAL = 1;
/**
* Segment type: footer items
*/
public static final int SEGMENT_TYPE_FOOTER = 2;
private RecyclerView.Adapter mHeaderAdapter;
private RecyclerView.Adapter mWrappedAdapter;
private RecyclerView.Adapter mFooterAdapter;
private ComposedChildAdapterTag mHeaderAdapterTag;
private ComposedChildAdapterTag mWrappedAdapterTag;
private ComposedChildAdapterTag mFooterAdapterTag;
/**
* Constructor
*
*/
public AbstractHeaderFooterWrapperAdapter() {
}
/**
*
* @param adapter Wrapped contents adapter.
*/
@SuppressWarnings("unchecked")
public AbstractHeaderFooterWrapperAdapter setAdapter(@NonNull RecyclerView.Adapter<? extends RecyclerView.ViewHolder> adapter) {
if (mWrappedAdapter != null) {
throw new IllegalStateException("setAdapter() can call only once");
}
mWrappedAdapter = adapter;
mHeaderAdapter = onCreateHeaderAdapter();
mFooterAdapter = onCreateFooterAdapter();
final boolean hasStableIds = adapter.hasStableIds();
mHeaderAdapter.setHasStableIds(hasStableIds);
mFooterAdapter.setHasStableIds(hasStableIds);
setHasStableIds(hasStableIds);
mHeaderAdapterTag = addAdapter(mHeaderAdapter);
mWrappedAdapterTag = addAdapter(mWrappedAdapter);
mFooterAdapterTag = addAdapter(mFooterAdapter);
return this;
}
@Override
protected void onRelease() {
super.onRelease();
mHeaderAdapterTag = null;
mWrappedAdapterTag = null;
mFooterAdapterTag = null;
mHeaderAdapter = null;
mWrappedAdapter = null;
mFooterAdapter = null;
}
/**
* Returns a newly created adapter for header items.
*
* @return Adapter for header items
*/
@NonNull
protected RecyclerView.Adapter onCreateHeaderAdapter() {
return new BaseHeaderAdapter(this);
}
/**
* Returns a newly created adapter for the footer items.
*
* @return Adapter for the footer items
*/
@NonNull
protected RecyclerView.Adapter onCreateFooterAdapter() {
return new BaseFooterAdapter(this);
}
/**
* Returns the adapter for the header items.
*
* @return Adapter for the header items
*/
public RecyclerView.Adapter getHeaderAdapter() {
return mHeaderAdapter;
}
/**
* Returns the adapter for the footer items.
*
* @return Adapter for the footer items
*/
public RecyclerView.Adapter getFooterAdapter() {
return mFooterAdapter;
}
/**
* Returns underlying adapter.
* @return The underlying adapter instance
*/
public RecyclerView.Adapter getWrappedAdapter() {
return mWrappedAdapter;
}
/**
* Returns the path segment for the underlying adapter.
*
* @return AdapterPathSegment for the wrapped adapter
*/
public AdapterPathSegment getWrappedAdapterSegment() {
return new AdapterPathSegment(mWrappedAdapter, mWrappedAdapterTag);
}
/**
* Returns the path segment for the header adapter.
*
* @return AdapterPathSegment for the header adapter
*/
public AdapterPathSegment getHeaderSegment() {
return new AdapterPathSegment(mHeaderAdapter, mHeaderAdapterTag);
}
/**
* Returns the path segment for the footer adapter.
*
* @return AdapterPathSegment for the footer adapter
*/
public AdapterPathSegment getFooterSegment() {
return new AdapterPathSegment(mFooterAdapter, mFooterAdapterTag);
}
/**
* Called when RecyclerView needs a new {@link RecyclerView.ViewHolder} of the
* given type to represent a header item.
*
* @param parent The ViewGroup into which the new View will be added after it is bound to
* a header adapter position.
* @param viewType The view type of the new header View.
* @return A new ViewHolder for the header that holds a View of the given view type.
* @see {@link RecyclerView.Adapter#onCreateViewHolder(ViewGroup, int)}
* @see {@link #getHeaderItemViewType(int)}
* @see {@link #onBindHeaderItemViewHolder(RecyclerView.ViewHolder, int)}
*/
public abstract HeaderVH onCreateHeaderItemViewHolder(ViewGroup parent, int viewType);
/**
* Called when RecyclerView needs a new {@link RecyclerView.ViewHolder} of the
* given type to represent a footer item.
*
* @param parent The ViewGroup into which the new View will be added after it is bound to
* a footer adapter position.
* @param viewType The view type of the new footer View.
* @return A new ViewHolder for the footer that holds a View of the given view type.
* @see {@link RecyclerView.Adapter#onCreateViewHolder(ViewGroup, int)}
* @see {@link #getFooterItemViewType(int)}
* @see {@link #onBindFooterItemViewHolder(RecyclerView.ViewHolder, int)}
*/
public abstract FooterVH onCreateFooterItemViewHolder(ViewGroup parent, int viewType);
/**
* Called by RecyclerView to display the data at the specified position. This method should
* update the contents of the {@link RecyclerView.ViewHolder#itemView} to reflect the header item
* at the given position.
*
* @param holder The ViewHolder which should be updated to represent the contents of the
* item at the given position in the data set.
* @param localPosition The position of the item within the header adapter's data set.
* @see {@link RecyclerView.Adapter#onBindViewHolder(RecyclerView.ViewHolder, int)}
*/
public abstract void onBindHeaderItemViewHolder(HeaderVH holder, int localPosition);
/**
* Called by RecyclerView to display the data at the specified position. This method should
* update the contents of the {@link RecyclerView.ViewHolder#itemView} to reflect the footer item
* at the given position.
*
* @param holder The ViewHolder which should be updated to represent the contents of the
* item at the given position in the data set.
* @param localPosition The position of the item within the footer adapter's data set.
* @see {@link RecyclerView.Adapter#onBindViewHolder(RecyclerView.ViewHolder, int)}
*/
public abstract void onBindFooterItemViewHolder(FooterVH holder, int localPosition);
/**
* Called by RecyclerView to display the data at the specified position. This method should
* update the contents of the {@link RecyclerView.ViewHolder#itemView} to reflect the header item
* at the given position.
*
* @param holder The ViewHolder which should be updated to represent the contents of the
* item at the given position in the data set.
* @param localPosition The position of the item within the header adapter's data set.
* @param payloads A non-null list of merged payloads. Can be empty list if requires full
* update.
* @see {@link RecyclerView.Adapter#onBindViewHolder(RecyclerView.ViewHolder, int, List)}
*/
public void onBindHeaderItemViewHolder(HeaderVH holder, int localPosition, List<Object> payloads) {
onBindHeaderItemViewHolder(holder, localPosition);
}
/**
* Called by RecyclerView to display the data at the specified position. This method should
* update the contents of the {@link RecyclerView.ViewHolder#itemView} to reflect the footer item
* at the given position.
*
* @param holder The ViewHolder which should be updated to represent the contents of the
* item at the given position in the data set.
* @param localPosition The position of the item within the footer adapter's data set.
* @param payloads A non-null list of merged payloads. Can be empty list if requires full
* update.
* @see {@link RecyclerView.Adapter#onBindViewHolder(RecyclerView.ViewHolder, int, List)}
*/
public void onBindFooterItemViewHolder(FooterVH holder, int localPosition, List<Object> payloads) {
onBindFooterItemViewHolder(holder, localPosition);
}
/**
* Returns the total number of items in the data set hold by the header adapter.
*
* @return The total number of items in the header adapter.
* @see {@link RecyclerView.Adapter#getItemCount()}
*/
public abstract int getHeaderItemCount();
/**
* Returns the total number of items in the data set hold by the footer adapter.
*
* @return The total number of items in the footer adapter.
* @see {@link RecyclerView.Adapter#getItemCount()}
*/
public abstract int getFooterItemCount();
/**
* Return the stable ID for the item at <code>localPosition</code>. If {@link #hasStableIds()}
* would return false this method should return {@link RecyclerView#NO_ID}.
*
* @param localPosition Header adapter position to query
* @return the stable ID of the item at position
* @see {@link RecyclerView.Adapter#getItemId(int)}
*/
@IntRange(from = ItemIdComposer.MIN_WRAPPED_ID, to = ItemIdComposer.MAX_WRAPPED_ID)
public long getHeaderItemId(int localPosition) {
if (hasStableIds()) {
return RecyclerView.NO_ID;
}
return localPosition; // This works for simple header items without structural changes
}
/**
* Return the stable ID for the item at <code>localPosition</code>. If {@link #hasStableIds()}
* would return false this method should return {@link RecyclerView#NO_ID}.
*
* @param localPosition Foote adapter position to query
* @return the stable ID of the item at position
* @see {@link RecyclerView.Adapter#getItemId(int)}
*/
@IntRange(from = ItemIdComposer.MIN_WRAPPED_ID, to = ItemIdComposer.MAX_WRAPPED_ID)
public long getFooterItemId(int localPosition) {
if (hasStableIds()) {
return RecyclerView.NO_ID;
}
return localPosition; // This works for simple footer items without structural changes
}
/**
* Return the view type of the header item at <code>localPosition</code> for the purposes
* of view recycling.
*
* <p>The default implementation of this method returns 0, making the assumption of
* a single view type for the adapter. Unlike ListView adapters, types need not
* be contiguous.</p>
*
* @param localPosition The header adapter local position to query
* @return integer value identifying the type of the view needed to represent the item at
* <code>localPosition</code>. Type codes need not be contiguous.
*/
@IntRange(from = ItemViewTypeComposer.MIN_WRAPPED_VIEW_TYPE, to = ItemViewTypeComposer.MAX_WRAPPED_VIEW_TYPE)
public int getHeaderItemViewType(int localPosition) {
return 0;
}
/**
* Return the view type of the footer item at <code>localPosition</code> for the purposes
* of view recycling.
*
* <p>The default implementation of this method returns 0, making the assumption of
* a single view type for the adapter. Unlike ListView adapters, types need not
* be contiguous.</p>
*
* @param localPosition The footer adapter local position to query
* @return integer value identifying the type of the view needed to represent the item at
* <code>localPosition</code>. Type codes need not be contiguous.
*/
@IntRange(from = ItemViewTypeComposer.MIN_WRAPPED_VIEW_TYPE, to = ItemViewTypeComposer.MAX_WRAPPED_VIEW_TYPE)
public int getFooterItemViewType(int localPosition) {
return 0;
}
public static class BaseHeaderAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
protected AbstractHeaderFooterWrapperAdapter mHolder;
public BaseHeaderAdapter(AbstractHeaderFooterWrapperAdapter holder) {
mHolder = holder;
}
@Override
public int getItemCount() {
return mHolder.getHeaderItemCount();
}
@Override
public long getItemId(int position) {
return mHolder.getHeaderItemId(position);
}
@Override
public int getItemViewType(int position) {
return mHolder.getHeaderItemViewType(position);
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return mHolder.onCreateHeaderItemViewHolder(parent, viewType);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
throw new IllegalStateException();
}
@Override
@SuppressWarnings("unchecked")
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List<Object> payloads) {
mHolder.onBindHeaderItemViewHolder(holder, position, payloads);
}
}
public static class BaseFooterAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
protected AbstractHeaderFooterWrapperAdapter mHolder;
public BaseFooterAdapter(AbstractHeaderFooterWrapperAdapter holder) {
mHolder = holder;
}
@Override
public int getItemCount() {
return mHolder.getFooterItemCount();
}
@Override
public long getItemId(int position) {
return mHolder.getFooterItemId(position);
}
@Override
public int getItemViewType(int position) {
return mHolder.getFooterItemViewType(position);
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return mHolder.onCreateFooterItemViewHolder(parent, viewType);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
throw new IllegalStateException();
}
@Override
@SuppressWarnings("unchecked")
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List<Object> payloads) {
mHolder.onBindFooterItemViewHolder(holder, position, payloads);
}
}
}