将单击侦听器设置为RecyclerView

时间:2018-04-22 18:21:09

标签: java android gridview android-recyclerview recycler-adapter

我使用RecyclerView适配器来显示活动中的数据,我想在活动中实现onClickListener,目前,我正常在适配器中设置onClickListener,这样可以正常工作。< / p>

public void onBindViewHolder(MyHolder holder, final int position) {
    final Listdata data = listdata.get(position);
    holder.vname.setText(data.getName());

    holder.vname.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Toast.makeText(activity, "clicked on " +position, Toast.LENGTH_SHORT).show();
        }
    });
}

但是我想在活动中实现它,所以我有更好的控制权。这不符合我的目的。我认为它对我们很多人都很有用。

11 个答案:

答案 0 :(得分:13)

您需要在此处查看this tutorial,以便更好地了解如何实现所需的行为。

如果从您的活动处理onClickListener,您需要基于具有接口的回调实现来工作。将接口从活动传递到适配器,然后在单击某些项目时从适配器调用回调函数。

以下是本教程的示例实现。

让我们先来看看界面。

public interface OnItemClickListener {
    void onItemClick(ContentItem item);
}

您需要修改适配器以将侦听器作为参数,如下所述。

private final List<ContentItem> items;
private final OnItemClickListener listener;

public ContentAdapter(List<ContentItem> items, OnItemClickListener listener) {
    this.items = items;
    this.listener = listener;
}

现在,在onBindViewHolder方法中,设置点击监听器。

@Override public void onBindViewHolder(ViewHolder holder, int position) {
    holder.bind(items.get(position), listener);
}

public void bind(final ContentItem item, final OnItemClickListener listener) {
    ...
    itemView.setOnClickListener(new View.OnClickListener() {
        @Override public void onClick(View v) {
            listener.onItemClick(item);
        }
    });
}

现在在RecyclerView中设置适配器。

recycler.setAdapter(new ContentAdapter(items, new ContentAdapter.OnItemClickListener() {
    @Override public void onItemClick(ContentItem item) {
        Toast.makeText(getContext(), "Item Clicked", Toast.LENGTH_LONG).show();
    }
}));

所以整个适配器代码如下所示。

public class ContentAdapter extends RecyclerView.Adapter<ContentAdapter.ViewHolder> {

    public interface OnItemClickListener {
        void onItemClick(ContentItem item);
    }

    private final List<ContentItem> items;
    private final OnItemClickListener listener;

    public ContentAdapter(List<ContentItem> items, OnItemClickListener listener) {
        this.items = items;
        this.listener = listener;
    }

    @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_item, parent, false);
        return new ViewHolder(v);
    }

    @Override public void onBindViewHolder(ViewHolder holder, int position) {
        holder.bind(items.get(position), listener);
    }

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

    static class ViewHolder extends RecyclerView.ViewHolder {

        private TextView name;
        private ImageView image;

        public ViewHolder(View itemView) {
            super(itemView);
            name = (TextView) itemView.findViewById(R.id.name);
            image = (ImageView) itemView.findViewById(R.id.image);
        }

        public void bind(final ContentItem item, final OnItemClickListener listener) {
            name.setText(item.name);
            Picasso.with(itemView.getContext()).load(item.imageUrl).into(image);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override public void onClick(View v) {
                    listener.onItemClick(item);
                }
            });
        }
    }
}

答案 1 :(得分:2)

clickListener内而不是onCreateView内注册onBindViewHolder的性能更高,因为仅在创建视图时才添加侦听器,而不会滚动recyclerView。

我将ListAdapter与DiffUtil回调一起使用,而不是RecyclerViewAdapter

