在RecyclerView中为单个接口使用多个onClick方法。

huangapple go评论82阅读模式
英文:

Using multiple onClick methods in a single interface for RecyclerView

问题

Context:

我在我的待办事项列表应用程序中实现了一个RecyclerView。

我希望能够为RecyclerView中的项目使用各种onClick方法,因此我创建了一个名为onTaskListener的接口。

该接口具有两个方法存根,一个用于onClick,一个用于onLongClick。在我的ViewHolder中,我实现了onClick()和onLongClick()方法,它们只是将控制权传递给我的onTaskClickListener()。

在我的适配器中,我创建了一个onTaskClickListener()。然后在我的主活动中,我实现了onTaskClickListener()中的方法。

问题: 我的问题是,虽然我的onTaskClick()完美地工作,但我的onTaskLongClick()似乎根本不起作用。我设置RecyclerView/适配器/ViewHolder/ViewModel模式时是否有问题?如果我实现接口的方式有问题,如何在单个接口中包含多种类型的点击事件?

以下是每个文件的相关内容(我知道代码很多,对于这堆代码,我非常抱歉):

onTaskClickListener.java:

public interface OnTaskListener {
    void onTaskClick(int position);
    void onTaskLongClick(int position);
}

itemViewHolder.java:

public class itemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {

    View itmView;
    TextView txtView;
    OnTaskListener onTaskListener;

    public itemViewHolder(@NonNull View itemView, OnTaskListener inputOnTaskListener) {
        super(itemView);
        itmView = itemView;
        txtView = itemView.findViewById(R.id.txtTask);
        this.onTaskListener = inputOnTaskListener;
        itemView.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        onTaskListener.onTaskClick(getAdapterPosition());
    }

    @Override
    public boolean onLongClick(View view) {
        onTaskListener.onTaskLongClick(getAdapterPosition());
        return true;
    }
}

dataAdapter.java

public class dataAdapter extends RecyclerView.Adapter<itemViewHolder> {

    List<taskItem> taskItemList;
    private OnTaskListener onTaskListener;

    public dataAdapter(List<taskItem> inputTaskItemList, OnTaskListener inputOnTaskListener){
        this.taskItemList = inputTaskItemList;
        this.onTaskListener = inputOnTaskListener;
    }

    @NonNull
    @Override
    public itemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View localView = LayoutInflater.from(parent.getContext()).inflate(R.layout.taskholder, parent, false);
        return new itemViewHolder(localView, onTaskListener);
    }

    @Override
    public void onBindViewHolder(@NonNull itemViewHolder holder, final int position) {
        holder.txtView.setText(taskItemList.get(position).taskTitle);
    }

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

MainActivity.java

public class MainActivity extends AppCompatActivity implements OnTaskListener {
    private RecyclerView taskList;
    private dataAdapter localAdapter;
    List<taskItem> myItems;
    private RecyclerView.LayoutManager localLayoutManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        taskList = findViewById(R.id.taskList);
        localLayoutManager = new LinearLayoutManager(this);
        taskList.setLayoutManager(localLayoutManager);
        taskList.setHasFixedSize(true);

        myItems = new ArrayList<>();
        myItems.add(new taskItem("groceries"));
        myItems.add(new taskItem("practice bjj"));

        localAdapter = new dataAdapter(myItems, this);
        taskList.setAdapter(localAdapter);
    }

    @Override
    public void onTaskClick(int position) {
        // 点击逻辑
    }

    @Override
    public void onTaskLongClick(int position) {
        // 长按逻辑
    }
}
英文:

Context:

I've implemented a RecyclerView in my to-do list app.

I wanted to be able to use various onClick methods for items within the RecyclerView so I created an interface called onTaskListener.

This interface has two method stubs, one for onClick and one for onLongClick. In my ViewHolder, I implement both the onClick() and onLongClick() methods which simply pass off control to my onTaskClickListener().

In my adapter, I create an onTaskClickListener().
Then in my main activity, I implement the methods within onTaskClickListener().

