Android:具有多个可点击按钮的ListView元素


182

ListView在列表中的每个元素都包含一个TextView和两个不同的Button的地方。像这样:

ListView
--------------------
[Text]
[Button 1][Button 2]
--------------------
[Text]
[Button 1][Button 2]
--------------------
... (and so on) ...

使用此代码,我可以OnItemClickListener为整个商品创建一个:

listView.setOnItemClickListener(new OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> list, View view, int position, long id) {
        Log.i(TAG, "onListItemClick: " + position);

        }

    }
});

但是,我不希望整个项目都可单击,而只希望每个列表元素具有两个按钮。

所以我的问题是,如何使用以下参数为这两个按钮实现onClickListener:

  • int button (已单击元素的哪个按钮)
  • int position (这是列表中单击按钮的元素)

更新:我找到了下面我的答案中所述的解决方案。现在,我可以通过触摸屏单击/点击按钮。但是,我无法通过轨迹球手动选择它。它总是选择整个列表项,并从那里直接进入下一个表项忽略的按钮,即使我设置.setFocusable(true)setClickable(true)在按钮getView()

我还将此代码添加到了自定义列表适配器中:

@Override
public boolean  areAllItemsEnabled() {
    return false;           
}

@Override
public boolean isEnabled(int position) {
        return false;
}

这导致根本没有列表项可供选择。但这并没有使嵌套按钮变为可选。

有人知道吗?


这些还需要吗?
斯图尔特·阿克森

如果查看BaseAdapter代码,您会看到areAllItemsEnabled()和isEnabled()只是硬编码为true,从而使它们成为简单的占位符而没有任何逻辑。
Artem Russakovskii 2011年

如果我想使用SimpleCursorAdapter怎么办?我必须使自定义适配器扩展simplecursoradapter吗?
oratis 2011年

Answers:


150

解决这个问题实际上比我想象的要容易。您只需在自定义适配器的getView()方法中为要使用的按钮添加setOnClickListener()即可。

与该按钮相关联的任何数据具有被添加myButton.setTag()getView()并可以在onClickListener通过以下方式访问view.getTag()

我在博客上发布了详细的解决方案作为教程。


2
固定。感谢您的举报:-)
znq 2011年

您是如何从查询中获得curItem.url的,您是否更具体?
oratis 2011年

1
谢谢znq,这对我来说非常有用...节省了很多时间。
Nandagopal T 2012年

观看11:39的演讲,有一个很好的例子:youtu.be/wDBM6wVEO70 ?t=11m39s然后执行当convertView == null时@znq所说的... setTag()并在onClick()中执行getTag()按钮的onClickListener()的方法。谢谢!
Shehaaz 2013年

12
最好在SO本身中提供答案,而不指向其他页面。博客链接已死!
gsb 2015年

63

这有点像@znq的答案...

在许多情况下,您想知道被单击项的行位置,并且想知道在该行中哪个视图被点击。在平板电脑用户界面中,这将变得更加重要。

您可以使用以下定制适配器执行此操作:

private static class CustomCursorAdapter extends CursorAdapter {

    protected ListView mListView;

    protected static class RowViewHolder {
        public TextView mTitle;
        public TextView mText;
    }

    public CustomCursorAdapter(Activity activity) {
        super();
        mListView = activity.getListView();
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        // do what you need to do
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        View view = View.inflate(context, R.layout.row_layout, null);

        RowViewHolder holder = new RowViewHolder();
        holder.mTitle = (TextView) view.findViewById(R.id.Title);
        holder.mText = (TextView) view.findViewById(R.id.Text);

        holder.mTitle.setOnClickListener(mOnTitleClickListener);
        holder.mText.setOnClickListener(mOnTextClickListener);

        view.setTag(holder);

        return view;
    }

    private OnClickListener mOnTitleClickListener = new OnClickListener() {
        @Override
        public void onClick(View v) {
            final int position = mListView.getPositionForView((View) v.getParent());
            Log.v(TAG, "Title clicked, row %d", position);
        }
    };

    private OnClickListener mOnTextClickListener = new OnClickListener() {
        @Override
        public void onClick(View v) {
            final int position = mListView.getPositionForView((View) v.getParent());
            Log.v(TAG, "Text clicked, row %d", position);
        }
    };
}

6
或者您也可以使用类似ListView的东西mListView =(ListView)v.getParent()。getParent();
max4ever 2012年

1
如果在单独的类中使用适配器,如何获得位置。我只是使用了OnClickListener内部的位置,就像like_c.get(position)一样。建议在适配器的getView方法中确定位置。如果我进行更改,则lisview中所有项目的位置都相同。你能建议我如何解决这个问题。
Manikandan 2012年

那是因为当您使用getView方法的position参数时,它将为您提供列表项的最新位置,而该列表项是最近创建的,但不是您单击的项的位置
Archie.bpgc 2012年

@Manikandan:如果您正在使用适配器的getView()方法,那么您已经在方法参数中将位置传递给了您。在这种情况下,您可以使用setTag()之类的东西来存储位置信息。
greg7gkb 2012年

1
我在我的应用中使用您的代码,但是在延迟或尝试滚动列表视图后会调用click事件。任何想法为什么会这样?
阿卜杜拉·乌默尔2014年

