1

I'm creating a selectable recycler view. I implemented all classes needed for it to work and it does work, I can select and deselect items and they change state. But if I deselect all items and then start selecting again, the app crashes. I didn't manage to find any solution, so here is my code:

public class WordsListAdapter extends RecyclerView.Adapter<WordsListAdapter.WordViewHolder> {

private ArrayList<Word> mWordsList = new ArrayList<>();
private SelectionTracker<Word> mSelectionTracker;
private Context mContext;

@Override
public long getItemId(int position) {
    return (long)position;
}

public WordsListAdapter(Context context) {
    mContext = context;
    setHasStableIds(true);
}

void addData(ArrayList<Word> data) {
    mWordsList.addAll(data);
}

void setSelectionTracker(SelectionTracker st) {
    mSelectionTracker = st;
}

@NonNull
@Override
public WordViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
    View v = LayoutInflater.from(viewGroup.getContext())
            .inflate(R.layout.word_list_item, viewGroup, false);

    return new WordViewHolder(v);
}

@Override
public void onBindViewHolder(@NonNull WordViewHolder wordViewHolder, int i) {}

@Override
public void onBindViewHolder(@NonNull WordViewHolder holder, int position, @NonNull List<Object> payloads) {
    Word word = mWordsList.get(position);

    holder.setActivatedState(mSelectionTracker.isSelected(word));
    if(!payloads.contains(SelectionTracker.SELECTION_CHANGED_MARKER)) {
        holder.setData(word);
    }
}

@Override
public int getItemCount() {
    return mWordsList.size();
}

public class WordViewHolder extends RecyclerView.ViewHolder implements ViewHolderWithDetails<Long> {

    private View mRootView;
    private TextView mWordTextView, mTranslationTextView;

    WordViewHolder(@NonNull View itemView) {
        super(itemView);

        mRootView = itemView;
        mWordTextView = itemView.findViewById(R.id.wordTextView);
        mTranslationTextView = itemView.findViewById(R.id.translationTextView);
        mWordTextView.setMovementMethod(new ScrollingMovementMethod());
        mTranslationTextView.setMovementMethod(new ScrollingMovementMethod());
    }

    protected void setData(Word word) {
        mWordTextView.setText(word.getWord());
        mTranslationTextView.setText(word.getTranslation());
    }

    public void setActivatedState(boolean state) {
        if(mRootView != null) {
            mRootView.setActivated(state);
            mRootView.setBackgroundColor(mContext.getResources().getColor(state ? R.color.colorPrimaryDark : R.color.colorAccent));
        }
    }


    @Override
    public WordDetails getItemDetails() {
        int pos = getAdapterPosition();
        return new WordDetails(pos, getItemId());
    }
}

public interface ViewHolderWithDetails<TItem> {
    ItemDetailsLookup.ItemDetails<TItem> getItemDetails();
}

public class WordKeyProvider extends ItemKeyProvider<Long> {
    private RecyclerView mRecyclerView;

    protected WordKeyProvider(RecyclerView recyclerView) {
        super(ItemKeyProvider.SCOPE_CACHED);
        mRecyclerView = recyclerView;
    }

    @Nullable
    @Override
    public Long getKey(int position) {
        return getItemId(position);
    }

    @Override
    public int getPosition(@NonNull Long key) {
        RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForItemId(key);
        return vh == null ? RecyclerView.NO_POSITION : vh.getLayoutPosition();
    }
}


}

WordDetails class:

public class WordDetails extends ItemDetailsLookup.ItemDetails<Long> {

private int adapterPosition;
private Long selectedKey;

public WordDetails(int position, Long key) {
    adapterPosition = position;
    selectedKey = key;
}

@Override
public int getPosition() {
    return adapterPosition;
}

@Nullable
@Override
public Long getSelectionKey() {
    return selectedKey;
}
}

WordLookup:

public class WordLookup extends ItemDetailsLookup<Long> {

private RecyclerView mRecyclerView;

public WordLookup(RecyclerView recyclerView) {
    mRecyclerView = recyclerView;
}

@Nullable
@Override
public ItemDetails<Long> getItemDetails(@NonNull MotionEvent e) {
    View v = mRecyclerView.findChildViewUnder(e.getX(), e.getY());
    if(v != null) {
        RecyclerView.ViewHolder vh = mRecyclerView.getChildViewHolder(v);
        if(vh instanceof  WordsListAdapter.WordViewHolder) {
            return ((WordsListAdapter.WordViewHolder)vh).getItemDetails();
        }
    }

    return null;
}
}

That's how I initialize SelectionTracker:

final SelectionTracker<Long> selectionTracker = new SelectionTracker.Builder<>(
            "my-word-selection",
            mWordsRecyclerView,
            new StableIdKeyProvider(mWordsRecyclerView),
            new WordLookup(mWordsRecyclerView),
            StorageStrategy.createLongStorage()
    ).build();
    mWordsAdapter.setSelectionTracker(selectionTracker);

Here is the logcat:

02-06 17:07:32.302 32103-32103/com.xel E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.xel, PID: 32103
java.lang.IllegalStateException
    at android.support.v4.util.Preconditions.checkState(Preconditions.java:131)
    at android.support.v4.util.Preconditions.checkState(Preconditions.java:143)
    at androidx.recyclerview.selection.GestureSelectionHelper.start(GestureSelectionHelper.java:76)
    at androidx.recyclerview.selection.SelectionTracker$Builder$4.run(SelectionTracker.java:742)
    at androidx.recyclerview.selection.TouchInputHandler.onLongPress(TouchInputHandler.java:136)
    at androidx.recyclerview.selection.GestureRouter.onLongPress(GestureRouter.java:95)
    at android.view.GestureDetector.dispatchLongPress(GestureDetector.java:770)
    at android.view.GestureDetector.access$200(GestureDetector.java:40)
    at android.view.GestureDetector$GestureHandler.handleMessage(GestureDetector.java:293)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:157)
    at android.app.ActivityThread.main(ActivityThread.java:5601)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:774)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:652)

So I have no idea what could cause this error, what can be the reason?

Alex Chashin
  • 3,129
  • 1
  • 13
  • 35

2 Answers2

2

Make sure recyclerview and recyclerview-selection dependencies in build.gradle file are with same version like this:

implementation "androidx.recyclerview:recyclerview:1.0.0"
implementation 'androidx.recyclerview:recyclerview-selection:1.0.0'

When I touched RecyclerView Items I had Illegalexception, because my gradle dependencies was like this:

implementation "androidx.recyclerview:recyclerview:1.1.0-alpha04"
implementation 'androidx.recyclerview:recyclerview-selection:1.0.0-alpha3'
hadi julaee
  • 31
  • 1
  • 5
0

You can examine the code in the GestureSelectionHelper.start method to see what arguments are being checked. In my case, the adapter had not yet been instantiated. It was necessary to also set the adapter on the RecyclerView before instantiating the SelectionTracker.

deHaar
  • 17,687
  • 10
  • 38
  • 51
Scott
  • 93
  • 1
  • 8