'Android Assign LiveData<> to ListView

I have a list view to display Users and an Adapter that works well together on normal ArrayLists of Users However i'm trying to implement ViewModels and cannot get my ListView adapter to work with LiveData. I cant get any of the similar questions/answers to work for me. But this feels fairly fundamental and like it should be easy.

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();
}}

and UserAdapter:

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;
}}

And the List_item Layout file

<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" />



Solution 1:[1]

Change:

mainActivityViewModel.getUsers().observe(this, new Observer<List<User>>() {
        @Override
        public void onChanged(@Nullable List<User> users) {
            playerAdapter.notifyDataSetChanged();
        }
    });

To:

mainActivityViewModel.getUsers().observe(this, new Observer<List<User>>() {
        @Override
        public void onChanged(@Nullable List<User> users) {
            playerAdapter.setUsers(users);
        }
    });

And in your adapter create a setter such as:

public void setUsers(List<User> users) {
    this.users = users;
    notifyDataSetChanged();
}

Solution 2:[2]

I recommend using Live Data directly in the Adapter:

public class MyAdapter extends BaseAdapter {

    private final Context mCtx;
    private final LiveData<List<MyModel>> mLiveDate;

    public MyAdapter(Context ctx, LiveData<List<MyModel>> livedata) {
        mCtx = ctx;
        mLiveDate= livedata;
        livedata.observeForever(myModels -> notifyDataSetChanged());
    }

    @Override
    public int getCount() {
        if (mLiveDate.getValue() == null)
            return 0;
        return mLiveDate.getValue().size();
    }

    @Override
    public MyModel getItem(int i) {
        return Objects.requireNonNull(mLiveDate.getValue()).get(i);
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
    ...

        MyModel data = getItem(i);

    ...
        return view;
    }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Almyk
Solution 2 luke8800gts