如何使用泛型将一个RecyclerView适配器用于不同类型的对象?

时间:2016-05-27 11:49:55

标签: java android generics android-recyclerview

我有一个RecyclerView我希望有时会使用String个对象和{其他时间使用Product个对象。所以我开始以这种方式创建它的管理器适配器:

// BaseSearchAdapter is the class that contains the 'List<T> mItems' member variable
public class SearchAdapter<T> extends BaseSearchAdapter<SearchAdapter.ViewHolder, T> {

    private Context mContext;

    public SearchAdapter(Context context, List<T> items) {
        mContext = context;
        mItems = new ArrayList<>(items);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        T item = mItems.get(position);
        holder.bind(item);
    }

    public class ViewHolder extends RecyclerView.ViewHolder {

        TextView textLabel;

        public ViewHolder(View v) {

        }

        public void bind(T item) {
            textLabel.setText(...); // How to handle T ?
        }
    }
}

根据计划,T可以是StringProduct

我的问题是如何在这种情况下将数据(无论是String还是Product)对象适当地绑定到相应的视图?或者有更好的方法来解决这个问题吗?

4 个答案:

答案 0 :(得分:6)

// BaseSearchAdapter is the class that contains the 'List<T> mItems' member variable
public class SearchAdapter<T> extends BaseSearchAdapter<SearchAdapter.ViewHolder<T>, T> {

    private Context mContext;
    private ViewHolderBinder<T> mBinder;

    public SearchAdapter(Context context, List<T> items, ViewHolderBinder<T> binder) {
        mContext = context;
        mItems = new ArrayList<>(items);
        mBinder = binder;
    }

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

    public static class ViewHolder<T> extends RecyclerView.ViewHolder {
        ViewHolderBinder<T> mBinder;

        TextView textLabel;


        public ViewHolder(View v, ViewHolderBinder<T> binder) {
            textLabel = (TextView)v.findViewById(R.id.text_label);
            this.mBinder = binder;
        }

        public void bind(T item) {
            binder.bind(this, item);
        }
    }

    public interface ViewHolderBinder<T> {
        void bind(ViewHolder<T> viewHolder, T item);
    }

    public static class StringViewHolderBinder implements ViewHolderBinder<String> {
        @Override
        public void bind(ViewHolder<String> viewHolder, String item) {
             viewHolder.textLabel.setText(item);
        }
    }

    public static class ProductViewHolderBinder implements ViewHolderBinder<Product> {
        @Override
        public void bind(ViewHolder<Product> viewHolder, Product item) {
             viewHolder.textLabel.setText(item.getName());
        }
    }
}

答案 1 :(得分:5)

我在项目中所做的是创建一个包含所有常见操作的类BaseRecyclerAdapter。然后对于大多数适配器,我必须定义的是ViewHolder和布局。

<强>更新 正如请求的那样,我发布了一个更全面的BaseRecyclerAdapter版本(根据项目需要而有所不同)。还包括一个简单的手势回调,允许您轻松启用滑动删除或拖动以重新排序操作。

注意:此版本更新回收商项目布局的膨胀方式。我现在更喜欢在BaseRecyclerAdapter.ViewHolder构造函数中扩充,允许在扩展的ViewHolder构造函数中指定布局。

示例基本适配器

public abstract class BaseRecyclerAdapter<T> extends RecyclerView.Adapter<BaseRecyclerAdapter.ViewHolder> {

    private final List<T> items = new ArrayList<>();
    OnItemSelectedListener<T> onItemSelectedListener = SmartNull.create(OnItemSelectedListener.class);

    public BaseRecyclerAdapter setOnItemSelectedListener(OnItemSelectedListener<T> onItemSelectedListener) {
        if (onItemSelectedListener == null) {
            this.onItemSelectedListener = SmartNull.create(OnItemSelectedListener.class);
        } else {
            this.onItemSelectedListener = onItemSelectedListener;
        }
        return this;
    }

    public boolean isEmpty() {
        return items.isEmpty();
    }

    public void setItems(List<T> items) {
        this.items.clear();
        this.items.addAll(items);
        notifyDataSetChanged();
    }

    public void addItems(T... items) {
        addItems(Arrays.asList(items));
    }

    public void addItems(List<T> items) {
        int startPosition = this.items.size() - 1;
        this.items.addAll(items);
        notifyItemRangeInserted(startPosition, items.size());
    }

    public void removeItem(int position) {
        T item = items.remove(position);
        if (itemRemovedListener != null) {
            itemRemovedListener.onItemRemoved(item);
        }
        notifyItemRemoved(position);
    }

    public void removeItem(T t) {
        int index = items.indexOf(t);
        if (index >= 0) {
            removeItem(index);
        }
    }

    public void addItem(T item) {
        items.add(item);
        notifyItemInserted(getItemCount() - 1);
    }

    public void moveItem(int startPosition, int targetPosition) {
        if (startPosition < targetPosition) {
            for (int i = startPosition; i < targetPosition; i++) {
                Collections.swap(items, i, i + 1);
            }
        } else {
            for (int i = startPosition; i > targetPosition; i--) {
                Collections.swap(items, i, i - 1);
            }
        }

        notifyItemMoved(startPosition, targetPosition);
    }

    public List<T> getItems() {
        return new ArrayList<>(items);
    }

    public void setItemAt(int position, T item){
        items.set(position, item);
        notifyItemChanged(position);
    }

    public void refreshItem(T item) {
        int i = items.indexOf(item);
        if (i >= 0) {
            notifyItemChanged(i);
        }
    }

    protected void setItemWithoutUpdate(int position, T item){
        items.set(position, item);
    }

    public int indexOf(T t) {
        return items.indexOf(t);
    }

    @SuppressWarnings("unchecked")
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        holder.bindItemAt(getItemAt(position), position);
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    public T getItemAt(int position) {
        return items.get(position);
    }

    private void onItemSelected(int position) {
        if (isValidPosition(position)) {
            onItemSelectedListener.onItemSelected(getItemAt(position));
        }
    }

    boolean isValidPosition(int position) {
        return position >=0 && position < items.size();
    }

    public abstract class ViewHolder<T> extends RecyclerView.ViewHolder implements View.OnClickListener {

        public ViewHolder(ViewGroup parent, @LayoutRes int layoutId) {
            super(LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false));
            itemView.setOnClickListener(this);
        }

        public ViewHolder(View view) {
            super(view);
            itemView.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {
            onItemSelected(getAdapterPosition());
        }

        public abstract void bindItemAt(T t, int position);

    }

    public interface OnItemSelectedListener<T> {
        void onItemSelected(T t);
    }
}

示例实施

public class ExampleAdapter extends com.stratospherequality.mobileworkforce.modules.common.BaseRecyclerAdapter<Long> {

    @Override
    protected RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new ViewHolder(parent);
    }

    private class ViewHolder extends RecyclerViewHolder<Long> {

        TextView text;

        public ViewHolder(ViewGroup parent) {
            super(parent, R.layout.row_my_layout);
            // I typically use ButterKnife here but this works as well
            text = (TextView) itemView.findViewById(R.id.text); 
        }

        @Override
        public void setItem(Long value, int position) {
            text.setText("#" + value);
        }
    }
}

