diff options
Diffstat (limited to 'library/recyclerview/src/com/android/setupwizardlib/view/HeaderRecyclerView.java')
-rw-r--r-- | library/recyclerview/src/com/android/setupwizardlib/view/HeaderRecyclerView.java | 408 |
1 files changed, 203 insertions, 205 deletions
diff --git a/library/recyclerview/src/com/android/setupwizardlib/view/HeaderRecyclerView.java b/library/recyclerview/src/com/android/setupwizardlib/view/HeaderRecyclerView.java index 0304b65..3808e11 100644 --- a/library/recyclerview/src/com/android/setupwizardlib/view/HeaderRecyclerView.java +++ b/library/recyclerview/src/com/android/setupwizardlib/view/HeaderRecyclerView.java @@ -19,259 +19,257 @@ package com.android.setupwizardlib.view; import android.content.Context; import android.content.res.TypedArray; import android.os.Build; +import androidx.recyclerview.widget.RecyclerView; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.widget.FrameLayout; - -import androidx.recyclerview.widget.RecyclerView; - import com.android.setupwizardlib.DividerItemDecoration; import com.android.setupwizardlib.R; /** * A RecyclerView that can display a header item at the start of the list. The header can be set by - * {@code app:suwHeader} in XML. Note that the header will not be inflated until a layout manager - * is set. + * {@code app:suwHeader} in XML. Note that the header will not be inflated until a layout manager is + * set. */ public class HeaderRecyclerView extends RecyclerView { - private static class HeaderViewHolder extends ViewHolder - implements DividerItemDecoration.DividedViewHolder { - - HeaderViewHolder(View itemView) { - super(itemView); - } - - @Override - public boolean isDividerAllowedAbove() { - return false; - } + private static class HeaderViewHolder extends ViewHolder + implements DividerItemDecoration.DividedViewHolder { - @Override - public boolean isDividerAllowedBelow() { - return false; - } + HeaderViewHolder(View itemView) { + super(itemView); } - /** - * An adapter that can optionally add one header item to the RecyclerView. - * - * @param <CVH> Type of the content view holder. i.e. view holder type of the wrapped adapter. - */ - public static class HeaderAdapter<CVH extends ViewHolder> - extends RecyclerView.Adapter<ViewHolder> { - - private static final int HEADER_VIEW_TYPE = Integer.MAX_VALUE; + @Override + public boolean isDividerAllowedAbove() { + return false; + } - private RecyclerView.Adapter<CVH> mAdapter; - private View mHeader; + @Override + public boolean isDividerAllowedBelow() { + return false; + } + } - private final AdapterDataObserver mObserver = new AdapterDataObserver() { + /** + * An adapter that can optionally add one header item to the RecyclerView. + * + * @param <CVH> Type of the content view holder. i.e. view holder type of the wrapped adapter. + */ + public static class HeaderAdapter<CVH extends ViewHolder> + extends RecyclerView.Adapter<ViewHolder> { - @Override - public void onChanged() { - notifyDataSetChanged(); - } + private static final int HEADER_VIEW_TYPE = Integer.MAX_VALUE; - @Override - public void onItemRangeChanged(int positionStart, int itemCount) { - if (mHeader != null) { - positionStart++; - } - notifyItemRangeChanged(positionStart, itemCount); - } + private final RecyclerView.Adapter<CVH> adapter; + private View header; - @Override - public void onItemRangeInserted(int positionStart, int itemCount) { - if (mHeader != null) { - positionStart++; - } - notifyItemRangeInserted(positionStart, itemCount); - } + private final AdapterDataObserver observer = + new AdapterDataObserver() { - @Override - public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { - if (mHeader != null) { - fromPosition++; - toPosition++; - } - // Why is there no notifyItemRangeMoved? - for (int i = 0; i < itemCount; i++) { - notifyItemMoved(fromPosition + i, toPosition + i); - } - } + @Override + public void onChanged() { + notifyDataSetChanged(); + } - @Override - public void onItemRangeRemoved(int positionStart, int itemCount) { - if (mHeader != null) { - positionStart++; - } - notifyItemRangeRemoved(positionStart, itemCount); + @Override + public void onItemRangeChanged(int positionStart, int itemCount) { + if (header != null) { + positionStart++; } - }; - - public HeaderAdapter(RecyclerView.Adapter<CVH> adapter) { - mAdapter = adapter; - mAdapter.registerAdapterDataObserver(mObserver); - setHasStableIds(mAdapter.hasStableIds()); - } + notifyItemRangeChanged(positionStart, itemCount); + } - @Override - public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - // Returning the same view (mHeader) results in crash ".. but view is not a real child." - // The framework creates more than one instance of header because of "disappear" - // animations applied on the header and this necessitates creation of another header - // view to use after the animation. We work around this restriction by returning an - // empty FrameLayout to which the header is attached using #onBindViewHolder method. - if (viewType == HEADER_VIEW_TYPE) { - FrameLayout frameLayout = new FrameLayout(parent.getContext()); - FrameLayout.LayoutParams params = new FrameLayout.LayoutParams( - FrameLayout.LayoutParams.MATCH_PARENT, - FrameLayout.LayoutParams.WRAP_CONTENT); - frameLayout.setLayoutParams(params); - return new HeaderViewHolder(frameLayout); - } else { - return mAdapter.onCreateViewHolder(parent, viewType); + @Override + public void onItemRangeInserted(int positionStart, int itemCount) { + if (header != null) { + positionStart++; } - } - - @Override - @SuppressWarnings("unchecked") // Non-header position always return type CVH - public void onBindViewHolder(ViewHolder holder, int position) { - if (mHeader != null) { - position--; + notifyItemRangeInserted(positionStart, itemCount); + } + + @Override + public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { + if (header != null) { + fromPosition++; + toPosition++; } - - if (holder instanceof HeaderViewHolder) { - if (mHeader == null) { - throw new IllegalStateException("HeaderViewHolder cannot find mHeader"); - } - if (mHeader.getParent() != null) { - ((ViewGroup) mHeader.getParent()).removeView(mHeader); - } - FrameLayout mHeaderParent = (FrameLayout) holder.itemView; - mHeaderParent.addView(mHeader); - } else { - mAdapter.onBindViewHolder((CVH) holder, position); + // Why is there no notifyItemRangeMoved? + for (int i = 0; i < itemCount; i++) { + notifyItemMoved(fromPosition + i, toPosition + i); } - } + } - @Override - public int getItemViewType(int position) { - if (mHeader != null) { - position--; + @Override + public void onItemRangeRemoved(int positionStart, int itemCount) { + if (header != null) { + positionStart++; } - if (position < 0) { - return HEADER_VIEW_TYPE; - } - return mAdapter.getItemViewType(position); - } - - @Override - public int getItemCount() { - int count = mAdapter.getItemCount(); - if (mHeader != null) { - count++; - } - return count; - } - - @Override - public long getItemId(int position) { - if (mHeader != null) { - position--; - } - if (position < 0) { - return Long.MAX_VALUE; - } - return mAdapter.getItemId(position); - } - - public void setHeader(View header) { - mHeader = header; - } - - public RecyclerView.Adapter<CVH> getWrappedAdapter() { - return mAdapter; - } - } - - private View mHeader; - private int mHeaderRes; + notifyItemRangeRemoved(positionStart, itemCount); + } + }; - public HeaderRecyclerView(Context context) { - super(context); - init(null, 0); + public HeaderAdapter(RecyclerView.Adapter<CVH> adapter) { + this.adapter = adapter; + this.adapter.registerAdapterDataObserver(observer); + setHasStableIds(this.adapter.hasStableIds()); } - public HeaderRecyclerView(Context context, AttributeSet attrs) { - super(context, attrs); - init(attrs, 0); + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + // Returning the same view (header) results in crash ".. but view is not a real child." + // The framework creates more than one instance of header because of "disappear" + // animations applied on the header and this necessitates creation of another header + // view to use after the animation. We work around this restriction by returning an + // empty FrameLayout to which the header is attached using #onBindViewHolder method. + if (viewType == HEADER_VIEW_TYPE) { + FrameLayout frameLayout = new FrameLayout(parent.getContext()); + FrameLayout.LayoutParams params = + new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT); + frameLayout.setLayoutParams(params); + return new HeaderViewHolder(frameLayout); + } else { + return adapter.onCreateViewHolder(parent, viewType); + } } - public HeaderRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(attrs, defStyleAttr); + @Override + @SuppressWarnings("unchecked") // Non-header position always return type CVH + public void onBindViewHolder(ViewHolder holder, int position) { + if (header != null) { + position--; + } + + if (holder instanceof HeaderViewHolder) { + if (header == null) { + throw new IllegalStateException("HeaderViewHolder cannot find mHeader"); + } + if (header.getParent() != null) { + ((ViewGroup) header.getParent()).removeView(header); + } + FrameLayout mHeaderParent = (FrameLayout) holder.itemView; + mHeaderParent.addView(header); + } else { + adapter.onBindViewHolder((CVH) holder, position); + } } - private void init(AttributeSet attrs, int defStyleAttr) { - final TypedArray a = getContext().obtainStyledAttributes(attrs, - R.styleable.SuwHeaderRecyclerView, defStyleAttr, 0); - mHeaderRes = a.getResourceId(R.styleable.SuwHeaderRecyclerView_suwHeader, 0); - a.recycle(); + @Override + public int getItemViewType(int position) { + if (header != null) { + position--; + } + if (position < 0) { + return HEADER_VIEW_TYPE; + } + return adapter.getItemViewType(position); } @Override - public void onInitializeAccessibilityEvent(AccessibilityEvent event) { - super.onInitializeAccessibilityEvent(event); - - // Decoration-only headers should not count as an item for accessibility, adjust the - // accessibility event to account for that. - final int numberOfHeaders = mHeader != null ? 1 : 0; - event.setItemCount(event.getItemCount() - numberOfHeaders); - event.setFromIndex(Math.max(event.getFromIndex() - numberOfHeaders, 0)); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - event.setToIndex(Math.max(event.getToIndex() - numberOfHeaders, 0)); - } + public int getItemCount() { + int count = adapter.getItemCount(); + if (header != null) { + count++; + } + return count; } - /** - * Gets the header view of this RecyclerView, or {@code null} if there are no headers. - */ - public View getHeader() { - return mHeader; + @Override + public long getItemId(int position) { + if (header != null) { + position--; + } + if (position < 0) { + return Long.MAX_VALUE; + } + return adapter.getItemId(position); } - /** - * Set the view to use as the header of this recycler view. - * Note: This must be called before setAdapter. - */ public void setHeader(View header) { - mHeader = header; + this.header = header; } - @Override - public void setLayoutManager(LayoutManager layout) { - super.setLayoutManager(layout); - if (layout != null && mHeader == null && mHeaderRes != 0) { - // Inflating a child view requires the layout manager to be set. Check here to see if - // any header item is specified in XML and inflate them. - final LayoutInflater inflater = LayoutInflater.from(getContext()); - mHeader = inflater.inflate(mHeaderRes, this, false); - } + public RecyclerView.Adapter<CVH> getWrappedAdapter() { + return adapter; } - - @Override - @SuppressWarnings("rawtypes,unchecked") // RecyclerView.setAdapter uses raw type :( - public void setAdapter(Adapter adapter) { - if (mHeader != null && adapter != null) { - final HeaderAdapter headerAdapter = new HeaderAdapter(adapter); - headerAdapter.setHeader(mHeader); - adapter = headerAdapter; - } - super.setAdapter(adapter); + } + + private View header; + private int headerRes; + + public HeaderRecyclerView(Context context) { + super(context); + init(null, 0); + } + + public HeaderRecyclerView(Context context, AttributeSet attrs) { + super(context, attrs); + init(attrs, 0); + } + + public HeaderRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(attrs, defStyleAttr); + } + + private void init(AttributeSet attrs, int defStyleAttr) { + final TypedArray a = + getContext() + .obtainStyledAttributes(attrs, R.styleable.SuwHeaderRecyclerView, defStyleAttr, 0); + headerRes = a.getResourceId(R.styleable.SuwHeaderRecyclerView_suwHeader, 0); + a.recycle(); + } + + @Override + public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + super.onInitializeAccessibilityEvent(event); + + // Decoration-only headers should not count as an item for accessibility, adjust the + // accessibility event to account for that. + final int numberOfHeaders = header != null ? 1 : 0; + event.setItemCount(event.getItemCount() - numberOfHeaders); + event.setFromIndex(Math.max(event.getFromIndex() - numberOfHeaders, 0)); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + event.setToIndex(Math.max(event.getToIndex() - numberOfHeaders, 0)); + } + } + + /** Gets the header view of this RecyclerView, or {@code null} if there are no headers. */ + public View getHeader() { + return header; + } + + /** + * Set the view to use as the header of this recycler view. Note: This must be called before + * setAdapter. + */ + public void setHeader(View header) { + this.header = header; + } + + @Override + public void setLayoutManager(LayoutManager layout) { + super.setLayoutManager(layout); + if (layout != null && header == null && headerRes != 0) { + // Inflating a child view requires the layout manager to be set. Check here to see if + // any header item is specified in XML and inflate them. + final LayoutInflater inflater = LayoutInflater.from(getContext()); + header = inflater.inflate(headerRes, this, false); + } + } + + @Override + @SuppressWarnings("rawtypes,unchecked") // RecyclerView.setAdapter uses raw type :( + public void setAdapter(Adapter adapter) { + if (header != null && adapter != null) { + final HeaderAdapter headerAdapter = new HeaderAdapter(adapter); + headerAdapter.setHeader(header); + adapter = headerAdapter; } + super.setAdapter(adapter); + } } |