notifyDatasetChanged не работает внутри метода getView() для пользовательского адаптера в Android

В основном я пытаюсь скрыть и показать текст в строке списка, когда я нажимаю кнопку в строке списка. Я добавил onClick() для кнопки внутри метода getView(), а затем вызвал метод notifyDataSetChanged(). Но это не работает. Отсутствие изменений в видимости текста. Вот мой собственный код адаптера:

  public class ListAdapter extends BaseAdapter {
    private Context context;
    private List<String> mListQuestion = null;
    private List<String> mListAnswer = null;
    ViewHolder holder = null;
    boolean flag = false;

    public ListAdapter(Context context, List<String> question, List<String> answer ) {
        this.mListQuestion = question;
        this.mListAnswer = answer;
        this.context = context;
    }

    @Override
    public Object getItem(int position)
    {
        return mListQuestion.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public int getCount() {
        return mListQuestion.size();
    }

    @Override
    @SuppressWarnings("deprecation")
    public View getView(int position, View convertView, ViewGroup parent) {


        if (convertView == null)
        {
            LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = vi.inflate(R.layout.list_faq_item, null);

            holder = new ViewHolder();
            holder.tvQuestion = (TextView) convertView.findViewById(R.id.text);
            holder.tvAns = (TextView) convertView.findViewById(R.id.anstext);
            holder.ivArrow = (Button)convertView.findViewById(R.id.arrow_expand);
            convertView.setTag(holder);
        }
        else
        {
            holder = (ViewHolder) convertView.getTag();
        }

        holder.tvQuestion.setText(mListQuestion.get(position));
        holder.tvAns.setText(mListAnswer.get(position));
        holder.ivArrow.setOnClickListener(new View.OnClickListener()
        {
                @Override
                public void onClick(View v)
                {
                    if (flag == false)
                    {
                        Logger.d("arrow clicked when flag is false");
                        holder.tvAns.setVisibility(View.VISIBLE);
                        holder.ivArrow.setBackgroundResource(R.drawable.up_arrow);
                        flag = true;
                    }
                    else if (flag == true)
                    {
                        Logger.d("arrow clicked when flag is true");
                        holder.tvAns.setVisibility(View.GONE);
                        holder.ivArrow.setBackgroundResource(R.drawable.down_arrow);
                        flag = false;
                    }
                    notifyDataSetChanged();
                }
        });

        return convertView;
    }


    static class ViewHolder {
        TextView tvQuestion;
        TextView tvAns;
        Button ivArrow;
    }

}

Может кто-нибудь, пожалуйста, скажите, что я делаю неправильно здесь. Заранее спасибо.

-Ариндам.


person Arindam Mukherjee    schedule 06.12.2015    source источник
comment
нет смысла звонить сюда notifyDataSetChanged(); для вашей цели   -  person M D    schedule 06.12.2015
comment
Не могли бы вы сказать, что еще я могу позвонить, чтобы обновить полное представление?   -  person Arindam Mukherjee    schedule 06.12.2015
comment
Я все еще не понимаю, в чем здесь ваше требование?   -  person M D    schedule 06.12.2015
comment
Все просто: вы не вносите никаких изменений в dataSet. notifyDataSetChanged() будет работать, только если вы вносите изменения в dataSets. Что в вашем случае mListQuestion и mListAnswer   -  person Paresh P.    schedule 06.12.2015
comment
В основном я получаю некоторые вопросы и ответы от сервера, которые я показываю в списке. По умолчанию ответы невидимы. Рядом с каждым вопросом есть маленькая кнопка расширения. Как только пользователь нажимает кнопку «Развернуть», я пытаюсь показать ответ. Опять же, при следующем нажатии ответ будет скрыт.   -  person Arindam Mukherjee    schedule 06.12.2015
comment
Вы пытались запустить приложение в режиме отладки и добавить точку останова, если условие в OnclickListener , оно входит?   -  person HourGlass    schedule 06.12.2015
comment
@HourGlass Я уже проверил это, добавив журналы. управление входит внутрь, когда флаг является истинным или ложным в зависимости от нажатия кнопки расширения.   -  person Arindam Mukherjee    schedule 06.12.2015


Ответы (4)


public class ListAdapter extends BaseAdapter {
    private Context context;
    private List<String> mListQuestion = null;
    private List<String> mListAnswer = null;
    ViewHolder holder = null;
    private List<Boolean> textViewVisibileState; 

    public ListAdapter(Context context, List<String> question, List<String> answer ) {
        this.mListQuestion = question;
        this.mListAnswer = answer;
        this.context = context;
        this.textViewVisibileState=new ArrayList<>(Arrays.asList(new Boolean[getCount()]));
          Collections.fill(this.textViewVisibileState,false);
    } 

    @Override 
    public Object getItem(int position)
    { 
        return mListQuestion.get(position);
    } 

    @Override 
    public long getItemId(int position) {
        return position;
    } 

    @Override 
    public int getCount() { 
        return mListQuestion.size();
    } 

    @Override 
    @SuppressWarnings("deprecation") 
    public View getView(int position, View convertView, ViewGroup parent) {


        if (convertView == null)
        { 
            LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = vi.inflate(R.layout.list_faq_item, null);

            holder = new ViewHolder();
            holder.tvQuestion = (TextView) convertView.findViewById(R.id.text);
            holder.tvAns = (TextView) convertView.findViewById(R.id.anstext);
            holder.ivArrow = (Button)convertView.findViewById(R.id.arrow_expand);
            convertView.setTag(holder);
        } 
        else 
        { 
            holder = (ViewHolder) convertView.getTag();
        } 

        holder.tvQuestion.setText(mListQuestion.get(position));
        holder.tvAns.setText(mListAnswer.get(position));
        if(textViewVisibileState.get(position))
{
            holder.tvAns.setVisibility(View.GONE);
                        holder.ivArrow.setBackgroundResource(R.drawable.down_arrow);

}
else
{
    Logger.d("arrow clicked when flag is false"); 
                        holder.tvAns.setVisibility(View.VISIBLE);
                        holder.ivArrow.setBackgroundResource(R.drawable.up_arrow);

}
        holder.ivArrow.setOnClickListener(new View.OnClickListener()
        { 
                @Override 
                public void onClick(View v)
                { 
                    if (textViewVisibileState.get(position))
                    { 
                        textViewVisibileState.set(position,false);
   } 
                    else
                    { 

                        textViewVisibileState.set(position,true);

 } 
                    notifyDataSetChanged();
                } 
        }); 

        return convertView;
    } 


    static class ViewHolder { 
        TextView tvQuestion;
        TextView tvAns;
        Button ivArrow;
    } 

} 

Это сработает.

person HourGlass    schedule 06.12.2015

Переменная flag не зависит от контекста объекта holder. Так что flag всегда = false в вашем случае. Как насчет setVisibility(View.GONE) изначально? И затем setVisibility(View.VISIBLE) ТОЛЬКО при нажатии на ivArrow.

person The Original Android    schedule 06.12.2015
comment
Я проверил, что falg не всегда является ложным. Когда я нажимаю кнопку «Развернуть», флаг становится истинным, и он входит в условие. - person Arindam Mukherjee; 06.12.2015
comment
@ArindamMukherjee, извините, теперь я вижу, что вы меняете значение флага. Но все же, я думаю, что это не правильная логика. Я предлагаю setVisibility(View.VISIBLE), когда пользователь щелкает значок стрелки, как я сказал в своем посте. - person The Original Android; 06.12.2015
comment
Я применил этот метод перед этим ответом и работал нормально - person Noor Hossain; 05.12.2020

Вызов notifyDataSetChanged() приводит к тому, что ListView все перестраивает. Он удалит свои дочерние представления, вызовет getView() для всех видимых элементов, и, таким образом, вы повторно привяжете все данные для этих элементов.

Но ваши данные на самом деле не изменились. Вы ничего не изменили в списке вопросов, поэтому повторное связывание данных бессмысленно. Вместо этого вы пытались что-то изменить в своем объекте ViewHolder, но нет гарантии, что convertView, которое вы получаете после notifyDataSetChanged(), относится к той же позиции, что и раньше, поэтому возможно, что был затронут какой-то другой элемент ( а может вообще никак?)

Попробуйте удалить вызов notifyDataSetChanged() из файла OnClickListener. Изменение видимости должно привести к изменению макета иерархии представлений, но пока вы не сообщили ListView, что данные изменились, он должен сохранить все свои текущие дочерние элементы.

person Karakuri    schedule 06.12.2015
comment
Я немного смущен после прочтения вашего ответа. Простите за это. Я понял, что notifyDataSetChanged() не имеет смысла, так как нет изменения данных. Я удалил его. Но все же это не работает. Я не понял эту часть: изменение видимости должно привести к изменению макета иерархии представлений, но пока вы не сообщили ListView, что данные изменились, он должен сохранить все свои текущие дочерние элементы. Не могли бы вы немного уточнить. - person Arindam Mukherjee; 06.12.2015
comment
@ArindamMukherjee Когда вид меняется с VISIBLE на GONE (или наоборот), он вызывает requestLayout(). Android необходимо измерить, разместить и нарисовать иерархию представлений, чтобы отобразить новое состояние. ListView — это ViewGroup; у него есть дети, он их измеряет, раскладывает и рисует. Если вы вызываете notifyDataSetChanged, LIstView удаляет свои дочерние элементы и создает новые (которые затем измеряются, размещаются и рисуются). Я предлагаю не вызывать это, чтобы ListView измерял/размещал/отрисовывал дочерние элементы, которые у него уже есть, в том числе тот, который изменил видимость. - person Karakuri; 06.12.2015
comment
@ArindamMukherjee Возможно, стоит попробовать использовать RecyclerView вместо ListView. ListView был разработан, чтобы полностью контролировать свои дочерние элементы, включая клики, поэтому вы должны предоставить OnItemClickListener. Делать определенные субрегионы этих детей кликабельными всегда было немного причудливо. RecyclerView использует противоположный подход: он в основном ничего не делает, кроме повторного использования, поэтому сами представления могут получать клики и выполнять более сложные действия. - person Karakuri; 06.12.2015
comment
Хорошее объяснение. Но дает ли listview какой-либо метод, чтобы просто аннулировать и показать его. Я уже удалил notifiyDatasetChanged(). Раньше я также работал с похожими вещами listview. Но не было возможности скрыть/показать некоторые элементы. Очень сложно снова вернуться к RecyclerView. - person Arindam Mukherjee; 06.12.2015
comment
@ArindamMukherjee Вы можете попробовать вызвать invalidate() в ListView, что должно заставить его только перерисовывать, но я не думаю, что это тоже будет полезно. Я по-прежнему рекомендую переключиться на RecyclerView, он не сильно отличается, если вы делаете относительно простые списки. - person Karakuri; 06.12.2015
comment
Итак, согласно вашей последней строке, как сказать listview? - person Noor Hossain; 05.12.2020

Создайте экземпляр адаптера, например Adapter myAdapter = new Adapter, установите для него значение listview или recyclerview, например listview.setAdapter(mydapter), и каждый раз, когда вы добавляете в него новые данные, вызывайте adapter.notifyDataSetChanged()

person life evader    schedule 06.12.2015
comment
перескакивает список в 0 позицию - person Noor Hossain; 05.12.2020