Просмотр списка с настраиваемым адаптером, содержащим флажки

У меня есть ListView, который использует настраиваемый адаптер, как показано:

private class CBAdapter extends BaseAdapter implements OnCheckedChangeListener{

    Context context;
    public String[] englishNames;
    LayoutInflater inflater;
    CheckBox[] checkBoxArray;
    LinearLayout[] viewArray;
    private boolean[] checked;

    public CBAdapter(Context con, String[] engNames){
        context=con;
        englishNames=engNames;
        inflater=(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        checked= new boolean[englishNames.length];
        for(int i=0; i<checked.length; i++){
            checked[i]=false;
            //Toast.makeText(con, checked.toString(),Toast.LENGTH_SHORT).show();
        }
        checkBoxArray = new CheckBox[checked.length];
        viewArray = new LinearLayout[checked.length];
    }

    public int getCount() {
        return englishNames.length;
    }

    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return null;
    }

    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return 0;
    }

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

        if(viewArray[position] == null){

            viewArray[position]=(LinearLayout)inflater.inflate(R.layout.record_view_start,null);

            TextView tv=(TextView)viewArray[position].findViewById(R.id.engName);
            tv.setText(englishNames[position]);

            checkBoxArray[position]=(CheckBox)viewArray[position].findViewById(R.id.checkBox1);
        }

        checkBoxArray[position].setChecked(checked[position]);
        checkBoxArray[position].setOnCheckedChangeListener(this);
        return viewArray[position];
    }


    public void checkAll(boolean areChecked){
        for(int i=0; i<checked.length; i++){
            checked[i]=areChecked;
            if(checkBoxArray[i] != null)
                checkBoxArray[i].setChecked(areChecked);
        }
        notifyDataSetChanged();
    }

    public void onCheckedChanged(CompoundButton cb, boolean isChecked) {
        for(int i=0; i<checked.length; i++){
            if(cb == checkBoxArray[i])
                checked[i]=isChecked;
        }




    }
    public boolean itemIsChecked(int i){
        return checked[i];
    }

}

Макеты довольно простые, поэтому я не буду публиковать их, если кто-то не сочтет их актуальными.

Проблема в том, что некоторые флажки не отвечают. Кажется, это те, которые видны при первом отображении макета. Все, что вам нужно прокрутить вниз, чтобы работать должным образом.

Любые указатели приветствуются.


person s1ni5t3r    schedule 28.09.2012    source источник


Ответы (3)


Ваш код из ответа работает, но неэффективен (вы действительно можете это увидеть, просто прокрутите ListView и проверьте Logcat, чтобы увидеть, как сборщик мусора выполняет свою работу). Усовершенствованный getView метод, который будет повторно использовать представления, приведен ниже:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
     LinearLayout view = (LinearLayout) convertView;
     if (view == null) {
          view = (LinearLayout) inflater.inflate(R.layout.record_view_start, parent, false);
     }
     TextView tv = (TextView) view.findViewById(R.id.engName);
     tv.setText(getItem(position));
     CheckBox cBox = (CheckBox) view.findViewById(R.id.checkBox1);
     cBox.setTag(Integer.valueOf(position)); // set the tag so we can identify the correct row in the listener
     cBox.setChecked(mChecked[position]); // set the status as we stored it        
     cBox.setOnCheckedChangeListener(mListener); // set the listener    
     return view;
}

OnCheckedChangeListener mListener = new OnCheckedChangeListener() {

     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {   
         mChecked[(Integer)buttonView.getTag()] = isChecked; // get the tag so we know the row and store the status 
     }
};

Что касается вашего кода из вашего вопроса, сначала я подумал, что это неправильно из-за того, как вы настраиваете строки, но я не понимаю, почему у адаптера будет такое поведение, когда вы отключили представление строки из списка. Кроме того, я даже протестировал код, и он неплохо работает в отношении CheckBoxes (но с очень плохой обработкой памяти). Может ты еще что-то делаешь, из-за чего адаптер не работает?

person user    schedule 31.10.2012
comment
Как это сделать для EcxpandableListView с дочерними флажками? - person RicNjesh; 22.04.2014
comment
@RicNjesh Вы захотите использовать метод getChildView(), который вызывается всякий раз, когда требуется дочерняя строка. - person user; 22.04.2014
comment
что в этом случае mchecked и как это объявить? - person Muneem Habib; 21.11.2014
comment
@MuneemHabib Это логический массив, размер которого равен количеству элементов в ListView. Объявите это в конструкторе адаптера. - person user; 21.11.2014
comment
@Luksprog спасибо, сэр, это мне очень помогло. я решил свою проблему - person Muneem Habib; 21.11.2014
comment
еще одна вещь, почему вы каждый раз создаете новую строку? почему вы не используете режим преобразования? Я использовал преобразование, а затем создал новые только флажки, и он работает f9 - person Muneem Habib; 21.11.2014
comment
@MuneemHabib Как именно я каждый раз создаю новую строку? Есть проверка view == null и только потом накачиваю новую строку. - person user; 21.11.2014
comment
Я говорю о текстовом представлении, которое создавалось каждый раз - person Muneem Habib; 21.11.2014
comment
@MuneemHabib Я нигде не создаю TextView, я ищу его в виде строки с помощью findViewById (). - person user; 21.11.2014

