I am working with a livedata<list<>> that's being used to poblate a recycler view which I then use to select an item from that list, and pass it down another activity.
Said recyclerview can be here seen in action:
As you might be able to see, when I click on the item "Ansiedad" I get another item "Artrosis de codo" which does not match the selected item. As long as I don't drag around the recyclerview, that's the item that will get selected, no matter where I touch on the list. As I move up or down the list, the item changes, but I've never managed to make it match the actually selected item. It always seems to select the last item on the recyclerview that can be seen without scrolling down. I've theorized that this is due to creating a new LiveData<List<>> when filtering, but I haven't found anything on that yet.
I've been having this problems for days, and I don't quite find any good information on how to fix this, so I thought it's best just to ask.
Here are the classes that are connected to that recyclerview:
Activity:
package com.gmproxy.pastilarma;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.room.Room;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.SearchView;
import android.widget.Toast;
import com.gmproxy.Adapters.PathologyListAdapter;
import com.gmproxy.DAO.DatabaseHelper;
import com.gmproxy.DAO.PathologyDAO;
import com.gmproxy.Entities.Pathology;
import com.gmproxy.Util.PathologyViewModel;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class PathologiesSearchScreen extends AppCompatActivity {
private PathologyViewModel viewModel;
SearchView searchView;
RecyclerView recyclerView;
Pathology pathology;
PathologyListAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pathology_search_list);
searchView = findViewById(R.id.SearchView);
recyclerView = findViewById(R.id.recyclerview);
adapter = new PathologyListAdapter(new PathologyListAdapter.UserDiff());
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
viewModel = new ViewModelProvider(this).get(PathologyViewModel.class);
viewModel.pathologies.observe(this, adapter::submitList);
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
viewModel.setFilter(searchView.getQuery().toString());
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
long start;
start = System.currentTimeMillis();
if ((newText.length() > 3) && (System.currentTimeMillis() - start > 500)) {
viewModel.setFilter(searchView.getQuery().toString());
}
return false;
}
});
recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
@Override
public boolean onInterceptTouchEvent(@NonNull @NotNull RecyclerView rv, @NonNull @NotNull MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_DOWN
&& rv.getScrollState() != RecyclerView.SCROLL_STATE_SETTLING
&& rv.getScrollState() != RecyclerView.SCROLL_STATE_DRAGGING){
pathology = getSelectedPathology();
Log.println(Log.INFO, "PathologyTest", pathology.toString());
final CharSequence[] options = {"Si", "No"};
AlertDialog.Builder builder = new AlertDialog.Builder(PathologiesSearchScreen.this);
builder.setTitle("¿Añadir la patología " + pathology.getPathologyName() + "?");
builder.setItems(options, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int item) {
if (options[item].equals("Si")) {
Toast.makeText(PathologiesSearchScreen.this, "Has añadido la patología " + pathology.getPathologyName() + ".", Toast.LENGTH_SHORT).show();
Intent mainAct = new Intent(PathologiesSearchScreen.this, UserAddScreen.class);
mainAct.putExtra("path", pathology);
int i = 1;
mainAct.putExtra("path-record",i);
startActivity(mainAct);
} else if (options[item].equals("No")) {
dialog.dismiss();
}
}
});
builder.show();
}
return false;
}
@Override
public void onTouchEvent(@NonNull @NotNull RecyclerView rv, @NonNull @NotNull MotionEvent e) {
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
});
}
public Pathology getSelectedPathology(){
Pathology path = adapter.getCurrentObject();
Log.println(Log.INFO, "PathologyTest ID", path.toString());
return path;
}
}
ListAdapter:
package com.gmproxy.Adapters;
import android.content.DialogInterface;
import android.content.Intent;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.LiveData;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
import com.gmproxy.Entities.Pathology;
import com.gmproxy.pastilarma.PathologiesSearchScreen;
import com.gmproxy.pastilarma.UserAddScreen;
import java.util.List;
public class PathologyListAdapter extends ListAdapter<Pathology, PathologyViewHolder> {
private int positionF;
private Pathology path;
public PathologyListAdapter(@NonNull DiffUtil.ItemCallback<Pathology> diffCallback) {
super(diffCallback);
}
@Override
public PathologyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return PathologyViewHolder.create(parent);
}
@Override
public void onBindViewHolder(PathologyViewHolder holder, int position) {
positionF = position;
Pathology current = getItem(position);
path = current;
holder.bind(current.getPathologyName());
}
public Pathology getCurrentObject(){
path = getItem(positionF);
return path;
}
public int getPositionF(){
return positionF;
}
public static class UserDiff extends DiffUtil.ItemCallback<Pathology> {
@Override
public boolean areItemsTheSame(@NonNull Pathology oldItem, @NonNull Pathology newItem) {
return oldItem == newItem;
}
@Override
public boolean areContentsTheSame(@NonNull Pathology oldItem, @NonNull Pathology newItem) {
return oldItem.getPathologyName().equals(newItem.getPathologyName());
}
}
}
ViewHolder:
package com.gmproxy.Adapters;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.TextView;
import android.widget.Toast;
import androidx.recyclerview.widget.RecyclerView;
import com.gmproxy.DAO.PathologyDAO;
import com.gmproxy.Entities.Pathology;
import com.gmproxy.pastilarma.PathologiesSearchScreen;
import com.gmproxy.pastilarma.R;
import java.nio.file.Path;
public class PathologyViewHolder extends RecyclerView.ViewHolder {
public final TextView objItemView;
public PathologyViewHolder(View itemView) {
super(itemView);
objItemView = itemView.findViewById(R.id.textView);
}
public void bind(String current) {
objItemView.setText(current);
}
static PathologyViewHolder create(ViewGroup parent) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.pathologies_item, parent, false);
return new PathologyViewHolder(view);
}
}
ViewModel:
package com.gmproxy.Util;
import android.app.Application;
import android.widget.Filter;
import android.widget.Filterable;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Transformations;
import com.gmproxy.DAO.PathologyRepository;
import com.gmproxy.Entities.Pathology;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
public class PathologyViewModel extends AndroidViewModel {
private PathologyRepository repository;
public LiveData<List<Pathology>> pathologies;
public MutableLiveData<String> filteredList = new MutableLiveData<>();
public PathologyViewModel(Application application) {
super(application);
repository = new PathologyRepository(application);
pathologies = Transformations.switchMap(filteredList, (input) -> {
if(input == null || input.equals("")){
return repository.getAllObjects();
} else {
return repository.filter(input);
}
});
}
public void setFilter(String query){
filteredList.setValue(query);
}
public Pathology ObtainById(String id) {return repository.obtainById(id); }
public int getDataCount() { return repository.getDataCount();}
public void insert(Pathology obj) { repository.insertObject(obj); }
public void delete(Pathology obj) { repository.deleteObject(obj); }
}
Entity repository:
package com.gmproxy.DAO;
import android.app.Application;
import android.os.AsyncTask;
import androidx.lifecycle.LiveData;
import com.gmproxy.Entities.Pathology;
import com.gmproxy.Entities.User;
import java.util.List;
import java.util.concurrent.ExecutionException;
public class PathologyRepository {
private PathologyDAO concerningDao;
private LiveData<List<Pathology>> pathologyList;
public PathologyRepository(Application application) {
DatabaseHelper db = DatabaseHelper.getDatabase(application);
concerningDao = db.pathologyDao();
pathologyList = concerningDao.getAllObjects();
}
public LiveData<List<Pathology>> getAllObjects() {
return concerningDao.getAllObjects();
}
void insertAllObjects(List<Pathology> objectsList) {
DatabaseHelper.databaseWriteExecutor.execute(() ->{
concerningDao.insertAllObjects(objectsList);
});
}
public void insertObject(Pathology obj){
DatabaseHelper.databaseWriteExecutor.execute(() ->{
concerningDao.insertObject(obj);
});
}
public Pathology obtainById(String id){
try{
return new ObjectAsyncTask(concerningDao).execute(id).get();
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
return null;
}
public int getDataCount() { return concerningDao.getDataCount(); }
public void deleteObject(Pathology obj) {
concerningDao.delete(obj);
}
public LiveData<List<Pathology>> filter(String input){
try{
return new FilterNoteAsyncTask(concerningDao).execute(input).get();
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
return null;
}
private static class FilterNoteAsyncTask extends AsyncTask<String, Void, LiveData<List<Pathology>>> {
private PathologyDAO pathologyDAO;
private FilterNoteAsyncTask(PathologyDAO pathologyDAO) {
this.pathologyDAO = pathologyDAO;
}
@Override
protected LiveData<List<Pathology>> doInBackground(String... strings) {
return pathologyDAO.filterText(strings[0]);
}
}
private static class ObjectAsyncTask extends AsyncTask<String, Void, Pathology>{
private PathologyDAO pathologyDAO;
private ObjectAsyncTask(PathologyDAO pathologyDAO) { this.pathologyDAO = pathologyDAO; }
@Override
protected Pathology doInBackground(String...strings) {
int id = Integer.parseInt(strings.toString());
return pathologyDAO.findObjectById(id);
}
}
}
DAO:
package com.gmproxy.DAO;
import androidx.lifecycle.LiveData;
import androidx.room.*;
import com.gmproxy.Entities.Pathology;
import com.gmproxy.Entities.User;
import java.util.List;
@Dao
public interface PathologyDAO {
@Query("SELECT * FROM condiciones")
LiveData<List<Pathology>> getAllObjects();
//This will come in handy for getting all those pathologies, will need to get them on a for loop since I'm not completely sure
//that the query will handle int[]
@Query("SELECT id_condiciones FROM condiciones WHERE id_condiciones LIKE :id_condiciones")
int getPathologiesForUser(int id_condiciones);
@Query("SELECT nombreCondicion FROM condiciones WHERE nombreCondicion LIKE :pathologyName")
String getPathologiesForName(String pathologyName);
@Query("SELECT * FROM condiciones WHERE nombreCondicion LIKE :pathologyName")
Pathology getPathologiesCompleteForName(String pathologyName);
@Query("SELECT * FROM condiciones WHERE id_condiciones=:id")
Pathology findObjectById(int id);
@Query("SELECT COUNT(id_condiciones) FROM condiciones")
int getDataCount();
@Query("SELECT * FROM condiciones WHERE nombreCondicion LIKE :filter || '%'")
LiveData<List<Pathology>> filterText(String filter);
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertAllObjects(List<Pathology> listObjects);
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertObject(Pathology object);
@Update
void updateObject(Pathology object);
@Delete
void delete(Pathology obj);
}