My issue is that while my onTaskClick() works perfectly, my onTaskLongClick doesn't seem to function at all. Is there something wrong with the way I set up my RecyclerView/Adapter/ViewHolder/ViewModel pattern?

Question: If the way I have implemented my interface is wrong, how do I include multiple types of click events within a single interface?

Here are the relevant contents of each file (I know it's a lot, I'm very sorry for the wall of code):

onTaskClickListener.java:

public interface OnTaskListener {
    void onTaskClick(int position); // Interfaces are implicitly abstract
    void onTaskLongClick(int position);
}

itemViewHolder.java:

public class itemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {

    View itmView; // This is the general view
    TextView txtView; // This is the specific text view that shows up as a singular task in the list of to-do tasks
    OnTaskListener onTaskListener; // Create an OnTaskListener inside our view holder which allows the view holder to realize it&#39;s been clicked

    public itemViewHolder(@NonNull View itemView, OnTaskListener inputOnTaskListener) {
        super(itemView);
        itmView = itemView;
        txtView = itemView.findViewById(R.id.txtTask);
        this.onTaskListener = inputOnTaskListener; // Take an onTaskListener that is passed into the object and store it internally
        itemView.setOnClickListener(this); // passes the View.OnClickListener context to the itemView via &quot;this&quot;
    }

    @Override
    public void onClick(View view) {
        onTaskListener.onTaskClick(getAdapterPosition()); // This says that whenever we register a click event, we pass the logic onto the taskClick event
    }

    @Override
    public boolean onLongClick(View view) {
        onTaskListener.onTaskLongClick(getAdapterPosition()); // This says that whenever we register a longClick event, we pass the logic onto the taskClick event
        return true; // This means that we have successfully consumed the long click event. No other click events will be notified
    }
}

dataAdapter.java

public class dataAdapter extends RecyclerView.Adapter&lt;itemViewHolder&gt; {

    List&lt;taskItem&gt; taskItemList;
    private OnTaskListener onTaskListener;

    public dataAdapter(List&lt;taskItem&gt; inputTaskItemList, OnTaskListener inputOnTaskListener){
        this.taskItemList = inputTaskItemList;
        this.onTaskListener = inputOnTaskListener;
    }

    @NonNull
    @Override
    public itemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View localView = LayoutInflater.from(parent.getContext()).inflate(R.layout.taskholder, parent, false); //Don&#39;t even know what this line does, it&#39;s all so over my head
        return new itemViewHolder(localView, onTaskListener); // Return an instance of whatever we made directly above this line
    }

    @Override
    public void onBindViewHolder(@NonNull itemViewHolder holder, final int position) {
        holder.txtView.setText(taskItemList.get(position).taskTitle);
        // Look inside our ViewModel and get the text for this specific instance of the ViewModel, which corresponds to the current position
    }

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

MainActivity.java

public class MainActivity extends AppCompatActivity implements OnTaskListener{
    private RecyclerView taskList; // Creates a RecyclerView to hook up to our RecyclerView widget in the UI
    private dataAdapter localAdapter; // Instantiates our custom adapter class
    List&lt;taskItem&gt; myItems; // Stores the items in a list of taskItem&#39;s
    private RecyclerView.LayoutManager localLayoutManager; // God knows what this does :(

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        taskList = findViewById(R.id.taskList); // Connects our list from UI to recycler view code
        localLayoutManager = new LinearLayoutManager(this); // assigns our localLayoutManager to an actual Layout Manager
        taskList.setLayoutManager(localLayoutManager); // connecting our layout manager to our recycler view
        taskList.setHasFixedSize(true);

        myItems = new ArrayList&lt;&gt;(); // Now we FINALLY make our to-do list and populate it with actual tasks
        myItems.add(new taskItem(&quot;groceries&quot;));
        myItems.add(new taskItem(&quot;practice bjj&quot;));

