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.