abstract class BaseListAdapter<ItemType>(
    callBack: DiffUtil.ItemCallback<ItemType> = DefaultItemDiffCallback(),
    private inline val onItemClicked: ((ItemType, Int) -> Unit)? = null
) : ListAdapter<ItemType, BaseItemViewHolder>(
    AsyncDifferConfig.Builder<ItemType>(callBack)
        .setBackgroundThreadExecutor(Executors.newSingleThreadExecutor())
        .build()
) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseItemViewHolder {

        return BaseItemViewHolder(

            DataBindingUtil.inflate(
                LayoutInflater.from(parent.context),
                getLayoutRes(viewType),
                parent, false
            )
        ).apply {
            onViewHolderCreated(this, viewType, binding)
        }

    }

    fun createCustomViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {

        return BaseItemViewHolder(

            DataBindingUtil.inflate(
                LayoutInflater.from(parent.context),
                getLayoutRes(viewType),
                parent, false
            )
        )
    }

    override fun onBindViewHolder(
        holder: BaseItemViewHolder,
        position: Int,
        payloads: MutableList<Any>
    ) {
        val item: ItemType? = currentList.getOrNull(position)

        item?.let {
            holder.binding.setVariable(BR.item, item)
            onViewHolderBound(holder.binding, item, position, payloads)
            holder.binding.executePendingBindings()
        }

    }

    override fun onBindViewHolder(holder: BaseItemViewHolder, position: Int) {

    }


    /**
     * get layout res based on view type
     */
    protected abstract fun getLayoutRes(viewType: Int): Int

    /**
     * Called when a ViewHolder is created. ViewHolder is either created first time or
     * when data is refreshed.
     *
     * This method is not called when RecyclerView is being scrolled
     */
    open fun onViewHolderCreated(
        viewHolder: RecyclerView.ViewHolder,
        viewType: Int,
        binding: ViewDataBinding
    ) {

        binding.root.setOnClickListener {
            onItemClicked?.invoke(getItem(viewHolder.bindingAdapterPosition), viewHolder.bindingAdapterPosition)
        }
    }

    /**
     * bind view while RecyclerView is being scrolled and new items are bound
     */
    open fun onViewHolderBound(
        binding: ViewDataBinding,
        item: ItemType,
        position: Int,
        payloads: MutableList<Any>
    ) {

    }


}

open class BaseItemViewHolder(
    val binding: ViewDataBinding
) : RecyclerView.ViewHolder(binding.root)


class DefaultItemDiffCallback<ItemType> : DiffUtil.ItemCallback<ItemType>() {

    override fun areItemsTheSame(
        oldItem: ItemType,
        newItem: ItemType
    ): Boolean {
        return oldItem === newItem
    }

    override fun areContentsTheSame(
        oldItem: ItemType,
        newItem: ItemType
    ): Boolean {
        return oldItem.hashCode() == newItem.hashCode()
    }
}

另一种更好的用户体验是将onBindViewHolderpayLoad一起使用,这使您仅更新部分行而不是整个行。例如,您在行中有图像,标题和正文,并且只有正文频繁更改,而没有有效载荷图像闪烁并提供不良的用户体验。但是使用有效负载,您可以决定应更新行的哪一部分,而不必重新加载未更新的部分。

答案 2 :(得分:1)

[最佳解决方案]

以我的方式,我只创建了一个ClickListener实例,它会将click事件调度到RecyclerView以及Activity或Fragment:

class LeagueAdapter(onLeagueSelected: (League, Int, View) -> Unit) : 
RecyclerView.Adapter<LeagueHolder>() {

    val dataSet = arrayListOf<League>()

    private val clickListener = View.OnClickListener {
        it?.let  {
            val adapterPosition = it.tag as Int
            onLeagueSelected(dataSet[adapterPosition], adapterPosition, it)

            // perform adapter related action here ...
        }
    }


    override fun getItemCount(): Int {
        return dataSet.size
    }

    override fun onBindViewHolder(holder: LeagueHolder, position: Int) {
        // put item position in tag field
        holder.itemView.tag = position
        holder.itemView.setOnClickListener(clickListener)
    }

}

在Activity中,我们有类似这样的内容:

private val headerAdapter = LeagueAdapter { league, i, view ->
    Log.e(TAG, "item clicked $i")
}

答案 3 :(得分:1)

我个人喜欢通过RxJava主题来解决这个问题:

主题是一种桥梁或代理,可在ReactiveX的某些实现中用作观察者和可观察对象。因为它是一个观察者,所以可以预订一个或多个Observable,并且因为它是一个Observable,它可以通过重新发布观察的项目来传递它们,并且还可以发出新的项目。

有关更多信息,请阅读 Understanding RxJava Subject — Publish, Replay, Behavior and Async Subject

在适配器中:

public static PublishSubject<MyData> onClickSubject = PublishSubject.create();

ViewHolder:

public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    .
    .
    .

    @Override
    public void onClick(View view) {
        onClickSubject.onNext(getItem(getAdapterPosition()));
    }
}

将一次性用品添加到CompositeDisposable中,然后将其放置在onDestroy()中:

private CompositeDisposable compositeDisposable = new CompositeDisposable();

在onCreate()中:

compositeDisposable.add(MyAdapter.onClickSubject.subscribe(myData -> {
    //do something here
}));

在onDestroy()中:

compositeDisposable.dispose();

注意:

1。。getItem()是androidx.recyclerview.widget.ListAdapter和androidx.paging.PagedListAdapter的方法,如果要扩展RecyclerView.Adapter,则可以按位置从数据列表中获取项目。