        localAdapter = new dataAdapter(myItems, this); // Pass the to do list to the adapter so it can feed it to the recycler view
        taskList.setAdapter(localAdapter); // Lastly set the recycler view&#39;s adapter to the one we made above
        
    }

    @Override
    public void onTaskClick(int position) {
        taskItem currentTask = myItems.get(position);
        if(!(currentTask.taskTitle.startsWith(&quot;Done: &quot;))){ // Logic that marks a task as done on tap
            currentTask.taskTitle = &quot;Done: &quot; + currentTask.taskTitle;
            //logic that moves the tapped item to bottom of list
            myItems.remove(position);
            myItems.add(myItems.size(), currentTask);
            localAdapter.notifyItemMoved(position, myItems.size());
        }
        else if(myItems.get(position).taskTitle.startsWith(&quot;Done: &quot;)){ // Logic for if user taps a task already marked &quot;done&quot;
            currentTask.taskTitle = currentTask.taskTitle.replaceFirst(&quot;Done: &quot;, &quot;&quot;);
            myItems.set(position, currentTask); // Remove prefix
            localAdapter.notifyItemChanged(position);
            myItems.remove(position);
            myItems.add(0, currentTask);
        }
        localAdapter.notifyDataSetChanged(); // Let the activity know that the data has changed
    }

    @Override
    public void onTaskLongClick(int position) { // This branch deals with deleting tasks on long click
        myItems.remove(position);
        localAdapter.notifyItemRemoved(position); // Item has been deleted
    }
}

答案1

得分: 3

你从不调用 setOnLongClickListener()

public itemViewHolder(@NonNull View itemView, OnTaskListener inputOnTaskListener) {
    super(itemView);
    itmView = itemView;
    txtView = itemView.findViewById(R.id.txtTask);
    this.onTaskListener = inputOnTaskListener; // 将传入对象的 onTaskListener 存储在内部
    itemView.setOnClickListener(this); // 通过 "this" 将 View.OnClickListener 上下文传递给 itemView

    // 添加这一行
    itemView.setOnLongClickListener(this); // 通过 "this" 将 View.OnLongClickListener 上下文传递给 itemView
}

或者,您可以完全内联整个 OnLongClickListener(对于 OnClickListener 也类似)来避免完全使用 this

itemView.setOnLongClickListener(new View.OnLongClickListener() {
    @Override
    public boolean onLongClick(View v) {
        onTaskListener.onTaskLongClick(getAdapterPosition()); // 这表明每当我们注册 longClick 事件时,我们将逻辑传递到 taskClick 事件
        return true; // 这意味着我们已成功消耗了长点击事件。不会通知其他点击事件
    }
});

从而避免让您的 itemViewHolder 类实现 OnLongClickListener 接口,并且无法忘记调用 setOnLongClickListener()

英文:

You never call setOnLongClickListener():

public itemViewHolder(@NonNull View itemView, OnTaskListener inputOnTaskListener) {
    super(itemView);
    itmView = itemView;
    txtView = itemView.findViewById(R.id.txtTask);
    this.onTaskListener = inputOnTaskListener; // Take an onTaskListener that is passed into the object and store it internally
    itemView.setOnClickListener(this); // passes the View.OnClickListener context to the itemView via &quot;this&quot;

    // Add this line
    itemView.setOnLongClickListener(this); // passes the View.OnLongClickListener context to the itemView via &quot;this&quot;
}

Alternatively, you can avoid going through this entirely by inlining the entire OnLongClickListener (and similarly for the OnClickListener):

itemView.setOnLongClickListener(new View.OnLongClickListener() {
    @Override
    public boolean onLongClick(View v) {
        onTaskListener.onTaskLongClick(getAdapterPosition()); // This says that whenever we register a longClick event, we pass the logic onto the taskClick event
        return true; // This means that we have successfully consumed the long click event. No other click events will be notified
    }
 });

Thus avoiding having your itemViewHolder class implement the OnLongClickListener interface and making it impossible to forget to call setOnLongClickListener().

huangapple
  • 本文由 发表于 2020年9月25日 12:15:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/64057723.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定