RecyclerView通用适配器

时间:2017-04-10 10:30:35

标签: android android-recyclerview recycler-adapter

尝试为RecyclerView创建通用适配器 已经探索过其他解决方案: RecyclerView generic adapter with DataBinding
Add click listener to Generic RecyclerView Adapter
https://github.com/ravirupareliya/Recyclerview-Generic-Adapter/blob/master/app/src/main/java/com/rrr/genericrecyclerview/adapter/RecyclerAdapter.java

但是想以一种不同的方式实现它 以下是我的适配器的样子:

public abstract class GenericAdapter<T> extends RecyclerView.Adapter<BaseHolder<T>> {

    private OnRecyclerItemClickListener mListener;
    private List<T> mItems;
    private final LayoutInflater mLayoutInflater;

    public GenericAdapter(Context context) {
        mLayoutInflater =
                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        mItems = Collections.emptyList();
    }

    @Override
    public BaseHolder<T> onCreateViewHolder(ViewGroup parent, int viewType) {
        // assume that viewType is a resource id returned by getItemViewType 
        // also other solutions are welcome
        View view = mLayoutInflater.inflate(viewType, parent, false);

        // TODO: how to create the view holder ??
    }

    @Override
    public void onBindViewHolder(BaseHolder<T> holder, int position) {
        T item = mItems.get(position);
        holder.onBind(item, mListener);
    }

    @Override
    public int getItemCount() {
        return mItems != null ? mItems.size() : 0;
    }  

    public void setItems(List<T> items) {
        mItems = items;
        notifyDataSetChanged();
    }

    public void setItemClickListener(OnRecyclerItemClickListener listener) {
        mListener = listener;
    }
}

这是一个基本视图持有者,我希望从中扩展所有其他持有者:

public abstract class BaseHolder<T> extends RecyclerView.ViewHolder {

    public BaseHolder(View itemView) {
        super(itemView);
    }

    public abstract void onBind(T item, OnRecyclerItemClickListener listener);
}

问题在于onCreateViewHolder方法 如何创建视图持有者?
我应该以某种方式实现抽象BaseHolder或者我应该以某种方式更改适配器类声明扩展,即在此处使用BaseHolder RecyclerView.Adapter&gt;应该是BaseHolder的一些通用实现?我试过&gt;但它不起作用

提前感谢任何建议

3 个答案:

答案 0 :(得分:0)

这就是我在“通用适配器”中所做的:

您传递的持有人类别与您当前的视图相对应,如下所示:

public class FooHolder extends BaseHolder<Foo> {
    ... // your code with the constructor matching super goes here
}

然后在适配器构造函数中添加一个参数,如下所示:

public GenericAdapter(Context context, Class<? extends BaseHolder> holderClass) {
        ...
        this.holderClass = holderClass;
}

然后在onCreateViewHolder方法中,您可以像这样创建该类的实例:

@Override
public BaseHolder<T> onCreateViewHolder(ViewGroup parent, int viewType) {
        ...
        viewHolder = (BaseHolder) holderClass.getConstructor(View.class).newInstance(view);
    }

答案 1 :(得分:0)

如果您想使用单个通用适配器,它们具有不同的2个布局项目和不同的模型用于不同的屏幕,您需要尝试这个:

创建一个java类适配器“ StoreListAdapter.java”

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Collection;

/**
 * Created by Deepak Sharma on 31/10/17.
 */
public class StoreListAdapter<T> extends RecyclerView.Adapter<StoreListAdapter.ViewHolder> implements Filterable {

private Collection<T> mItems;
    private Context context;
    private int mLayout;
    IClickable iClickable;
    private boolean isAnimationAllowed;
    private StoreSearchFilter<T> mSearchFilter;

public StoreListAdapter(Context context)
    {
        this.context = context;
    }
    public void setData(Collection<T> items, boolean isAnimationAllowed)
    {
        mItems = items;
        this.isAnimationAllowed = isAnimationAllowed;
    }

    public void setCallback(int layout, IClickable iClickable)
    {
        this.mLayout = layout;
        this.iClickable = iClickable;
    }

@Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(mLayout, viewGroup, false);
        iClickable.init(view);

        StoreListAdapter.ViewHolder viewHolder = new StoreListAdapter.ViewHolder(view);

        view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
        int width = view.getMeasuredWidth();
        int height = view.getMeasuredHeight();
//        viewHolder.itemView.getLayoutParams().width = width;
        viewHolder.itemView.getLayoutParams().height = height+24;
        return viewHolder;
    }

