简体   繁体   中英

Item position in RecyclerView only changing when dragging list, while being wrong too

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.

As it can be seen here:

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


}

It seems that the problem that you are having is that your Adapter stores the last bound item not the item that you click. This is the reason why most of the methods of the Adapter take the "position" parameter.

Maybe you can set an IteractionListener in the Adapter that is later passed to the ViewHolder (or is accessed by it in some other way), and then adding an OnClick listener to the TextView.

Problem is that you are not passing the item you're clicking so you can not now where the click has been made, instead of this:

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

do something like this:

recyclerView.addOnItemTouchListener(
new RecyclerItemClickListener(context, recyclerView ,new RecyclerItemClickListener.OnItemClickListener() {
  @Override public void onItemClick(View view, int position) {
     pathology = getSelectedPathology(position);
    // rest of your code
  }

  @Override public void onLongItemClick(View view, int position) {
    // do whatever
  }
}));

   public Pathology getSelectedPathology(int position){
    Pathology path = adapter.getCurrentObject(position);
    Log.println(Log.INFO, "PathologyTest ID", path.toString());
    return path;
}

now your getCurrentObject method in the adapter gets the item position where the click was made

    public Pathology getCurrentObject(int clickPosition){
    path = getItem(clickPosition);
    return path;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM