将单击侦听器设置为RecyclerView

Ami*_* De 11 java android gridview recycler-adapter android-recyclerview

我使用RecyclerView适配器来显示活动内部的数据,我想onClickListener在活动内部实现,目前,我onClickListener正常设置内部适配器,工作正常.

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();
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

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

Thr*_*ian 22

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

我使用 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()
    }
}
Run Code Online (Sandbox Code Playgroud)

另一个更好的用户体验是使用onBindViewHolderwith payLoad,它允许您只更新部分行而不是整行。例如,您的图像、标题和正文成行排列,只有正文经常更改,没有有效负载图像会闪烁并提供糟糕的用户体验。但是通过有效负载,您可以决定应该更新行的哪一部分,从而使您不必重新加载未更新的部分。

  • 答案是用java编写的,我不知道你为什么要发布kotlin。 (2认同)
  • @Adam 可能作者使用了 Kotlin,因为它是 Android 团队推荐使用的语言。 (2认同)

Rea*_*hed 17

您需要在此处查看本教程,以便更好地了解如何实现所需的行为.

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

这是本教程的示例实现.

让我们先来看看界面.

public interface OnItemClickListener {
    void onItemClick(ContentItem item);
}
Run Code Online (Sandbox Code Playgroud)

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

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

public ContentAdapter(List<ContentItem> items, OnItemClickListener listener) {
    this.items = items;
    this.listener = listener;
}
Run Code Online (Sandbox Code Playgroud)

现在在您的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);
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

现在将适配器设置在您的RecyclerView.

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

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

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);
                }
            });
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这会在每次调用“onBindViewHolder”时创建一个新的侦听器,这可能会由于 GC 导致滚动大型列表时性能明显下降 (25认同)
  • 作为参考,Google 官方推荐的在 RecyclerView 中实现“项目点击侦听器”的方法在此演讲中:https://youtu.be/KhLVD6iiZQs?t=2048 (2认同)

Ami*_* De 10

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

示例代码:

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);
            }
        });
    }
}
}
Run Code Online (Sandbox Code Playgroud)

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

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

我从这里找到了这个

希望它对你有帮助。


Nul*_*ion 7

非常简单和干净的解决方案是:

  create a class with the name of RecyclerTouchListener:

    public class RecyclerTouchListener implements RecyclerView.OnItemTouchListener {

        private GestureDetector gestureDetector;
        private ClickListener clickListener;

        public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final ClickListener clickListener) {
            this.clickListener = clickListener;
            gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
                @Override
                public boolean onSingleTapUp(MotionEvent e) {
                    return true;
                }

                @Override
                public void onLongPress(MotionEvent e) {
                    View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
                    if (child != null && clickListener != null) {
                        clickListener.onLongClick(child, recyclerView.getChildPosition(child));
                    }
                }
            });
        }

        @Override
        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {

            View child = rv.findChildViewUnder(e.getX(), e.getY());
            if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
                clickListener.onClick(child, rv.getChildPosition(child));
            }
            return false;
        }

        @Override
        public void onTouchEvent(RecyclerView rv, MotionEvent e) {
        }

        @Override
        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

        }

        public interface ClickListener {
            void onClick(View view, int position);

            void onLongClick(View view, int position);
        }
    }
Run Code Online (Sandbox Code Playgroud)

在您的回收站活动中:

recyclerView.addOnItemTouchListener(new RecyclerTouchListener(getApplicationContext(), recyclerView, new RecyclerTouchListener.ClickListener() {
            @Override
            public void onClick(View view, int position) {
                speech(countries_list_code[position]);
            }

            @Override
            public void onLongClick(View view, int position) {

            }
        }));
Run Code Online (Sandbox Code Playgroud)


dar*_*h f 7

以我的方式,我只是创建了一个实例ClickListener,它将单击事件分派给RecyclerViewActivity 或 Fragment:

class LeagueAdapter(
    onLeagueSelected: (League, Int, View) -> Unit
) : RecyclerView.Adapter<LeagueHolder>() {
    private val dataSet = arrayListOf<League>()

    private val clickListener = View.OnClickListener { view ->
        val adapterPosition = view.tag as Int
        onLeagueSelected(dataSet[adapterPosition], adapterPosition, view)

        // 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)
    }
}
Run Code Online (Sandbox Code Playgroud)

在 Activity 内部,我们有这样的内容:

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