@Override
    public void onBindViewHolder(StoreListAdapter.ViewHolder viewHolder, int i) {
        iClickable.execute(viewHolder, mItems.toArray()[i],viewHolder.getAdapterPosition());
        if (isAnimationAllowed)
            setAnimation(viewHolder.itemView, i);
    }

    @Override
    public int getItemCount() {
        return mItems.size()>0?mItems.size():0;
    }

@Override
    public Filter getFilter() {
        if (mSearchFilter == null)
            mSearchFilter = new StoreSearchFilter<T>((ArrayList<StoreModel>) mItems, new IFilteredList<T>() {
                @Override
                public void onListFiltered(ArrayList<T> list) {

                    setData(list, false);
                    notifyDataSetChanged();
                }
            });
        return mSearchFilter;
    }

public class ViewHolder extends RecyclerView.ViewHolder {

        private final TextView mTextView;
        //private CheckBox mCheckBox;

        ViewHolder(View v) {
            super(v);
            mTextView = (TextView)v.findViewById(R.id.list_item);
        }
    }

public interface IClickable<T> {
        public void init(View view);
        public void execute(StoreListAdapter.ViewHolder holder, T object, int position);
    }

/**
     * Here is the key method to apply the animation
     */
    private void setAnimation(View viewToAnimate, int position)
    {
        // If the bound view wasn't previously displayed on screen, it's animated
        /*if (position > lastPosition)
        {*/
            Animation animation = AnimationUtils.loadAnimation(context, android.R.anim.slide_in_left);
            viewToAnimate.startAnimation(animation);
            //lastPosition = position;
        /*}*/
    }

}

创建商店模型“StoreModel.java”

import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Deepak Sharma on 31/10/17.
 */

public class StoreModel implements Parcelable {

    private String storeName;
    private int storeId;
    private String storeAddress;
    private String storePhone = "(310)816-2365";
    private String storeCity;
    private int prizesAvailable;
    private double storeLatitude;
    private double storeLongitude;
    private List<Games> gamesList;

    public StoreModel(int id,String sName, String add, String city, int prizes, double lat, double lon, ArrayList<Games> list)
    {
        this.storeId    = id;
        this.storeName       = sName;
        this.storeAddress    = add;
        this.storeCity       = city;
        this.prizesAvailable = prizes;
        this.storeLatitude = lat;
        this.storeLongitude = lon;
        this.gamesList = list;
    }

    public String getStoreName() {
        return storeName;
    }

    public void setStoreName(String storeName) {
        this.storeName = storeName;
    }

    public String getStoreAddress() {
        return storeAddress;
    }

    public void setStoreAddress(String storeAddress) {
        this.storeAddress = storeAddress;
    }

    public String getStoreCity() {
        return storeCity;
    }

    public void setStoreCity(String storeCity) {
        this.storeCity = storeCity;
    }

    public int getPrizesAvailable() {
        return prizesAvailable;
    }

    public void setPrizesAvailable(int prizesAvailable) {
        this.prizesAvailable = prizesAvailable;
    }

    public double getStoreLatitude() {
        return storeLatitude;
    }

    public void setStoreLatitude(double storeLatitude) {
        this.storeLatitude = storeLatitude;
    }

    public double getStoreLongitude() {
        return storeLongitude;
    }

    public void setStoreLongitude(double storeLongitude) {
        this.storeLongitude = storeLongitude;
    }

    public List<Games> getGamesList() {
        return gamesList;
    }

    public void setGamesList(List<Games> gamesList) {
        this.gamesList = gamesList;
    }

    public String getStorePhone() {
        return storePhone;
    }

    public void setStorePhone(String storePhone) {
        this.storePhone = storePhone;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this)
            return true;   //If objects equal, is OK
        if (obj == null)
            return false;
        if (!(obj instanceof StoreModel))
            return false;

        if (obj instanceof StoreModel) {
            StoreModel store = (StoreModel)obj;
            return ((storeId == store.storeId)  && (storeName.equalsIgnoreCase(store.storeName)));
//            return (storeId == store.storeId)  && y == store.y);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return storeId;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.storeName);
        dest.writeString(this.storeAddress);
        dest.writeString(this.storePhone);
        dest.writeString(this.storeCity);
        dest.writeInt(this.prizesAvailable);
        dest.writeDouble(this.storeLatitude);
        dest.writeDouble(this.storeLongitude);
        dest.writeList(this.gamesList);
    }

