如何知道单击了特定 ListView 项目中的哪个视图

发布于 2024-12-21 11:36:27 字数 535 浏览 2 评论 0原文

我有一个 ListView ,其中包含派生自 BaseAdapter 的我自己的自定义适配器。 ListView 中的每个项目都有 ImageViewTextView 等子项目。

我如何知道用户单击了这些子项中的哪一项?例如,是否可以在 getView 函数中附加一个侦听器,或者这可能是一个问题?

/ Henrik

编辑:目前我在 Activity 中有一个 onItemClick,其中包含 ListView。有没有什么好方法可以通过检查onItemClick中的参数来知道ListView中特定项目中的哪个子项目已被按下。

@Override 
public void onItemClick(AdapterView<?> a, View v, int pos, long id) {
.
.
}

I'm having a ListView with my own custom adapter derived from a BaseAdapter. Each item in the ListView has sub items such as ImageView and TextView.

How can I know which one of these sub items the user clicked? Is it possible to attach a listener in the getView function for example, or could that be a problem?

/ Henrik

Edit: Currently I have a onItemClick in the Activity which contains the ListView. Is there any good way to know which sub item in a specific item in the ListView which has been pressed by checking the params in the onItemClick.

@Override 
public void onItemClick(AdapterView<?> a, View v, int pos, long id) {
.
.
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

鼻尖触碰 2024-12-28 11:36:27

我使用了 Miga 的爱好编程 中的一个想法。

关键是从新的 onClick 侦听器调用 PerformItemClick()。这会将点击传递给已用于列表视图的 onItemClick()。它是如此的快速和简单,我觉得我在作弊。

这是来自列表适配器的 getView():

@Override
public View getView(final int position, View convertView, final ViewGroup parent) {

    // Check if an existing view is being reused, otherwise inflate the view
    if (convertView == null) {
        convertView = LayoutInflater.from(getContext()).inflate(R.layout.one_line, parent, false);
    }

    // This chunk added to get textview click to register in Fragment's onItemClick()
    // Had to make position and parent 'final' in method definition
    convertView.findViewById(R.id.someName).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            ((ListView) parent).performItemClick(v, position, 0);
        }
    });
    // do stuff...
}

和 onItemClick():

@Override
public void onItemClick(AdapterView adapterView, View view, int position, long id) {

    long viewId = view.getId();

    if (viewId == R.id.someName) {
        Toast.makeText(getActivity(), "someName item clicked", Toast.LENGTH_SHORT).show();
    }
    else {
        Toast.makeText(getActivity(), "ListView clicked: " + id, Toast.LENGTH_SHORT).show();

   }
}

I used an idea from Miga's Hobby Programming.

The key is calling performItemClick() from the new onClick listener. This passes the click on through to the onItemClick() that's already being used for the listview. It's so quick and easy, I feel like I'm cheating.

Here's getView(), from the list adapter:

@Override
public View getView(final int position, View convertView, final ViewGroup parent) {

    // Check if an existing view is being reused, otherwise inflate the view
    if (convertView == null) {
        convertView = LayoutInflater.from(getContext()).inflate(R.layout.one_line, parent, false);
    }

    // This chunk added to get textview click to register in Fragment's onItemClick()
    // Had to make position and parent 'final' in method definition
    convertView.findViewById(R.id.someName).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            ((ListView) parent).performItemClick(v, position, 0);
        }
    });
    // do stuff...
}

And the onItemClick():

@Override
public void onItemClick(AdapterView adapterView, View view, int position, long id) {

    long viewId = view.getId();

    if (viewId == R.id.someName) {
        Toast.makeText(getActivity(), "someName item clicked", Toast.LENGTH_SHORT).show();
    }
    else {
        Toast.makeText(getActivity(), "ListView clicked: " + id, Toast.LENGTH_SHORT).show();

   }
}
Spring初心 2024-12-28 11:36:27

