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 () просто жорстко закодовані, що робить їх простими заповнювачами без будь-якої логіки.
— Артем Русаковський

Що робити, якщо я хочу використовувати SimpleCursorAdapter? Чи потрібно зробити спеціальний адаптер, який розширює simplecursoradapter?
— oratis

Відповіді:


150

Рішення цього насправді простіше, ніж я думав. Ви можете просто додати в getView()метод вашого користувальницького адаптера setOnClickListener () для використовуваних кнопок.

Будь-які дані, пов’язані з кнопкою, повинні бути додані myButton.setTag()в getView()і до них можна отримати доступ у onClickListener черезview.getTag()

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


2
Виправлено. Дякуємо за повідомлення :-)
— znq

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

1
Спасибі znq, Це було дуже корисно для мене ... Заощадив купу часу.
— Нандагопаль T

Подивіться цю розмову о 11:39, є чудовий приклад: youtu.be/wDBM6wVEO70?t=11m39s Потім зробіть те, що говорить @znq ... setTag (), коли convertView == null і зробіть getTag () в onClick () метод кнопки onClickListener (). Дякую!
— Shehaaz

12
було б чудово мати відповіді в самому ТА, а не вказувати на якусь іншу сторінку. Посилання в блозі мертве!
— 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 адаптера. Якщо я змушу це змінити положення, яке я отримую, є однаковим для всіх елементів у візуальному перегляді. Не могли б ви запропонувати мені, як це вирішити.
— Манікандан

Це тому, що коли ви використовуєте параметр позиції методу getView, він дає вам позицію елемента списку, який останнім часом створений, але не той, який ви натиснули
— Archie.bpgc

@Manikandan: Якщо ви використовуєте метод getView () адаптера, ви вже маєте позицію, передану вам у парамах методу. У цьому випадку ви можете використовувати щось на зразок setTag () для зберігання інформації про позицію.
— greg7gkb

1
Я використовую ваш код у своєму додатку, але подія натискання викликається після затримки або коли я намагаюся прокрутити перегляд списку. Будь-які ідеї, чому це відбувається?
— Абдулла Умер

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

}

цікаве рішення
— sherpya

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.