RecyclerView 中的项目位置仅在拖动列表时发生变化,同时也是错误的

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

我正在使用一个 livedata>,它被用来 poblate 一个回收器视图,然后我用它来 select 该列表中的一个项目,并将它传递给另一个 activity .

Said recyclerview can be here seen in action:

您可能会看到,当我单击“Ansiedad”项时,我得到了另一个与 selected 项不匹配的项“Artrosis de codo”。只要我不在 recyclerview 周围拖来拖去,无论我在列表上的哪个位置触摸,该项目都会得到 selected。当我在列表中向上或向下移动时,项目会发生变化,但我从未设法使其与实际 selected 项目相匹配。似乎总是 select 无需向下滚动即可看到 recyclerview 上的最后一项。我推测这是由于在过滤时创建了一个新的 LiveData>,但我还没有找到任何关于它的东西。

As it can be seen here:

我已经遇到这个问题好几天了,我没有找到任何关于如何解决这个问题的好信息,所以我认为最好问一下。

这里是 类 连接到那个 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;
    }

}

列表适配器:

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


}

视图模型:

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


}

实体库:

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


}

问题是您没有传递您正在点击的项目,所以您现在不能在点击的地方,而不是这个:

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

做这样的事情:

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

现在您在适配器中的 getCurrentObject 方法获取点击的项目位置

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

您遇到的问题似乎是您的适配器存储了最后绑定的项目,而不是您单击的项目。这就是为什么Adapter的大部分方法都采用“position”参数的原因。

也许你可以在 Adapter 中设置一个 IteractionListener,稍后传递给 ViewHolder(或由它以其他方式访问),然后将 OnClick 监听器添加到 TextView。