Android: элементы ListView с несколькими нажимаемыми кнопками


182

У меня есть, 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 (который является элементом в списке, по которому произошло нажатие кнопки)

Обновление: я нашел решение, как описано в моем ответе ниже. Теперь я могу нажать / нажать кнопку через сенсорный экран. Однако я не могу вручную выбрать его с помощью трекбола. Он всегда выбирает весь элемент списка и оттуда сразу переходит к следующему элементу списка, игнорируя кнопки, хотя я установил .setFocusable(true)и setClickable(true)для кнопок в getView().

Я также добавил этот код в свой адаптер списка:

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

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

Это приводит к тому, что ни один элемент списка больше не может быть выбран вообще. Но это не помогло сделать вложенные кнопки выбираемыми.

Есть идеи?


Они все еще нужны?
— Стюарт Аксон

Если вы посмотрите на код BaseAdapter, вы увидите, что areAllItemsEnabled () и isEnabled () просто жестко закодированы в true, что делает их простыми заполнителями без какой-либо логики.
— Артем Руссаковский

Что делать, если я хочу использовать SimpleCursorAdapter? Нужно ли делать собственный адаптер, расширяющий simplecursoradapter?
— oratis

Ответы:


150

Решение этой проблемы на самом деле проще, чем я думал. Вы можете просто добавить в getView()методе своего пользовательского адаптера setOnClickListener () для кнопок, которые вы используете.

Любые данные, связанные с кнопкой, должны быть добавлены myButton.setTag()в getView()и могут быть доступны в onClickListener черезview.getTag()

Я разместил подробное решение в своем блоге в качестве учебного пособия.


2
Исправлена. Спасибо за сообщение :-)
— znq

Насколько точно вы получили curItem.url из запроса, не могли бы вы быть более конкретным?
— oratis

1
Спасибо znq, это было очень полезно для меня ... Экономия времени.
— Nandagopal T

Посмотрите этот доклад в 11:39. Отличный пример: youtu.be/wDBM6wVEO70?t=11m39s. Затем делайте то, что говорит @znq ... setTag (), когда convertView == null, и выполняйте getTag () в onClick (). метод кнопки onClickListener (). Спасибо!
— Shehaaz

12
было бы здорово иметь ответы в самой SO и не указывать на какую-то другую страницу. Ссылка на блог мертва!
— GSB

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

1
Как я могу получить должность, если я использую адаптер в отдельном классе. Я просто использовал позицию внутри OnClickListener, что-то вроде like_c.get (position). Предлагается сделать позицию финальной в методе getView адаптера. Если я сделаю это изменение, то позиция, которую я получу, будет одинаковой для всех элементов в lisview. Не могли бы вы подсказать мне, как решить эту проблему?
— Маникандан

Это потому, что когда вы используете параметр position метода getView, он дает вам позицию элемента списка, который был создан последним, но не тот, который вы щелкнули
— Archie.bpgc

@Manikandan: Если вы используете метод getView () адаптера, то вам уже передана позиция в параметрах метода. В этом случае вы можете использовать что-то вроде setTag () для хранения информации о местоположении.
— greg7gkb

1
Я использую ваш код в своем приложении, но событие click вызывается после задержки или при попытке прокрутки списка. Есть идеи, почему это происходит?
— Абдулла Умер

22

Для будущих читателей:

Чтобы вручную выбрать кнопки с помощью трекбола:

myListView.setItemsCanFocus(true);

И чтобы отключить фокус на весь список элементов:

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

У меня это работает нормально, я могу нажимать на кнопки с сенсорным экраном, а также позволяет фокусировать клик с помощью клавиатуры


2
Только этот помог мне! Большое тебе спасибо!
— Онур

Что касается ExpandableListView, у меня есть несколько представлений в элементе, я просто хочу, чтобы представления в дочернем элементе были активными, спасибо.
— Twlkyao

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

}

интересное решение
— шерпя

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

Разве платформенное решение для этой реализации не использует контекстное меню, которое отображается при долгом нажатии?

Знает ли автор вопроса о контекстных меню? Расположение кнопок в представлении списка влияет на производительность, загромождает ваш интерфейс и нарушает рекомендуемый дизайн интерфейса для платформы.

С другой стороны; контекстные меню - по природе отсутствия пассивного представления - не очевидны для конечного пользователя. Рассматриваете документирование поведения?

Это руководство должно дать вам хорошее начало.

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.