У меня есть представление списка для отображения пользователей и адаптера, который хорошо работает вместе с обычными списками пользователей ArrayLists. Однако я пытаюсь реализовать ViewModels и не могу заставить свой адаптер ListView работать с LiveData. Я не могу заставить любой из подобных вопросов/ответов работать на меня. Но это кажется довольно фундаментальным и, как будто это должно быть легко.
public class MainActivity extends AppCompatActivity {
MainActivityViewModel mainActivityViewModel;
ListView usersLV;
UsersAdapter userAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mainActivityViewModel = ViewModelProviders.of(this).get(MainActivityViewModel.class);
usersLV = findViewById(R.id.usersLV);
ArrayList<User> users = (ArrayList<User>)mainActivityViewModel.getUsers().getValue();
if (users != null){
userAdapter = new UsersAdapter(this,users);
usersLV.setAdapter(userAdapter);
}else{
Log.d("gwyd","user list was null");
userAdapter = new UsersAdapter(this,new ArrayList<User>());
usersLV.setAdapter(userAdapter);
}
mainActivityViewModel.getUsers().observe(this, new Observer<List<User>>() {
@Override
public void onChanged(@Nullable List<User> users) {
if (users != null) {
userAdapter.setUsers(users);
}else {
Log.d("gwyd","no users found in db");
userAdapter.setUsers(new ArrayList<User>());
}
}
});
}}
ViewModel:
public class MainActivityViewModel extends AndroidViewModel {
private DaoRepository daoRepository;
private LiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
if (users == null){
updateUsersList();
}
return users;
}
public MainActivityViewModel(@NonNull Application application) {
super(application);
daoRepository = new DaoRepository(application);
users = updateUsersList();
}
public LiveData<List<User>> updateUsersList(){
return daoRepository.getAllUsers();
}}
и пользовательский адаптер:
public class UsersAdapter extends ArrayAdapter<User> {
private List<User> users;
public void setUsers(List<User> users) {
this.users = users;
notifyDataSetChanged();
}
public UsersAdapter(Context context, List<User> users) {
super(context, 0, users);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
User user = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.user_list_item, parent, false);
}
// Lookup view for data population
TextView tvName = (TextView) convertView.findViewById(R.id.userLvItemTV);
// Populate the data into the template view using the data object
tvName.setText(user.getUserName());
// Return the completed view to render on screen
return convertView;
}}
И файл макета List_item
<LinearLayout
xmlns:android = "http://schemas.android.com/apk/res/android" android:layout_width = "match_parent"
android:layout_height = "match_parent"
android:orientation = "vertical">
<TextView
android:id = "@+id/userLvItemTV"
android:layout_width = "match_parent"
android:layout_height = "wrap_content" />
Изменять:
mainActivityViewModel.getUsers().observe(this, new Observer<List<User>>() {
@Override
public void onChanged(@Nullable List<User> users) {
playerAdapter.notifyDataSetChanged();
}
});
К:
mainActivityViewModel.getUsers().observe(this, new Observer<List<User>>() {
@Override
public void onChanged(@Nullable List<User> users) {
playerAdapter.setUsers(users);
}
});
И в вашем адаптере создайте сеттер, например:
public void setUsers(List<User> users) {
this.users = users;
notifyDataSetChanged();
}
Я внес предложенные вами изменения, мне также пришлось добавить некоторые другие изменения, чтобы предотвратить сбой, в основном проверка нулевого возврата. Теперь он находится в точке, когда я создаю адаптер, он не может найти пользователей, поэтому использует часть else в статусе. Затем, когда onChange получает удар, он может найти пользователя в списке, но все равно не выводит его, как ожидалось.
(ArrayList<User>)mainActivityViewModel.getUsers().getValue() Кажется, всегда возвращает значение null, если только оно не находится в onChange... но тогда я не могу установить его на адаптер и получить эту ошибку: (UsersAdapter (android.content .Context, List<User>) в UsersAdapter нельзя применить к (anonymous android.arch.lifecycle.Observer<java.util.List<com.example.coursework.model.User>>, List<User>) ) Также мы дали адаптеру список пользователей, но не указали, как заставить его использовать этот список, а не исходный / установить исходный список на этот. сбит с толку
Я думаю, что ваша проблема заключается в этом: public LiveData<List<User>> getUsers() { if (users == null){ updateUsersList(); } return users; изменить так, чтобы: users = updateUsersList();
После нескольких строгих слов с самим собой я придумал это как решение: В основном то же самое, что и я, но в методе onChange ваших наблюдателей используйте getApplicationContext() вместо this для инициализации ArrayAdapter. Кажется, что контекст действий не может быть доступен из внутреннего класса наблюдателей и выдает расплывчатую ошибку, которая привела к путанице...
Мне нужно больше читать о том, как различные контексты влияют на вещи. но если никто не придумает slam dunk, вот как это сделать, я, вероятно, отмечу это как ответ через пару дней, потому что, похоже, это решило проблему.
Основная деятельность:
public class MainActivity extends AppCompatActivity {
MainActivityViewModel mainActivityViewModel;
ListView usersLV;
UsersAdapter userAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mainActivityViewModel = ViewModelProviders.of(this).get(MainActivityViewModel.class);
usersLV = findViewById(R.id.usersLV);
mainActivityViewModel.getUsers().observe(this, new Observer<List<User>>() {
@Override
public void onChanged(@Nullable List<User> users) {
if (users != null) {
userAdapter =new UsersAdapter(getApplicationContext(), users);
usersLV.setAdapter(userAdapter);
}
userAdapter.notifyDataSetChanged();
}
});
}}
Просмотр модели:
public class MainActivityViewModel extends AndroidViewModel {
private DaoRepository daoRepository;
private LiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
if (users == null){
users = updateUsersList();
}
return users;
}
public MainActivityViewModel(@NonNull Application application) {
super(application);
daoRepository = new DaoRepository(application);
users = updateUsersList();
}
public LiveData<List<User>> updateUsersList(){
return daoRepository.getAllUsers();
}}
Адаптер массива:
public class UsersAdapter extends ArrayAdapter<User> {
public UsersAdapter(Context context, List<User> users) {
super(context, 0, users);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
User user = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.user_list_item, parent, false);
}
// Lookup view for data population
TextView tvName = (TextView) convertView.findViewById(R.id.userLvItemTV);
// Populate the data into the template view using the data object
tvName.setText(user.getUserName());
// Return the completed view to render on screen
return convertView;
}
}
Элемент списка Xml:
<?xml version = "1.0" encoding = "utf-8"?>
<LinearLayout
xmlns:android = "http://schemas.android.com/apk/res/android" android:layout_width = "match_parent"
android:layout_height = "match_parent"
android:orientation = "vertical">
<TextView
android:id = "@+id/userLvItemTV"
android:layout_width = "match_parent"
android:textSize = "20sp"
android:height = "20dp"
android:fontFamily = "sans-serif-medium"
android:gravity = "center"
android:layout_height = "60dp" />
</LinearLayout>
Но теперь вы воссоздаете адаптер каждый раз, когда ваш список пользователей меняется. Это не кажется правильным. Ответ Алмыка ниже выглядит более подходящим.
Мой метод getUsers каким-то образом возвращает null, так что мне придется немного разобраться в этом. Но это имеет смысл. дать адаптеру массива ссылку на список пользователей