2。。要使用Disposable,您需要RxJava2或更高版本

答案 4 :(得分:1)

RecyclerView小部件在这种情况下只有2个有用的apollo-invalidation-policies

该代码的灵感来自与RecyclerView.OnItemTouchListener相关的TouchEvents示例,并且可以在Activity / Fragment中工作,而无需在Adapter中设置任何侦听器

recyclerView.addOnItemTouchListener(object : RecyclerView.SimpleOnItemTouchListener() {
    var downTouch = false
    override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
        when (e.action) {
            MotionEvent.ACTION_DOWN -> downTouch = true
            MotionEvent.ACTION_UP -> if (downTouch) {
                downTouch = false
                recyclerView.findChildViewUnder(e.x, e.y)?.let {
                    val position = rv.getChildAdapterPosition(it)
                    Toast.makeText(rv.context, "clicked on $position", Toast.LENGTH_SHORT)
                        .show()
                }
            }
            else -> downTouch = false
        }
        return super.onInterceptTouchEvent(rv, e)
    }
})

答案 5 :(得分:0)

您可以让Activity实现View.OnClickListener并将其传递给适配器。以下是一个例子。

class  RAdapter extends RecyclerView.Adapter<>{
    View.OnClickListener listner;
    public RAdapter(View.OnClickListener listner) {
        this.listner = listner;
    }
    public void onBindViewHolder(MyHolder holder, final int position) {
        holder.vname.setOnClickListener(listner);

    }
}

但要处理Activity中的点击,您将需要点击位置。您可以使用adapter.getAdapterPosition()来验证点击了哪个项目。

除此之外,如果您已经引用Activity,则可以在适配器内部使用OnClick并调用Activity的公共方法,并执行操作。

处理ViewHolder点击次数的更好方法。请参阅以下示例。

class Holder extends RecyclerView.ViewHolder implements View.OnClickListener {
        Button button;
        public Holder(View itemView) {
            super(itemView);
            button=itemView.findViewById(R.id.b1);
            button.setOnClickListener(this);
        }
        @Override
        public void onClick(View v) {
            if(v.getId()==R.id.b1){
                int position=getAdapterPosition();
                // Call a public method of Activity here 
                // with postion
            }
        }
    }

答案 6 :(得分:0)

如果我理解正确,你想在活动中设置点击逻辑。

您可以通过在Activity中设置OnClickListener并在Adapter构造函数中传递它来完成此操作。

MyAdapter myAdapter = new MyAdapter(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Toast.makeText(activity, "clicked on " +position, Toast.LENGTH_SHORT).show();
        }
    }));

你的MyAdapter构造函数将是:

final private OnClickListener onClickListener;

public MyAdapter(OnClickListener onClickListener) {
    this.OnClickListener = OnClickListener;
}

所以你的新代码就是这样的

public void onBindViewHolder(MyHolder holder, final int position) {
    final Listdata data = listdata.get(position);
    holder.vname.setText(data.getName());

    holder.vname.setOnClickListener(onClickListener);

}

答案 7 :(得分:0)

  

为适配器类创建接口

private OnItemClickListener mListener;

public CustomAdapter(List<Listdata> listdata, OnItemClickListener listener) {
    mListener = listener;
    ...
    ...
}

private class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

    ViewHolder(View view) {
        ...
        ...
        view.setOnClickLister(this);
    }

    @override
    public void onClick(View v) {
        mListener.onAdapterItemClick(getAdapterPosition())
    }
}

interface OnItemClickListener {
    void onAdapterItemClick(int position);
}
  

让活动实现界面

public class CustomListActivity extends AppCompatActivity implements OnItemClickListener {

...
...

@override
public void onAdapterItemClick(int position) {
    Toast.makeText(activity, "clicked on " +position, Toast.LENGTH_SHORT).show();
}

还有另一种方法,请查看this实施

答案 8 :(得分:0)

我发现超级傻瓜简单的方法!我推荐这个

示例代码:

public class ContentAdapter extends RecyclerView.Adapter<ContentAdapter.ViewHolder> {

public interface OnItemClickListener {
    void onItemClick(ContentItem item);
}

private final List<ContentItem> items;
private final OnItemClickListener listener;

public ContentAdapter(List<ContentItem> items, OnItemClickListener listener) {
    this.items = items;
    this.listener = listener;
}

@Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_item, parent, false);
    return new ViewHolder(v);
}

@Override public void onBindViewHolder(ViewHolder holder, int position) {
    holder.bind(items.get(position), listener);
}

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

static class ViewHolder extends RecyclerView.ViewHolder {

    private TextView name;
    private ImageView image;