你可以做到的。您需要修改您的 getView 方法:

@Override
public View getView(final int position, View row, final ViewGroup parent) {
    ...     
    YourWrapper wrapper = null;
    if (row == null) {
        row = getLayoutInflater().inflate(R.layout.your_row, parent, false);
        wrapper = new YourWrapper(row);
        row.setTag(wrapper);
    } else {
        wrapper = (YourWrapper) row.getTag();
    }

    wrapper.yourSubView.setOnClickListener(new View.OnClickListener()   
    {               
    @Override
    public void onClick(View v) {
        // do something
    }
    ...
}

You can do it. You need to modify your getView method:

@Override
public View getView(final int position, View row, final ViewGroup parent) {
    ...     
    YourWrapper wrapper = null;
    if (row == null) {
        row = getLayoutInflater().inflate(R.layout.your_row, parent, false);
        wrapper = new YourWrapper(row);
        row.setTag(wrapper);
    } else {
        wrapper = (YourWrapper) row.getTag();
    }

    wrapper.yourSubView.setOnClickListener(new View.OnClickListener()   
    {               
    @Override
    public void onClick(View v) {
        // do something
    }
    ...
}

ListView 回收行视图对象,并在调用“getView”时为其分配新数据,因此使用的方法是在 getView 函数中添加侦听器。以下是应用程序中的代码示例,展示了如何完成此操作:

private class DeletePlayerAdapter extends ArrayAdapter<Player> {
        Context context;
        int layoutResourceId;
        ArrayList<Player> data;

        public DeletePlayerAdapter(Context context, int layout,
                ArrayList<Player> list) {
            super(context, layout, list);
            this.layoutResourceId = layout;
            this.context = context;
            this.data = list;
        }

        @Override
        public View getView(final int position, View convertView,
                ViewGroup parent) {
            View row = convertView;
            PlayerHolder holder = null;
            if (row == null) {
                LayoutInflater inflater = ((Activity) context)
                        .getLayoutInflater();
                row = inflater.inflate(layoutResourceId, parent, false);
                holder = new PlayerHolder();
                holder.player_name = (TextView) row
                        .findViewById(R.id.player_name);
                holder.player_number = (TextView) row
                        .findViewById(R.id.player_number);
                holder.seeded_button = (ImageButton) row
                        .findViewById(R.id.delete_toggle);
                holder.player_name.setTypeface(tf);
                holder.player_number.setTypeface(tf);
                row.setTag(holder);
                players_array.get(position).marked_for_delete = false;

            } else {
                Log.d("PLAYER_ADAPTER", "NOT_NULL ROW");
                holder = (PlayerHolder) row.getTag();
            }
            holder.seeded_button.setOnClickListener(new OnClickListener() {
                //
                // Here is the magic sauce that makes it work.
                //
                private int pos = position;

                public void onClick(View v) {
                    ImageButton b = (ImageButton) v;
                    if (b.isSelected()) {
                        b.setSelected(false);
                        players_array.get(pos).marked_for_delete = false;
                    } else {
                        b.setSelected(true);
                        players_array.get(pos).marked_for_delete = true;
                    }
                }
            });
            Player p = data.get(position);
            holder.player_name.setText(p.name);
            holder.player_number.setText(String.valueOf(position+1));
            holder.seeded_button
                    .setSelected(players_array.get(position).marked_for_delete);
            return row;
        }

    }

    static class PlayerHolder {
        TextView player_number;
        TextView player_name;
        ImageButton seeded_button;
    }

ListView recycles the row view objects and assigns fresh data on them when "getView" is called, so the approach to use, is to add a listener in the getView function. Here is a code sample from an app that shows how that is done:

private class DeletePlayerAdapter extends ArrayAdapter<Player> {
        Context context;
        int layoutResourceId;
        ArrayList<Player> data;

        public DeletePlayerAdapter(Context context, int layout,
                ArrayList<Player> list) {
            super(context, layout, list);
            this.layoutResourceId = layout;
            this.context = context;
            this.data = list;
        }

        @Override
        public View getView(final int position, View convertView,
                ViewGroup parent) {
            View row = convertView;
            PlayerHolder holder = null;
            if (row == null) {
                LayoutInflater inflater = ((Activity) context)
                        .getLayoutInflater();
                row = inflater.inflate(layoutResourceId, parent, false);
                holder = new PlayerHolder();
                holder.player_name = (TextView) row
                        .findViewById(R.id.player_name);
                holder.player_number = (TextView) row
                        .findViewById(R.id.player_number);
                holder.seeded_button = (ImageButton) row
                        .findViewById(R.id.delete_toggle);
                holder.player_name.setTypeface(tf);
                holder.player_number.setTypeface(tf);
                row.setTag(holder);
                players_array.get(position).marked_for_delete = false;

            } else {
                Log.d("PLAYER_ADAPTER", "NOT_NULL ROW");
                holder = (PlayerHolder) row.getTag();
            }
            holder.seeded_button.setOnClickListener(new OnClickListener() {
                //
                // Here is the magic sauce that makes it work.
                //
                private int pos = position;

                public void onClick(View v) {
                    ImageButton b = (ImageButton) v;
                    if (b.isSelected()) {
                        b.setSelected(false);
                        players_array.get(pos).marked_for_delete = false;
                    } else {
                        b.setSelected(true);
                        players_array.get(pos).marked_for_delete = true;
                    }
                }
            });
            Player p = data.get(position);
            holder.player_name.setText(p.name);
            holder.player_number.setText(String.valueOf(position+1));
            holder.seeded_button
                    .setSelected(players_array.get(position).marked_for_delete);
            return row;
        }

    }

    static class PlayerHolder {
        TextView player_number;
        TextView player_name;
        ImageButton seeded_button;
    }
咿呀咿呀哟 2024-12-28 11:36:27

由于您只有一个 ImageView 和一个 TextView,因此您可以将 ImageView 更改为 ImageButton。然后,您可以在 ImageButton 上添加一个侦听器,当用户单击图像时将调用该侦听器。如果他单击该项目内的任何其他位置(包括 TextView),则会调用 onItemclicklistener。
我认为这要简单得多。

Since you have only an ImageView and a TextView, you can change your ImageView to ImageButton. You can then add a listener on the ImageButton that will be called if the user clicks on the image. If he clicks anywhere else within the item (including the TextView), the onItemclicklistener will be called.
This is much simpler i think.

蓝戈者 2024-12-28 11:36:27

我想出了一个通用且可重用的解决方案。我没有扩展具体的列表适配器并修改 getView() 方法,而是创建了一个实现 ListAdapter 接口的新类,该类盲目地将几乎所有内容转发到另一个 ListAdapter< /code> 除了getView()。它看起来像这样:

public class SubClickableListAdapter implements ListAdapter {

    public static interface OnSubItemClickListener {
        public void onSubItemClick(View subView, int position);
    }
    private ListAdapter other;

    private SparseArray<OnSubItemClickListener> onClickListeners;

    public SubClickableListAdapter(ListAdapter other) {
        this.other = other;
        onClickListeners = new SparseArray<OnSubItemClickListener>();
    }

    public void setOnClickListener(int id, OnSubItemClickListener listener) {
        onClickListeners.put(id, listener);
    }

    public void removeOnClickListener(int id) {
        onClickListeners.remove(id);
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        View view = other.getView(position, convertView, parent);
        for(int i = 0; i < onClickListeners.size(); i++) {
            View subView = view.findViewById(onClickListeners.keyAt(i));
            if (subView != null) {
                final OnSubItemClickListener listener = onClickListeners.valueAt(i);
                if (listener != null) {
                    subView.setOnClickListener(new OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            listener.onSubItemClick(v, position);
                        }
                    });
                }
            }
        }
        return view;
    }

    // other implemented methods
}

其他实现的方法如下所示:

@Override
public Object getItem(int position) {
    return other.getItem(position);
}

要使用它,只需实例化它并提供任何其他 ListAdapter (无论是 ArrayAdapter 还是 SimpleCursorAdapter 或其他任何内容)。然后为您想要侦听点击的每个视图调用 setOnClickListener(),在 id 参数中提供其 id,并在 listener 中提供您的侦听器代码>参数。要获取被单击的行的行 ID,请调用 ListView 的 getItemIdAtPosition(position) 方法(您必须通过其他方式获取该方法,因为它没有作为回调的参数给出,但是在大多数情况下这不应该是一个大问题)。

该解决方案的优点是它可以与任何ListAdapter一起使用。因此,如果您的应用程序有多个 ListView,每个 ListView 使用不同的基础视图,甚至不同的适配器,则不必为每个列表创建一个新的适配器类。

此问题与所有其他解决方案相同:如果您单击已注册的视图,则不会调用 ListViewOnItemClick()听众.对于未注册侦听器的视图,将调用此回调。因此,例如,您的列表项有一个活动,其中包含两个文本字段和一个按钮,并且您为该按钮注册了一个侦听器,然后单击该按钮将不会调用 OnItemClick()ListView 的 >,而是您的回调。单击其他任何位置都会调用 OnItemClick()

I came up with a generic and reusable solution. Instead of extending a concrete list adapter and modifying the getView() method, I created a new class implementing the ListAdapter interface that blindly forwards almost everything to another ListAdapter except getView(). It looks like this:

public class SubClickableListAdapter implements ListAdapter {

    public static interface OnSubItemClickListener {
        public void onSubItemClick(View subView, int position);
    }
    private ListAdapter other;

    private SparseArray<OnSubItemClickListener> onClickListeners;

    public SubClickableListAdapter(ListAdapter other) {
        this.other = other;
        onClickListeners = new SparseArray<OnSubItemClickListener>();
    }

    public void setOnClickListener(int id, OnSubItemClickListener listener) {
        onClickListeners.put(id, listener);
    }

    public void removeOnClickListener(int id) {
        onClickListeners.remove(id);
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        View view = other.getView(position, convertView, parent);
        for(int i = 0; i < onClickListeners.size(); i++) {
            View subView = view.findViewById(onClickListeners.keyAt(i));
            if (subView != null) {
                final OnSubItemClickListener listener = onClickListeners.valueAt(i);
                if (listener != null) {
                    subView.setOnClickListener(new OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            listener.onSubItemClick(v, position);
                        }
                    });
                }
            }
        }
        return view;
    }

    // other implemented methods
}

The other implemented methods simply look like the following one:

@Override
public Object getItem(int position) {
    return other.getItem(position);
}

To use it, simply instantiate it supplying any other ListAdapter (be it ArrayAdapter or SimpleCursorAdapter or anything else). Then call setOnClickListener() for each view where you want to listen on the clicks, giving its id in the id parameter, and your listener in the listener parameter. To get the row id for the row which was clicked, call the getItemIdAtPosition(position) method of your ListView (which you have to get some other way, because it's not given as parameter to your callback, but that shouldn't be a big problem in most cases).

The advantage of this solution is that it can be used with any ListAdapter. So if your application has several ListViews, each using different underlying views, or even different adapters, you don't have to create a new adapter class for each one.

The problem with this is the same as with all the other solutions: the OnItemClick() of the ListView won't be called if you click on a view that you registered a listener for. For views that you didn't register a listener, this callback will be called though. So, for example, you have an activity for your list item that contains two text fields and a button, and you register a listener for the button, then clicking on the button won't call the OnItemClick() of the ListView, but your callback instead. Clicking on anywhere else calls OnItemClick().

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文