Позвольте мне сначала сказать, что вы отказались от одного из основных преимуществ использования адаптера: многоразовых представлений. Жесткая ссылка на каждый созданный View может привести к превышению лимита памяти. Вы должны повторно использовать convertView, если оно не равно нулю, и создавать свое представление, когда convertView равно нулю. Есть много руководств, которые покажут вам, как это сделать.

Представления, используемые в адаптере, обычно имеют OnClickListener, прикрепленные к ним родительским View, так что вы можете установить OnItemClickListener на ListView. Это заменит любые сенсорные слушатели на отдельных представлениях. Попробуйте установить android:clickable="true" на CheckBox в XML.

person Jason Robinson    schedule 28.09.2012
comment
Спасибо за внимание к convertView. Мне было интересно, для чего этот параметр. Думаю, у меня есть решение исходной проблемы, но я все еще тестирую. Я скоро снова отправлю сообщение. - person s1ni5t3r; 29.09.2012

Возможно, это не самое элегантное или эффективное решение, но оно работает в моей ситуации. По какой-то причине попытка повторно использовать представления либо из массива представлений, либо с помощью convertView заставляет все шататься, и CheckBox не реагирует.

Единственное, что сработало, - это создание нового View каждый раз, когда вызывается getView ().

    public View getView(final int position, View convertView, ViewGroup parent) {
        LinearLayout view;
        view=(LinearLayout)inflater.inflate(R.layout.record_view_start,null);


        TextView tv=(TextView)view.findViewById(R.id.engName);
        tv.setText(englishNames[position]);

        CheckBox cBox=(CheckBox)view.findViewById(R.id.checkBox1);
        cBox.setChecked(checked[position]);
        cBox.setOnCheckedChangeListener(new OnCheckedChangeListener(){

            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                checked[position]=isChecked;
            }
        });
        return view;
    }

Поиску этого решения также мешал тот факт, что я вызывал отдельно определенный onCheckedChangedListener, который затем определял, какой CheckBox по идентификатору, вместо того, чтобы иметь нового слушателя для каждого CheckBox.

Пока что я не пометил это как правильный ответ, так как надеюсь, что другие могут внести свой вклад в отношении довольно расточительного восстановления представления каждый раз.

person s1ni5t3r    schedule 05.10.2012
comment
Есть много вопросов о stackoverflow относительно ListView с настраиваемым адаптером, который использует строку с CheckBox, вам следует подробнее изучить эти решения. Ваш код работает, но очень неэффективен, каждый раз, когда ListView прокручивается, вы будете создавать новые представления. - person user; 05.10.2012
comment
Я полностью согласен с Luksprog, но после многих попыток, используя решения, которые работают для других, я не могу найти решение, которое работает иначе, чем это. Надеюсь, сборка мусора не отразится на потерянных просмотрах и не вызовет проблем с памятью, а старые телефоны не будут слишком сильно тормозить. В моем приложении в большинстве ситуаций в списке не будет слишком много записей, поэтому прокрутку следует свести к минимуму. - person s1ni5t3r; 19.10.2012
comment
Причина, по которой ваш код работает сейчас, заключается в том, что вы каждый раз раздуваете строку и избегаете OnCheckedChangeListener нарушения статуса CheckBox. Никогда не следует игнорировать повторное использование представлений в методе getView. Вот ваш улучшенный getView метод, который должен делать то, что вы хотите, но также будет повторно использовать представления. gist.github.com/3917222 - person user; 19.10.2012
comment
Превосходно. Это работает именно так, как задумано. Ваш код упал сразу, и из проверенного массива была удалена только буква «m». (Это мой первый проект на java, и я не знал об условных обозначениях.) Теперь у меня есть две просьбы об одолжении. 1. Можете ли вы репостить это как ответ, чтобы я мог установить его как принятый, и 2. Можете ли вы объяснить, почему это работает, когда мой код в исходном сообщении не работал. У меня такое чувство, что ответ может пролить свет на некоторые другие концепции, с которыми я борюсь. - person s1ni5t3r; 31.10.2012