/* * Copyright (C) 2006 The Android Open Source Project * * 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.mredrock.date.widget; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Filter; import android.widget.Filterable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; /** * A concrete BaseAdapter that is backed by an array of arbitrary * objects. By default this class expects that the provided resource id references * a single TextView. If you want to use a more complex layout, use the constructors that * also takes a field id. That field id should reference a TextView in the larger layout * resource. * * <p>However the TextView is referenced, it will be filled with the toString() of each object in * the array. You can add lists or arrays of custom objects. Override the toString() method * of your objects to determine what text will be displayed for the item in the list. * * <p>To use something other than TextViews for the array display, for instance, ImageViews, * or to have some of data besides toString() results fill the views, */ abstract public class RecyclerArrayAdapter<T> extends RecyclerView.Adapter<BaseViewHolder> implements Filterable { /** * Contains the list of objects that represent the data of this ArrayAdapter. * The content of this list is referred to as "the array" in the documentation. */ private List<T> mObjects; private ArrayList<HeaderView> headers = new ArrayList<>(); private ArrayList<FooterView> footers = new ArrayList<>(); private static final int SPID = 325864; public interface HeaderView{ public View onCreateView(ViewGroup parent); public void onBindView(View headerView); } public interface FooterView{ public View onCreatView(ViewGroup parent); public void onBindView(View headerView); } /** * Lock used to modify the content of {@link #mObjects}. Any write operation * performed on the array should be synchronized on this lock. This lock is also * used by the filter (see {@link #getFilter()} to make a synchronized copy of * the original array of data. */ private final Object mLock = new Object(); /** * Indicates whether or not {@link #notifyDataSetChanged()} must be called whenever * {@link #mObjects} is modified. */ private boolean mNotifyOnChange = true; private Context mContext; // A copy of the original mObjects array, initialized from and then used instead as soon as // the mFilter ArrayFilter is used. mObjects will then only contain the filtered values. private ArrayList<T> mOriginalValues; private ArrayFilter mFilter; private LayoutInflater mInflater; private static final int INTEGER_DEVIDER = 382654; /** * Constructor * * @param context The current context. */ public RecyclerArrayAdapter(Context context) { init(context, new ArrayList<T>()); } /** * Constructor * * @param context The current context. * @param objects The objects to represent in the ListView. */ public RecyclerArrayAdapter(Context context, T[] objects) { init(context, Arrays.asList(objects)); } /** * Constructor * * @param context The current context. * @param objects The objects to represent in the ListView. */ public RecyclerArrayAdapter(Context context, List<T> objects) { init(context, objects); } public int getPage() { return page; } public void addPage() { this.page +=1; } public void setPage(int page){ this.page = page; } public void resetPage(){ this.page = 0; } private int page; /** * Adds the specified object at the end of the array. * * @param object The object to add at the end of the array. */ public void add(T object) { synchronized (mLock) { if (mOriginalValues != null) { mOriginalValues.add(object); } else { mObjects.add(object); } } if (mNotifyOnChange) notifyDataSetChanged(); } public void addHeader(HeaderView view){ headers.add(view); notifyDataSetChanged(); } public void addFooter(FooterView view){ footers.add(view); notifyDataSetChanged(); } /** * Adds the specified Collection at the end of the array. * * @param collection The Collection to add at the end of the array. */ public void addAll(Collection<? extends T> collection) { if (collection==null||collection.size()==0){ return; } addPage(); synchronized (mLock) { if (mOriginalValues != null) { mOriginalValues.addAll(collection); } else { mObjects.addAll(collection); } } if (mNotifyOnChange) notifyDataSetChanged(); } /** * Adds the specified items at the end of the array. * * @param items The items to add at the end of the array. */ public void addAll(T ... items) { if (items==null||items.length==0){ return; } addPage(); synchronized (mLock) { if (mOriginalValues != null) { Collections.addAll(mOriginalValues, items); } else { Collections.addAll(mObjects, items); } } if (mNotifyOnChange) notifyDataSetChanged(); } /** * Inserts the specified object at the specified index in the array. * * @param object The object to insert into the array. * @param index The index at which the object must be inserted. */ public void insert(T object, int index) { synchronized (mLock) { if (mOriginalValues != null) { mOriginalValues.add(index, object); } else { mObjects.add(index, object); } } if (mNotifyOnChange) notifyDataSetChanged(); } /** * Removes the specified object from the array. * * @param object The object to remove. */ public void remove(T object) { synchronized (mLock) { if (mOriginalValues != null) { mOriginalValues.remove(object); } else { mObjects.remove(object); } } if (mNotifyOnChange) notifyDataSetChanged(); } /** * Remove all elements from the list. */ public void clear() { resetPage(); synchronized (mLock) { if (mOriginalValues != null) { mOriginalValues.clear(); } else { mObjects.clear(); } } if (mNotifyOnChange) notifyDataSetChanged(); } /** * Sorts the content of this adapter using the specified comparator. * * @param comparator The comparator used to sort the objects contained * in this adapter. */ public void sort(Comparator<? super T> comparator) { synchronized (mLock) { if (mOriginalValues != null) { Collections.sort(mOriginalValues, comparator); } else { Collections.sort(mObjects, comparator); } } if (mNotifyOnChange) notifyDataSetChanged(); } /** * Control whether methods that change the list ({@link #add}, * {@link #insert}, {@link #remove}, {@link #clear}) automatically call * {@link #notifyDataSetChanged}. If set to false, caller must * manually call notifyDataSetChanged() to have the changes * reflected in the attached view. * * The default is true, and calling notifyDataSetChanged() * resets the flag to true. * * @param notifyOnChange if true, modifications to the list will * automatically call {@link * #notifyDataSetChanged} */ public void setNotifyOnChange(boolean notifyOnChange) { mNotifyOnChange = notifyOnChange; } private void init(Context context , List<T> objects) { mContext = context; mObjects = objects; } /** * Returns the context associated with this array adapter. The context is used * to create views from the resource passed to the constructor. * * @return The Context associated with this adapter. */ public Context getContext() { return mContext; } @Override public int getItemCount() { return mObjects.size()+headers.size()+footers.size(); } public View creatSpViewByType(ViewGroup parent, int viewType){ for (HeaderView headerView:headers){ View view = headerView.onCreateView(parent); if (headerView.hashCode() == viewType){ return view; } } for (FooterView footerview:footers){ View view = footerview.onCreatView(parent); if (view.hashCode() == viewType){ return view; } } return null; } @Override public final BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = creatSpViewByType(parent,viewType); if (view!=null){ return new StateViewHolder(view); } return OnCreateViewHolder(parent, viewType); } abstract public BaseViewHolder OnCreateViewHolder(ViewGroup parent, int viewType); @Override public final void onBindViewHolder(BaseViewHolder holder, int position) { holder.itemView.setId(position); if (headers.size()!=0 && position<headers.size()){ headers.get(position).onBindView(holder.itemView); return ; } int i = position - headers.size() - mObjects.size(); if (footers.size()!=0 && i>=0){ footers.get(position).onBindView(holder.itemView); return ; } OnBindViewHolder(holder,position-headers.size()); } abstract public void OnBindViewHolder(BaseViewHolder holder, int position); @Override public final int getItemViewType(int position) { if (headers.size()!=0){ if (position<headers.size())return headers.get(position).hashCode(); } if (footers.size()!=0){ /* eg: 0:header1 1:header2 2 2:object1 3:object2 4:object3 5:object4 6:footer1 6(position) - 2 - 4 = 0 7:footer2 */ int i = position - headers.size() - mObjects.size(); if (i >= 0){ return footers.get(i).hashCode(); } } return getViewType(position); } public int getViewType(int position){ return 0; } /** * {@inheritDoc} */ public T getItem(int position) { if (position < mObjects.size()) { return mObjects.get(position); } else { return null; } } /** * Returns the position of the specified item in the array. * * @param item The item to retrieve the position of. * * @return The position of the specified item. */ public int getPosition(T item) { return mObjects.indexOf(item); } /** * {@inheritDoc} */ public long getItemId(int position) { return position; } /** * {@inheritDoc} */ public Filter getFilter() { if (mFilter == null) { mFilter = new ArrayFilter(); } return mFilter; } public LayoutInflater getInflater() { return mInflater; } private class StateViewHolder extends BaseViewHolder{ public StateViewHolder(View itemView) { super(itemView); } } /** * <p>An array filter constrains the content of the array adapter with * a prefix. Each item that does not start with the supplied prefix * is removed from the list.</p> */ private class ArrayFilter extends Filter { @Override protected FilterResults performFiltering(CharSequence prefix) { FilterResults results = new FilterResults(); if (mOriginalValues == null) { synchronized (mLock) { mOriginalValues = new ArrayList<T>(mObjects); } } if (prefix == null || prefix.length() == 0) { ArrayList<T> list; synchronized (mLock) { list = new ArrayList<T>(mOriginalValues); } results.values = list; results.count = list.size(); } else { String prefixString = prefix.toString().toLowerCase(); ArrayList<T> values; synchronized (mLock) { values = new ArrayList<T>(mOriginalValues); } final int count = values.size(); final ArrayList<T> newValues = new ArrayList<T>(); for (int i = 0; i < count; i++) { final T value = values.get(i); final String valueText = value.toString().toLowerCase(); // First match against the whole, non-splitted value if (valueText.startsWith(prefixString)) { newValues.add(value); } else { final String[] words = valueText.split(" "); final int wordCount = words.length; // Start at index 0, in case valueText starts with space(s) for (int k = 0; k < wordCount; k++) { if (words[k].startsWith(prefixString)) { newValues.add(value); break; } } } } results.values = newValues; results.count = newValues.size(); } return results; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { //noinspection unchecked mObjects = (List<T>) results.values; if (results.count > 0) { notifyDataSetChanged(); } else { notifyDataSetChanged(); mNotifyOnChange = true; } } } }