【问题标题】:Android: ListView elements with multiple clickable buttonsAndroid:具有多个可点击按钮的 ListView 元素
【发布时间】:2010-12-15 02:45:54
【问题描述】:

我有一个ListView,其中列表中的每个元素都包含一个 TextView 和两个不同的按钮。像这样的:

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(这是列表中发生按钮单击的元素)

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

我还将这段代码添加到我的自定义列表适配器中:

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

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

这会导致列表项不再可选择。但它没有帮助使嵌套按钮可选择。

谁有想法?

【问题讨论】:

  • 这些还需要吗?
  • 如果您查看 BaseAdapter 代码,您会发现 areAllItemsEnabled() 和 isEnabled() 只是硬编码为 true,使它们成为没有任何逻辑的简单占位符。
  • 如果我想使用 SimpleCursorAdapter 怎么办?我必须让自定义适配器扩展 simplecursoradapter 吗?

标签: android listview button


【解决方案1】:

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

与按钮相关的任何数据都必须在getView() 中添加myButton.setTag(),并且可以通过view.getTag() 在onClickListener 中访问

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

【讨论】:

  • 已修复。感谢您的报告 :-)
  • 你是如何从查询中得到 curItem.url 的,你能说得更具体点吗?
  • 谢谢znq,这对我很有用...节省了大量时间。
  • 在 11:39 观看这个演讲,有一个很好的例子:youtu.be/wDBM6wVEO70?t=11m39s 然后在 convertView==null 时执行@znq 所说的...setTag() 并在 onClick 中执行 getTag() () 按钮的 onClickListener() 方法。谢谢!
  • 如果在 SO 本身中有答案而不指向其他页面,那就太好了。博客链接已失效!
【解决方案2】:

这有点像@znq 的回答……

在很多情况下,您想知道被点击项目的行位置,并且您想知道该行中的哪个视图被点击了。这在平板电脑 UI 中将变得更加重要。

您可以使用以下自定义适配器来做到这一点:

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);
        }
    };
}

【讨论】:

  • 或者你也可以使用类似这样的 ListView mListView = (ListView) v.getParent().getParent();
  • 如果我在单独的类中使用适配器,如何获得位置。我只是使用了 OnClickListener 中的位置,类似于 like_c.get(position) 。它建议在适配器的 getView 方法中将位置设置为最终位置。如果我做出改变,我得到的位置对于 lisview 中的所有项目都是相同的。你能建议我如何解决这个问题吗?
  • 那是因为当您使用 getView 方法的 position 参数时,它会为您提供最近创建的列表项的位置,而不是您单击的项
  • @Manikandan:如果您使用适配器的 getView() 方法,那么您已经在方法参数中将位置传递给您。在这种情况下,您可以使用 setTag() 之类的东西来存储位置信息。
  • 我在我的应用程序中使用了您的代码,但点击事件在延迟后或当我尝试滚动列表视图时被调用。任何想法为什么会发生这种情况?
【解决方案3】:

对于未来的读者:

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

myListView.setItemsCanFocus(true);

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

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

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

【讨论】:

  • 只有这个帮助了我!非常感谢!
  • ExpandableListView 怎么样,我在项目中有多个视图,我只想让子视图中的视图可点击,谢谢。
【解决方案4】:

我没有上述用户的经验,但我遇到了同样的问题,我用下面的解决方案解决了这个问题

<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();

}

【讨论】:

    【解决方案5】:

    可能你已经找到了方法,但你可以打电话

    ListView.setItemsCanFocus(true)
    

    现在您的按钮将获得焦点

    【讨论】:

      【解决方案6】:

      我不确定是不是最好的方法,但可以正常工作,并且所有代码都保留在您的 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;
      }
      
      }
      

      【讨论】:

        【解决方案7】:

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

         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;
            }
        }
        

        【讨论】:

          【解决方案8】:

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

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

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

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

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-02-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-07-05
            • 2014-10-03
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多