Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for recyclerview-selection #324

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions example/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ dependencies {
implementation project(':example-shared')
implementation project(':library')
implementation project(':library-kotlin-android-extensions')
implementation "androidx.recyclerview:recyclerview-selection:1.1.0-rc01"
}

repositories {
Expand Down
37 changes: 19 additions & 18 deletions example/src/main/java/com/xwray/groupie/example/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,21 @@ import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.recyclerview.selection.SelectionPredicates
import androidx.recyclerview.selection.SelectionTracker
import androidx.recyclerview.selection.StableIdKeyProvider
import androidx.recyclerview.selection.StorageStrategy
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import com.xwray.groupie.ExpandableGroup
import com.xwray.groupie.Group
import com.xwray.groupie.GroupAdapter
import com.xwray.groupie.GroupieViewHolder
import com.xwray.groupie.OnItemClickListener
import com.xwray.groupie.OnItemLongClickListener
import com.xwray.groupie.Section
import com.xwray.groupie.*
import com.xwray.groupie.example.core.InfiniteScrollListener
import com.xwray.groupie.example.core.Prefs
import com.xwray.groupie.example.core.SettingsActivity
import com.xwray.groupie.example.core.decoration.CarouselItemDecoration
import com.xwray.groupie.example.core.decoration.DebugItemDecoration
import com.xwray.groupie.example.core.decoration.SwipeTouchCallback
import com.xwray.groupie.example.item.CardItem
import com.xwray.groupie.example.item.CarouselCardItem
import com.xwray.groupie.example.item.CarouselItem
import com.xwray.groupie.example.item.ColumnItem
import com.xwray.groupie.example.item.FAVORITE
import com.xwray.groupie.example.item.FullBleedCardItem
import com.xwray.groupie.example.item.HeaderItem
import com.xwray.groupie.example.item.HeartCardItem
import com.xwray.groupie.example.item.SmallCardItem
import com.xwray.groupie.example.item.SwipeToDeleteItem
import com.xwray.groupie.example.item.UpdatableItem
import com.xwray.groupie.example.item.*
import com.xwray.groupie.groupiex.plusAssign
import kotlinx.android.synthetic.main.activity_main.*

Expand All @@ -57,6 +45,8 @@ class MainActivity : AppCompatActivity() {
private val infiniteLoadingSection = Section(HeaderItem(R.string.infinite_loading))
private val swipeSection = Section(HeaderItem(R.string.swipe_to_delete))

private var tracker: SelectionTracker<Long>? = null

// Normally there's no need to hold onto a reference to this list, but for demonstration
// purposes, we'll shuffle this list and post an update periodically
private val updatableItems: ArrayList<UpdatableItem> by lazy {
Expand Down Expand Up @@ -102,6 +92,16 @@ class MainActivity : AppCompatActivity() {
})
}

tracker = SelectionTracker.Builder<Long>(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding this tracker to the whole recyclerview in the example breaks it. You should only add selection on the items that can actually be selected. From the looks of it, we should probably change GroupieLookup to only returns items if they can be selected

GroupieLookup.GROUPIE_SELECTION_ID,
recyclerView,
StableIdKeyProvider(recyclerView),
GroupieLookup(recyclerView),
StorageStrategy.createLongStorage()
).withSelectionPredicate(SelectionPredicates.createSelectAnything())
.build()
groupAdapter.setSelectionTracker(tracker)

ItemTouchHelper(touchCallback).attachToRecyclerView(recyclerView)

fab.setOnClickListener { startActivity(Intent(this@MainActivity, SettingsActivity::class.java)) }
Expand All @@ -112,6 +112,7 @@ class MainActivity : AppCompatActivity() {

private fun recreateAdapter() {
groupAdapter.clear()
groupAdapter.setHasStableIds(true)

if (prefs.useAsync) {
populateAdapterAsync()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.xwray.groupie.example.item

import android.graphics.Color
import androidx.annotation.ColorInt
import com.xwray.groupie.example.INSET
import com.xwray.groupie.example.INSET_TYPE_KEY
Expand All @@ -17,7 +18,11 @@ open class CardItem (@ColorInt private val colorInt: Int, val text: CharSequence
override fun getLayout() = R.layout.item_card

override fun bind(viewHolder: GroupieViewHolder, position: Int) {
viewHolder.root.setBackgroundColor(colorInt)
if (isSelectable) {
viewHolder.root.setBackgroundColor(Color.GRAY)
} else {
viewHolder.root.setBackgroundColor(colorInt)
}
viewHolder.text.text = text
}
}
1 change: 1 addition & 0 deletions library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ android {

dependencies {
implementation "androidx.recyclerview:recyclerview:1.1.0"
compileOnly "androidx.recyclerview:recyclerview-selection:1.1.0-rc01"
testImplementation "junit:junit:$junit_version"
testImplementation "org.mockito:mockito-core:$mockito_version"
}
Expand Down
12 changes: 11 additions & 1 deletion library/src/main/java/com/xwray/groupie/GroupAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.selection.SelectionTracker;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
Expand All @@ -22,6 +23,7 @@ public class GroupAdapter<VH extends GroupieViewHolder> extends RecyclerView.Ada
private final List<Group> groups = new ArrayList<>();
private OnItemClickListener onItemClickListener;
private OnItemLongClickListener onItemLongClickListener;
private SelectionTracker<Long> selectionTracker;
private int spanCount = 1;
private Item lastItemForViewTypeLookup;

Expand Down Expand Up @@ -200,6 +202,10 @@ public void setOnItemLongClickListener(@Nullable OnItemLongClickListener onItemL
this.onItemLongClickListener = onItemLongClickListener;
}

public void setSelectionTracker(@Nullable SelectionTracker<Long> selectionTracker) {
this.selectionTracker = selectionTracker;
}

@Override
@NonNull
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
Expand All @@ -217,7 +223,11 @@ public void onBindViewHolder(@NonNull VH holder, int position) {
@Override
public void onBindViewHolder(@NonNull VH holder, int position, @NonNull List<Object> payloads) {
Item contentItem = getItem(position);
contentItem.bind(holder, position, payloads, onItemClickListener, onItemLongClickListener);
if (selectionTracker != null) {
contentItem.bind(holder, position, payloads, onItemClickListener, onItemLongClickListener, selectionTracker.isSelected(contentItem.getId()));
}else {
contentItem.bind(holder, position, payloads, onItemClickListener, onItemLongClickListener, false);
}
}

@Override
Expand Down
33 changes: 33 additions & 0 deletions library/src/main/java/com/xwray/groupie/GroupieLookup.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.xwray.groupie;

import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.selection.ItemDetailsLookup;
import androidx.recyclerview.widget.RecyclerView;

public class GroupieLookup extends ItemDetailsLookup<Long> {

public final static String GROUPIE_SELECTION_ID = "groupie-selection";
private RecyclerView recyclerView;

public GroupieLookup(RecyclerView recyclerView) {
this.recyclerView = recyclerView;
}

@Nullable
@Override
public ItemDetails<Long> getItemDetails(@NonNull MotionEvent e) {
View view = recyclerView.findChildViewUnder(e.getX(), e.getY());
if (view != null) {
RecyclerView.ViewHolder viewHolder = recyclerView.getChildViewHolder(view);
if (viewHolder instanceof GroupieViewHolder) {
return ((GroupieViewHolder) viewHolder).getItemDetails();
}
}

return null;
}
}
16 changes: 16 additions & 0 deletions library/src/main/java/com/xwray/groupie/GroupieViewHolder.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.selection.ItemDetailsLookup;
import androidx.recyclerview.widget.RecyclerView;
import android.view.View;

Expand Down Expand Up @@ -94,4 +95,19 @@ public Item getItem() {
public View getRoot() {
return itemView;
}

public ItemDetailsLookup.ItemDetails<Long> getItemDetails() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure whether this is the best place for this. Should this instead be on the item so that users can overwrite it? I can imagine us wanting to provide users with a custom selection key.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding the type of the custom selection key I would like to suggest Long.

return new ItemDetailsLookup.ItemDetails<Long>(){
@Override
public int getPosition() {
return getAdapterPosition();
}

@Nullable
@Override
public Long getSelectionKey() {
return getItemId();
}
};
}
}
12 changes: 11 additions & 1 deletion library/src/main/java/com/xwray/groupie/Item.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.selection.SelectionTracker;
import androidx.recyclerview.widget.RecyclerView;
import android.view.View;

Expand All @@ -18,6 +19,7 @@ public abstract class Item<VH extends GroupieViewHolder> implements Group, SpanS
protected GroupDataObserver parentDataObserver;
private final long id;
private Map<String, Object> extras = new HashMap<>();
private boolean isSelectable = false;

public Item() {
this(ID_COUNTER.decrementAndGet());
Expand All @@ -40,11 +42,14 @@ public VH createViewHolder(@NonNull View itemView) {
* @param payloads Any payloads (this list may be empty)
* @param onItemClickListener An optional adapter-level click listener
* @param onItemLongClickListener An optional adapter-level long click listener
* @param isSelected An optional property stating if a selectable item is selected
*/
@CallSuper
public void bind(@NonNull VH viewHolder, int position, @NonNull List<Object> payloads,
@Nullable OnItemClickListener onItemClickListener,
@Nullable OnItemLongClickListener onItemLongClickListener) {
@Nullable OnItemLongClickListener onItemLongClickListener,
boolean isSelected) {
isSelectable = isSelected;
viewHolder.bind(this, onItemClickListener, onItemLongClickListener);
bind(viewHolder, position, payloads);
}
Expand All @@ -70,6 +75,7 @@ public void bind(@NonNull VH viewHolder, int position, @NonNull List<Object> pay
*/
@CallSuper
public void unbind(@NonNull VH viewHolder) {
isSelectable = false;
viewHolder.unbind();
}

Expand Down Expand Up @@ -152,6 +158,10 @@ public boolean isLongClickable() {
return true;
}

public boolean isSelectable() {
return isSelectable;
}

public void notifyChanged() {
if (parentDataObserver != null) {
parentDataObserver.onItemChanged(this, 0);
Expand Down