22

对于未来的读者:

要使用轨迹球手动选择按钮,请使用:

myListView.setItemsCanFocus(true);

并禁用对整个列表项的关注:

myListView.setFocusable(false);
myListView.setFocusableInTouchMode(false);
myListView.setClickable(false);

它对我来说很好用,我可以单击带触摸屏的按钮,也可以使用键盘使点击集中


2
只有这个帮助了我!非常感谢!
Onur

那么ExpandableListView呢,我在该项目中有多个视图,我只希望子级中的视图可点击,谢谢。
twlkyao 2014年

12

我没有比上述用户更多的经验,但是我遇到了同样的问题,因此我通过以下解决方案解决了这个问题

<Button
        android:id="@+id/btnRemove"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@+id/btnEdit"
        android:layout_weight="1"
        android:background="@drawable/btn"
        android:text="@string/remove" 
        android:onClick="btnRemoveClick"
        />

btnRemoveClick点击事件

public void btnRemoveClick(View v)
{
    final int position = listviewItem.getPositionForView((View) v.getParent()); 
    listItem.remove(position);
    ItemAdapter.notifyDataSetChanged();

}

有趣的解决方案
sherpya 2015年

9

可能您已经找到了解决方法,但是您可以致电

ListView.setItemsCanFocus(true)

现在您的按钮将吸引焦点


5

我不确定是不是最好的方法,但是可以正常工作,并且所有代码都保留在您的ArrayAdapter中。

package br.com.fontolan.pessoas.arrayadapter;

import java.util.List;

import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import br.com.fontolan.pessoas.R;
import br.com.fontolan.pessoas.model.Telefone;

public class TelefoneArrayAdapter extends ArrayAdapter<Telefone> {

private TelefoneArrayAdapter telefoneArrayAdapter = null;
private Context context;
private EditText tipoEditText = null;
private EditText telefoneEditText = null;
private ImageView deleteImageView = null;

public TelefoneArrayAdapter(Context context, List<Telefone> values) {
    super(context, R.layout.telefone_form, values);
    this.telefoneArrayAdapter = this;
    this.context = context;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    View view = inflater.inflate(R.layout.telefone_form, parent, false);

    tipoEditText = (EditText) view.findViewById(R.id.telefone_form_tipo);
    telefoneEditText = (EditText) view.findViewById(R.id.telefone_form_telefone);
    deleteImageView = (ImageView) view.findViewById(R.id.telefone_form_delete_image);

    final int i = position;
    final Telefone telefone = this.getItem(position);
    tipoEditText.setText(telefone.getTipo());
    telefoneEditText.setText(telefone.getTelefone());

    TextWatcher tipoTextWatcher = new TextWatcher() {
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }

        @Override
        public void afterTextChanged(Editable s) {
            telefoneArrayAdapter.getItem(i).setTipo(s.toString());
            telefoneArrayAdapter.getItem(i).setIsDirty(true);
        }
    };

    TextWatcher telefoneTextWatcher = new TextWatcher() {
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }

        @Override
        public void afterTextChanged(Editable s) {
            telefoneArrayAdapter.getItem(i).setTelefone(s.toString());
            telefoneArrayAdapter.getItem(i).setIsDirty(true);
        }
    };

    tipoEditText.addTextChangedListener(tipoTextWatcher);
    telefoneEditText.addTextChangedListener(telefoneTextWatcher);

    deleteImageView.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            telefoneArrayAdapter.remove(telefone);
        }
    });

    return view;
}

}

3

我知道已经晚了,但这可能会有所帮助,这是一个示例,说明如何为不同的点击操作编写自定义适配器类

 public class CustomAdapter extends BaseAdapter {

    TextView title;
  Button button1,button2;

    public long getItemId(int position) {
        return position;
    }

    public int getCount() {
        return mAlBasicItemsnav.size();  // size of your list array
    }

    public Object getItem(int position) {
        return position;
    }

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

        if (convertView == null) {
            convertView = getLayoutInflater().inflate(R.layout.listnavsub_layout, null, false); // use sublayout which you want to inflate in your each list item
        }

        title = (TextView) convertView.findViewById(R.id.textViewnav); // see you have to find id by using convertView.findViewById 
        title.setText(mAlBasicItemsnav.get(position));
      button1=(Button) convertView.findViewById(R.id.button1);
      button1.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            //your click action 

           // if you have different click action at different positions then
            if(position==0)
              {
                       //click action of 1st list item on button click
        }
           if(position==1)
              {
                       //click action of 2st list item on button click
        }
    });

 // similarly for button 2

   button2=(Button) convertView.findViewById(R.id.button2);
      button2.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            //your click action 

    });



        return convertView;
    }
}

-4

该实现的平台解决方案不是使用长按显示的上下文菜单吗?

问题作者是否知道上下文菜单?在列表视图中堆叠按钮会影响性能,会使您的UI混乱并违反平台推荐的UI设计。

另一方面; 上下文菜单(本质上没有被动表示)对于最终用户而言并不明显。考虑记录行为?

本指南应为您提供一个良好的开端。

http://www.mikeplate.com/2010/01/21/show-a-context-menu-for-long-clicks-in-an-android-listview/

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.