Base GestureCallback

public class AdapterGestureCallback extends ItemTouchHelper.SimpleCallback {

    public interface OnRemoveItemCallback<T> {
        void onRemoveItem(BaseRecyclerAdapter<T> adapter, T t);
    }

    public enum Direction {
        UP(ItemTouchHelper.UP),
        DOWN(ItemTouchHelper.DOWN),
        LEFT(ItemTouchHelper.LEFT),
        RIGHT(ItemTouchHelper.RIGHT),
        START(ItemTouchHelper.START),
        END(ItemTouchHelper.END);

        public final int value;

        Direction(int value) {
            this.value = value;
        }
    }

    private final BaseRecyclerAdapter adapter;
    private OnRemoveItemCallback onRemoveItemCallback;
    private boolean enabled = true;

    public AdapterGestureCallback(BaseRecyclerAdapter adapter) {
        super(0, 0);
        this.adapter = adapter;
    }

    public AdapterGestureCallback setOnRemoveItemCallback(OnRemoveItemCallback onRemoveItemCallback) {
        this.onRemoveItemCallback = onRemoveItemCallback;
        return this;
    }

    public AdapterGestureCallback setEnabled(boolean enabled) {
        this.enabled = enabled;
        return this;
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        adapter.moveItem(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        return true;
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        int position = viewHolder.getAdapterPosition();
        if (onRemoveItemCallback == null){
            adapter.removeItem(position);
        } else {
            onRemoveItemCallback.onRemoveItem(adapter, adapter.getItemAt(position));
    }
    }

    public AdapterGestureCallback withDragDirections(Direction... dragDirections) {
        setDefaultDragDirs(valueFor(dragDirections));
        return this;
    }

    public AdapterGestureCallback withSwipeDirections(Direction... swipeDirections) {
        setDefaultSwipeDirs(valueFor(swipeDirections));
        return this;
    }

    @Override
    public boolean isItemViewSwipeEnabled() {
        return enabled;
    }

    @Override
    public boolean isLongPressDragEnabled() {
        return enabled;
    }

    public int valueFor(Direction... directions) {
        int val = 0;
        for (Direction d : directions) {
            val |= d.value;
        }

        return val;
    }

    public AdapterGestureCallback attach(RecyclerView recyclerView) {
        new ItemTouchHelper(this).attachToRecyclerView(recyclerView);
        return this;
    }
}

使用滑动的GestureCallback

new AdapterGestureCallback(adapter)
            .withSwipeDirections(AdapterGestureCallback.Direction.LEFT, AdapterGestureCallback.Direction.RIGHT)
            .setOnRemoveItemCallback(this)
            .attach(recyclerView);

答案 2 :(得分:0)

例如,ArrayAdapter<T>使用的方法是在您传递的toString()上调用T。这当然适用于String,您必须在toString()中实施Product以返回有意义的陈述。

答案 3 :(得分:0)

可以像Holder一样使用泛型:

public abstract class ActionBarAdapter<T,VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {

 private Context mContext;

 public SearchAdapter(Context context, List<T> items) {
    mContext = context;
    mItems = new ArrayList<>(items);
 }
}