    protected StoreModel(Parcel in) {
        this.storeName = in.readString();
        this.storeAddress = in.readString();
        this.storePhone = in.readString();
        this.storeCity = in.readString();
        this.prizesAvailable = in.readInt();
        this.storeLatitude = in.readDouble();
        this.storeLongitude = in.readDouble();
        this.gamesList = new ArrayList<Games>();
        in.readList(this.gamesList, Games.class.getClassLoader());
    }

    public static final Parcelable.Creator<StoreModel> CREATOR = new Parcelable.Creator<StoreModel>() {
        @Override
        public StoreModel createFromParcel(Parcel source) {
            return new StoreModel(source);
        }

        @Override
        public StoreModel[] newArray(int size) {
            return new StoreModel[size];
        }
    };
}

创建“store_item.xml”

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/coordinator_game_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.v7.widget.CardView
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:id="@+id/store_item_cardview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        card_view:cardBackgroundColor="@android:color/transparent"
        card_view:cardElevation="0dp"
        android:layout_marginTop="@dimen/dp_five">

<TextView
                    android:id="@+id/txtStoreName"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Store Name"
                    android:textSize="18sp" />

 </android.support.v7.widget.CardView>

</android.support.design.widget.CoordinatorLayout>

现在在Activity或Fragment类中,调用此方法来设置Adapter:

private void setBottomViewAdapter() {
        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        mRecyclerStore.setHasFixedSize(true);

        // use a linear layout manager
        LinearLayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
        mRecyclerStore.setLayoutManager(mLayoutManager);

        // specify an adapter (see also next example)
        mStoreList = new ArrayList<>();
        mStoreList.add(new StoreModel(1001, "Von's", "9860 National Blvd., Los Angeles", "Culver City", 12, 28.624035, 77.365541, getGamesList(8)));
        mStoreList.add(new StoreModel(1002, "Jack", "1311 Wilshire Blvd., Santa Monica", "Mid City", 10, 28.622665, 77.364082, getGamesList(6)));
        mStoreList.add(new StoreModel(1003, "Ian", "1430 S Fairfax Ave., Los Angeles", "Culver City", 8, 28.620899, 77.365258, getGamesList(2)));
        mStoreList.add(new StoreModel(1004, "Jessica Alberto", "1372 Wilshire Blvd., Santa Monica", "Mid City", 10, 28.623890, 77.374136, getGamesList(10)));
        mStoreList.add(new StoreModel(1005, "Robert", "2545 National Blvd, Los Angeles", "Culver City", 6, 28.631175, 77.375661, getGamesList(4)));
        mStoreList.add(new StoreModel(1006, "Clark", "1372 Wilshire Blvd., Santa Monica", "Mid City", 10, 28.627153, 77.381809, getGamesList(11)));
        mStoreList.add(new StoreModel(1007, "Jason", "9860 National Blvd., Los Angeles", "Culver City", 12, 28.626569, 77.371963, getGamesList(3)));
        mStoreList.add(new StoreModel(1008, "Papon", "1372 Wilshire Blvd., Santa Monica", "Mid City", 10, 28.623155, 77.371677, getGamesList(2)));
        mStoreList.add(new StoreModel(1009, "Von's", "2545 National Blvd, Los Angeles", "Culver City", 11, 28.611569, 77.38545, getGamesList(13)));
        mStoreList.add(new StoreModel(1010, "Robert Downey Jr.", "1430 S Fairfax Ave., Los Angeles", "Culver City", 8, 28.623127, 77.369113, getGamesList(4)));
        mStoreList.add(new StoreModel(1011, "Ben Affleck", "1335 Wilshire Blvd., Santa Monica", "Mid City", 4, 28.62373, 77.39452, getGamesList(12)));
        mStoreListAdapter = new StoreListAdapter(getActivity());
        boolean isAnimate = false;
        mStoreListAdapter.setData(mStoreList, isAnimate);
        mStoreListAdapter.setCallback(R.layout.store_item, new StoreListAdapter.IClickable() {
            @Override
            public void init(View view) {
                //    Toast.makeText(getActivity(), "Initialized", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void execute(StoreListAdapter.ViewHolder viewHolder, Object object, int position) {
                final StoreModel model = (StoreModel) object;

                View view = viewHolder.itemView;
                StoreListAdapter.ViewHolder holder = viewHolder;

                final CoordinatorLayout fabGameview = (CoordinatorLayout) view;
                final CardView cardView = (CardView) fabGameview.findViewById(R.id.store_item_cardview);

                TextView txtStoreName = (TextView) cardView.findViewById(R.id.txtStoreName);

                cardView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        boolean isAddedToBackStack = true;
                        StoreDetailsAndProductListFragment storeDetailsAndProductListFragment = new StoreDetailsAndProductListFragment();
                        Bundle bundle = new Bundle();
                        bundle.putParcelable(ExtrasUtil.STORE, model);
                        storeDetailsAndProductListFragment.setArguments(bundle);
                        showOtherFragment(storeDetailsAndProductListFragment, getActivity().getFragmentManager(), isAddedToBackStack);
                    }
                });
            }
        });
        mRecyclerStore.setAdapter(mStoreListAdapter);
    }

