Наблюдать за LiveData из текущего элемента ListView

В основном я хочу получить int (счет), сгенерированный из текущего ListViewItem, и назначить его обратно конкретному TextView в моем ListView. Я использую SharedViewModel с LiveData, но когда я наблюдаю, что ничего не происходит. Я использую компонент архитектуры Nav с одним действием. Буду рад, если кто-то поможет. Спасибо, вот код.

открытый класс ListFrag расширяет фрагмент {

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);

    model.getCurrentScore().observe(getViewLifecycleOwner(), new Observer<Integer>() {
        @Override
        public void onChanged(@Nullable Integer s) {

            for (int i = 0; i < myListView.getAdapter().getCount(); i++) {

                v =  myListView.getAdapter().getView(i,null, myListView);

                finalScore = v.findViewById(R.id.finalScoreView);

                    if (s != null) {
    itemAdapter = new ItemAdapter(getActivity(),items,bushido,description,s,finalScore);
                        myListView.setAdapter(itemAdapter);
                       finalScore.setText(String.valueOf(s));

                    }
      ((BaseAdapter)myListView.getAdapter()).notifyDataSetChanged();
            }
        }
    });
}

открытый класс SharedViewModel расширяет ViewModel {

private MutableLiveData<Integer> currentScore = new MutableLiveData<>();

public LiveData<Integer> getCurrentScore(){
    return currentScore;
}

public void setCurrentScore(Integer finito) {
    currentScore.setValue(finito);

}

}

открытый класс ItemAdapter extends BaseAdapter {

LayoutInflater mInflater;
String[] items;
String[] bushido;
String[] description;
 TextView finalscorre;
Integer scr;

public ItemAdapter(Context c,String[] i ,String [] p ,String[] d, Integer scc,TextView sc) {
    items = i;
    bushido = p;
    description = d;
    finalscorre = sc;
    scr = scc;
    mInflater = (LayoutInflater) c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getCount() {
    return items.length;
}

@Override
public Object getItem(int i) {
    return items[i];
}

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

@Override
public View getView(int i, View convertView, ViewGroup parent) {
    View v = mInflater.inflate(R.layout.my_listview_detail, null);
    TextView nameTextView = v.findViewById(R.id.NameTextView);
    TextView bushidoTextView = v.findViewById(R.id.bushidoTextView);
    TextView descriptionTextView = v.findViewById(R.id.descriptionTextView);
    finalscorre = v.findViewById(R.id.finalScoreView);

    String name = items[i];
    String desc = description[i];
    String bush = bushido[i];


    finalscorre.setText("Waat");
    nameTextView.setText(name);
    descriptionTextView.setText(desc);
    bushidoTextView.setText(bush);

    ItemAdapter.this.notifyDataSetChanged();

    return v;
}

}

Когда я пытаюсь назначить LiveData в TextView из ListView, он работает, но когда я пытаюсь это сделать, ничего не происходит (без ошибок и без результата).


person Radostin Rachev    schedule 25.05.2019    source источник
comment
Вы получаете представления элементов из адаптера и напрямую их обновляете. Правильный способ сделать это - передать обновленные данные адаптеру и сигнализировать адаптеру об обновлении их содержимого, вызвав .notifyDataSetChanged(). Если вам нужен более подробный ответ, вам следует показать реализацию вашего адаптера.   -  person Sanlok Lee    schedule 25.05.2019
comment
@SanlokLee Я вношу некоторые изменения, но все еще не работает. Возможно, у меня есть некоторые заблуждения, о которых я не знаю. Я открыт для новых советов.   -  person Radostin Rachev    schedule 26.05.2019


Ответы (2)


Адаптеры действуют как мост между данными (в вашем случае массивом строк, который вы передаете в адаптер) и ListView (в вашем случае myListView). Для любых изменений ListView необходимо выполнить следующие действия:

  • внести желаемые изменения в сами данные
  • передать эти новые данные адаптеру
  • адаптер обновляет представление списка

Я написал пример кода:

ItemAdapter

public class ItemAdapter extends BaseAdapter {

    LayoutInflater mInflater;
    String[] items;
    String[] bushido;
    String[] description;
    // TextView finalscorre; // I commented out this TextView please see the comment below
    Integer scr;

    public ItemAdapter(Context c,String[] i ,String [] p ,String[] d, Integer scc,TextView sc) {
        items = i;
        bushido = p;
        description = d;
        // finalscorre = sc;
        scr = scc;
        mInflater = (LayoutInflater) c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    public void updateItems(String[] i, String[] p, String[] d, Integer scc) {
        items = i;
        bushido = p;
        description = d;
        scr = scc;

        // notify the adapter to refresh the list view.
        notifyDataSetChanged()
    }

    @Override
    public int getCount() {
        return items.length;
    }

    @Override
    public Object getItem(int i) {
        return items[i];
    }

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

    @Override
    public View getView(int i, View convertView, ViewGroup parent) {
        View v = mInflater.inflate(R.layout.my_listview_detail, null);
        TextView nameTextView = v.findViewById(R.id.NameTextView);
        TextView bushidoTextView = v.findViewById(R.id.bushidoTextView);
        TextView descriptionTextView = v.findViewById(R.id.descriptionTextView);
        //finalscorre = v.findViewById(R.id.finalScoreView);
        TextView finalscorre = v.findViewById(R.id.finalScoreView);


        String name = items[i];
        String desc = description[i];
        String bush = bushido[i];


        // finalscorre.setText("Waat");
        finalscorre.setText(scr);
        nameTextView.setText(name);
        descriptionTextView.setText(desc);
        bushidoTextView.setText(bush);

        // ItemAdapter.this.notifyDataSetChanged(); Do NOT call this method inside getView

        return v;
    }
}

Фрагмент

public class ListFrag extends Fragment {

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        // Create and set adapter just once. Do not create or set new adapter in observer, for loop, etc.
        itemAdapter = new ItemAdapter(getActivity(),items,bushido,description,s,finalScore);
        myListView.setAdapter(itemAdapter);

        model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);

        model.getCurrentScore().observe(getViewLifecycleOwner(), new Observer<Integer>() {
            @Override
            public void onChanged(@Nullable Integer s) {
                itemAdapter.updateItems(items,bushido,description,s);
            }
        });
    }
}

Мне не удалось понять, что finalscorre должно быть, поэтому я предположил, что это текстовое представление, в котором хранится окончательная оценка, о которой вы говорили, и все элементы представления списка должны иметь одинаковое значение. Если это не так, пожалуйста, поясните свой вопрос.

Также обратите внимание, что, хотя этого решения достаточно для решения конкретной проблемы, о которой вы упомянули, можно внести некоторые другие улучшения:

  • Используйте RecyclerView вместо ListView.
  • Вместо того, чтобы поддерживать несколько String[], определите POJO, который представляет элемент и имеет один массив.

Кроме того, проверяя это реализация адаптера в примере Google поможет.

person Sanlok Lee    schedule 26.05.2019
comment
Теперь его работа :). Спасибо за время, приятель. И да, теперь я попытаюсь выяснить, как обновить только один просмотр. Я уже пробовал какую-то ерунду: D. - person Radostin Rachev; 27.05.2019

внутри адаптера вы ничего не делаете, что обновляет ваши LiveData

person Nurseyit Tursunkulov    schedule 26.05.2019