    public ViewHolder(View itemView) {
        super(itemView);
        name = (TextView) itemView.findViewById(R.id.name);
        image = (ImageView) itemView.findViewById(R.id.image);
    }

    public void bind(final ContentItem item, final OnItemClickListener listener) {
        name.setText(item.name);
        Picasso.with(itemView.getContext()).load(item.imageUrl).into(image);
        itemView.setOnClickListener(new View.OnClickListener() {
            @Override public void onClick(View v) {
                listener.onItemClick(item);
            }
        });
    }
}
}

并使用以下代码使用RecyclerView适配器:

recycler.setAdapter(new ContentAdapter(items, new ContentAdapter.OnItemClickListener() {
@Override public void onItemClick(ContentItem item) {
    Toast.makeText(getContext(), "Item Clicked", Toast.LENGTH_LONG).show();
}
}));

我从here

找到了这个

希望它对您有所帮助。

答案 9 :(得分:0)

CodePath 中记录了另一种非常简单的方法。

ItemClickSupport.addTo(recyclerView).setOnItemClickListener(
    new ItemClickSupport.OnItemClickListener() {
        @Override
        public void onItemClicked(RecyclerView recyclerView, int position, View v) {
            // do stuff
        }
    }
);

ItemClickSupport 的实现。

答案 10 :(得分:-1)

我的项目中总是有一个通用适配器,以避免每次我使用Recyclerview时都创建一个Adapter类。这里有一些例子

public class AdapterRecyclerviewTextOnly extends RecyclerView.Adapter<AdapterRecyclerviewTextOnly.ViewHolder> {
private RecyclerView recyclerView;
private OnRecyclerviewListener onRecyclerviewListener;

public interface OnRecyclerviewListener {
    void onRecyclerviewBind(RecyclerView recyclerView, AdapterRecyclerviewTextOnly.ViewHolder viewHolder, int position);
    void onRecyclerviewClick(RecyclerView recyclerView, int position);
    int onItemCount(RecyclerView recyclerView);
}

public void setOnRecyclerviewListener(OnRecyclerviewListener listener) { this.onRecyclerviewListener = listener; }

public AdapterRecyclerviewTextOnly(RecyclerView recyclerView) {
    super();
    this.recyclerView = recyclerView;
}


public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    RecyclerView recyclerView;
    public TextView textView;

    ViewHolder(RecyclerView recyclerView, View itemView) {
        super(itemView);
        this.recyclerView = recyclerView;
        this.itemView.setOnClickListener(this);

        this.textView = itemView.findViewById(R.id.textview_title);
    }

    void onBind(int position) { onRecyclerviewListener.onRecyclerviewBind(this.recyclerView, this, position); }

    @Override
    public void onClick(View v) {
        onRecyclerviewListener.onRecyclerviewClick(this.recyclerView, getAdapterPosition());
    }
}

@Override
public AdapterRecyclerviewTextOnly.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View inflatedView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recyclerview_text_only, parent, false);
    return new ViewHolder(this.recyclerView, inflatedView);
}

@Override
public void onBindViewHolder(AdapterRecyclerviewTextOnly.ViewHolder holder, int position) {
    holder.onBind(position);
}

@Override
public int getItemCount() {
    return onRecyclerviewListener.onItemCount(this.recyclerView);
}
}

然后在“活动类”中,可以将此适配器与一起使用:

    this.recyclerView = findViewById(R.id.recyclerview);
    this.recyclerView.setHasFixedSize(true);
    this.recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
    AdapterRecyclerviewTextOnly recyclerViewAdapter = new AdapterRecyclerviewTextOnly(this.recyclerView);
    this.recyclerView.setAdapter(this.recyclerViewAdapter);
    this.recyclerViewAdapter.setOnRecyclerviewListener(new AdapterRecyclerviewTextOnly.OnRecyclerviewListener() {
        @Override
        public void onRecyclerviewBind(RecyclerView recyclerView, AdapterRecyclerviewTextOnly.ViewHolder viewHolder, int position) {

        }

        @Override
        public void onRecyclerviewClick(RecyclerView recyclerView, int position) {

        }

        @Override
        public int onItemCount(RecyclerView recyclerView) { 
}
});

您也可以在2或3个recyclerview中重复使用此功能。 首先,声明一个globar侦听器private AdapterRecyclerviewTextOnly.OnRecyclerviewListener listener;

然后使用新对象初始化侦听器,然后使用侦听器设置每个recyclerview。使用特定的标识符:

if (recyclerView == recyclerViewA){ } else if (recyclerView == recyclerViewB) { }来管理适配器内的recyclerview。