创建一个界面:

import java.util.ArrayList;

/**
 * Created by Deepak Sharma on 3/8/2017.
 */

public interface IFilteredList<T> {
    public void onListFiltered(ArrayList<T> list);
}

创建自定义可过滤类 StoreSearchFilter.java

import android.widget.Filter;
import java.util.ArrayList;
import java.util.List;

public final class StoreSearchFilter<T> {

    //    private final Pattern pattern;
    private ArrayList<StoreModel> mList;
    private IFilteredList<T> callback;

    public StoreSearchFilter(final String regex)
    {
//        pattern = Pattern.compile(regex);
    }

    public StoreSearchFilter(ArrayList<StoreModel> list, IFilteredList<T> listener)
    {
        this.mList = list;
        this.callback = listener;
    }

    @Override
    protected Filter.FilterResults performFiltering(CharSequence constraint) {
        Filter.FilterResults results = new Filter.FilterResults();
        if (constraint == null || constraint.length() == 0) {
            results.values = mList;
            results.count = mList.size();
        }
        else {
            // Some search copnstraint has been passed
            // so let's filter accordingly
            ArrayList<StoreModel> filteredContacts = new ArrayList<StoreModel>();

            if (mList!=null && mList.size()>0)
            {
            // We'll go through all the contacts and see
            // if they contain the supplied string
            for (StoreModel model : mList) {
                // TODO Here search for the store name match
                if (model.getStoreName().toUpperCase().contains(constraint.toString().toUpperCase())) {
                    // if `contains` == true then add it
                    // to our filtered list
                    filteredContacts.add(model);
                }
                // TODO Here search for the product name match
                else {
                    List<Games> gameList = model.getGamesList();
                    if (gameList!=null && gameList.size()>0)
                    {
                    for (Games game : gameList) {
                        if (game.getProductName().toUpperCase().contains(constraint.toString().toUpperCase())) {
                            filteredContacts.add(model);
                            break;
                        }
                    }
                }
                }
            }
        }
            // Finally set the filtered values and size/count
            results.values = filteredContacts;
            results.count = filteredContacts.size();
        }

        // Return our FilterResults object
        return results;
    }

    @Override
    protected void publishResults(CharSequence constraint, Filter.FilterResults results) {
        callback.onListFiltered((ArrayList<T>) results.values);
    }
}

答案 2 :(得分:0)

P.S。已经好久了,但是如果有人感兴趣,
当时我解决了它,并发布在Generic RecyclerView Adapter library中。
最终,创建适配器非常容易。
我仍在几个生产项目中使用它。

编辑: 因此,适配器最终看起来像:

const fetchPromise = fetch(<your params>);

fetchPromise.then(response => {
  if (response.ok()){
    //Your request was successful
    const jsonPromise = response.json();
    jsonPromise.then(data => {
      console.log("Successful request, parsed json body", data);
    }).catch(error => {
      //error handling for json parsing errors (empty body etc.)
      console.log("Successful request, Could not parse body as json", error);
    })
  } else {
    //Your request was not successful
    /*
     You can check the body of the response here anyways. Maybe your api does return a json error?
    */
  }
}).catch(error => {
  //error handling for fetch errors
}))

因此,将ViewHolder的创建委托给适配器实现,即:

public abstract class GenericRecyclerViewAdapter<T, L extends BaseRecyclerListener, VH extends BaseViewHolder<T, L>> extends RecyclerView.Adapter<VH> {

    @Override
    public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);

    // ...
}

ViewHolder是:

public class SimpleAdapter extends GenericRecyclerViewAdapter<User, OnRecyclerItemClickListener, UserViewHolder> {

    // ...

    @Override
    public UserViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // or return here any specific ViewHolder according to the viewType
        return new UserViewHolder(inflate(R.layout.item_user, parent), getListener());
    }
}