Adds OnMoveCallback to the SwipeToDeleteCallback class.

Makes traits orderable.
This commit is contained in:
Tom Hicks
2021-06-26 23:21:28 -07:00
parent 1a16404948
commit 6a5278362c
11 changed files with 120 additions and 65 deletions

View File

@@ -7,23 +7,22 @@ import androidx.room.TypeConverters;
import com.majinnaibu.monstercards.data.MonsterDAO;
import com.majinnaibu.monstercards.data.converters.ArmorTypeConverter;
import com.majinnaibu.monstercards.data.converters.ChallengeRatingConverter;
import com.majinnaibu.monstercards.data.converters.ListOfTraitsConverter;
import com.majinnaibu.monstercards.data.converters.SetOfLanguageConverter;
import com.majinnaibu.monstercards.data.converters.SetOfSkillConverter;
import com.majinnaibu.monstercards.data.converters.SetOfStringConverter;
import com.majinnaibu.monstercards.data.converters.SetOfTraitConverter;
import com.majinnaibu.monstercards.data.converters.UUIDConverter;
import com.majinnaibu.monstercards.models.Monster;
import com.majinnaibu.monstercards.models.MonsterFTS;
@SuppressWarnings("unused")
@Database(entities = {Monster.class, MonsterFTS.class}, version = 3)
@TypeConverters({
ArmorTypeConverter.class,
ChallengeRatingConverter.class,
ListOfTraitsConverter.class,
SetOfLanguageConverter.class,
SetOfSkillConverter.class,
SetOfStringConverter.class,
SetOfTraitConverter.class,
UUIDConverter.class,
})
public abstract class AppDatabase extends RoomDatabase {

View File

@@ -7,20 +7,20 @@ import com.google.gson.reflect.TypeToken;
import com.majinnaibu.monstercards.models.Trait;
import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.Set;
import java.util.ArrayList;
import java.util.List;
public class SetOfTraitConverter {
public class ListOfTraitsConverter {
@TypeConverter
public static String fromSetOfTrait(Set<Trait> traits) {
public static String fromListOfTraits(List<Trait> traits) {
Gson gson = new Gson();
return gson.toJson(traits);
}
@TypeConverter
public static Set<Trait> setOfTraitFromString(String string) {
public static List<Trait> listOfTraitsFromString(String string) {
Gson gson = new Gson();
Type setType = new TypeToken<HashSet<Trait>>() {
Type setType = new TypeToken<ArrayList<Trait>>() {
}.getType();
return gson.fromJson(string, setType);
}

View File

@@ -187,22 +187,22 @@ public class Monster {
public Set<Language> languages;
@ColumnInfo(name = "abilities", defaultValue = "[]")
public Set<Trait> abilities;
public List<Trait> abilities;
@ColumnInfo(name = "actions", defaultValue = "[]")
public Set<Trait> actions;
public List<Trait> actions;
@ColumnInfo(name = "reactions", defaultValue = "[]")
public Set<Trait> reactions;
public List<Trait> reactions;
@ColumnInfo(name = "lair_actions", defaultValue = "[]")
public Set<Trait> lairActions;
public List<Trait> lairActions;
@ColumnInfo(name = "legendary_actions", defaultValue = "[]")
public Set<Trait> legendaryActions;
public List<Trait> legendaryActions;
@ColumnInfo(name = "regional_actions", defaultValue = "[]")
public Set<Trait> regionalActions;
public List<Trait> regionalActions;
public Monster() {
id = UUID.randomUUID();
@@ -258,12 +258,12 @@ public class Monster {
damageVulnerabilities = new HashSet<>();
conditionImmunities = new HashSet<>();
languages = new HashSet<>();
abilities = new HashSet<>();
actions = new HashSet<>();
reactions = new HashSet<>();
lairActions = new HashSet<>();
legendaryActions = new HashSet<>();
regionalActions = new HashSet<>();
abilities = new ArrayList<>();
actions = new ArrayList<>();
reactions = new ArrayList<>();
lairActions = new ArrayList<>();
legendaryActions = new ArrayList<>();
regionalActions = new ArrayList<>();
}
public static int getAbilityModifierForScore(int score) {

View File

@@ -75,11 +75,11 @@ public class EditLanguagesFragment extends MCFragment {
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(context, layoutManager.getOrientation());
recyclerView.addItemDecoration(dividerItemDecoration);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(context, position -> {
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(context, (position, direction) -> {
if (position > 0) {
mViewModel.removeLanguage(position - 1);
}
}));
}, null));
itemTouchHelper.attachToRecyclerView(recyclerView);
}

View File

@@ -992,12 +992,12 @@ public class EditMonsterViewModel extends ChangeTrackedViewModel {
monster.damageVulnerabilities = new HashSet<>(mDamageVulnerabilities.getValue());
monster.conditionImmunities = new HashSet<>(mConditionImmunities.getValue());
monster.languages = new HashSet<>(mLanguages.getValue());
monster.abilities = new HashSet<>(mAbilities.getValue());
monster.actions = new HashSet<>(mActions.getValue());
monster.reactions = new HashSet<>(mReactions.getValue());
monster.lairActions = new HashSet<>(mLairActions.getValue());
monster.legendaryActions = new HashSet<>(mLegendaryActions.getValue());
monster.regionalActions = new HashSet<>(mRegionalActions.getValue());
monster.abilities = new ArrayList<>(mAbilities.getValue());
monster.actions = new ArrayList<>(mActions.getValue());
monster.reactions = new ArrayList<>(mReactions.getValue());
monster.lairActions = new ArrayList<>(mLairActions.getValue());
monster.legendaryActions = new ArrayList<>(mLegendaryActions.getValue());
monster.regionalActions = new ArrayList<>(mRegionalActions.getValue());
return monster;
}
@@ -1176,12 +1176,30 @@ public class EditMonsterViewModel extends ChangeTrackedViewModel {
}
}
@SuppressWarnings("SameParameterValue")
private static class Helpers {
static String addStringToList(String newString, MutableLiveData<List<String>> strings) {
return addItemToList(strings, newString, String::compareToIgnoreCase);
public boolean moveTrait(TraitType type, int from, int to) {
switch (type) {
case ABILITY:
return Helpers.moveItemInList(mAbilities, from, to);
case ACTION:
return Helpers.moveItemInList(mActions, from, to);
case LAIR_ACTION:
return Helpers.moveItemInList(mLairActions, from, to);
case LEGENDARY_ACTION:
return Helpers.moveItemInList(mLegendaryActions, from, to);
case REACTIONS:
return Helpers.moveItemInList(mReactions, from, to);
case REGIONAL_ACTION:
return Helpers.moveItemInList(mRegionalActions, from, to);
default:
Logger.logUnimplementedFeature(String.format("Unrecognized TraitType: %s", type));
return false;
}
}
@SuppressWarnings("SameParameterValue")
private static class Helpers {
static <T> T addItemToList(MutableLiveData<List<T>> listData, T newItem) {
return addItemToList(listData, newItem, null);
}
@@ -1228,6 +1246,7 @@ public class EditMonsterViewModel extends ChangeTrackedViewModel {
listData.setValue(newList);
}
@SuppressWarnings("unused")
static <T> void replaceItemInList(MutableLiveData<List<T>> listData, int position, T newItem) {
replaceItemInList(listData, position, newItem, null);
}
@@ -1266,5 +1285,23 @@ public class EditMonsterViewModel extends ChangeTrackedViewModel {
}
return value;
}
static <T> boolean moveItemInList(ChangeTrackedLiveData<List<T>> listData, int from, int to) {
List<T> oldList = listData.getValue();
if (oldList == null) {
oldList = new ArrayList<>();
}
ArrayList<T> newList = new ArrayList<>(oldList);
T item = oldList.get(from);
if (from > to) {
from = from + 1;
} else if (to > from) {
to = to + 1;
}
newList.add(to, item);
newList.remove(from);
listData.setValue(newList);
return true;
}
}
}

View File

@@ -70,7 +70,7 @@ public class EditSkillsFragment extends MCFragment {
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(context, layoutManager.getOrientation());
recyclerView.addItemDecoration(dividerItemDecoration);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(context, mViewModel::removeSkill));
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(context, (position, direction) -> mViewModel.removeSkill(position), null));
itemTouchHelper.attachToRecyclerView(recyclerView);
}

View File

@@ -101,7 +101,7 @@ public class EditStringsFragment extends MCFragment {
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(context, layoutManager.getOrientation());
recyclerView.addItemDecoration(dividerItemDecoration);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(context, position -> mViewModel.removeString(mStringType, position)));
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(context, (position, direction) -> mViewModel.removeString(mStringType, position), null));
itemTouchHelper.attachToRecyclerView(recyclerView);
}

View File

@@ -35,6 +35,7 @@ public class EditTraitsFragment extends MCFragment {
private EditMonsterViewModel mViewModel;
private ViewHolder mHolder;
private TraitType mTraitType;
private EditTraitsRecyclerViewAdapter mAdapter;
@Override
@@ -88,22 +89,21 @@ public class EditTraitsFragment extends MCFragment {
recyclerView.setLayoutManager(layoutManager);
LiveData<List<Trait>> traitData = mViewModel.getTraits(mTraitType);
if (traitData != null) {
traitData.observe(getViewLifecycleOwner(), traits -> {
EditTraitsRecyclerViewAdapter adapter = new EditTraitsRecyclerViewAdapter(traits, trait -> {
mAdapter = new EditTraitsRecyclerViewAdapter(trait -> {
if (trait != null) {
navigateToEditTrait(trait);
} else {
Logger.logError("Can't navigate to EditTraitFragment with a null trait");
}
});
recyclerView.setAdapter(adapter);
});
if (traitData != null) {
traitData.observe(getViewLifecycleOwner(), traits -> mAdapter.submitList(traits));
}
recyclerView.setAdapter(mAdapter);
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(context, layoutManager.getOrientation());
recyclerView.addItemDecoration(dividerItemDecoration);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(context, position -> mViewModel.removeTrait(mTraitType, position)));
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(context, (position, direction) -> mViewModel.removeTrait(mTraitType, position), (from, to) -> mViewModel.moveTrait(mTraitType, from, to)));
itemTouchHelper.attachToRecyclerView(recyclerView);
}

View File

@@ -4,6 +4,9 @@ import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
import com.majinnaibu.monstercards.databinding.FragmentEditTraitsListItemBinding;
@@ -11,14 +14,23 @@ import com.majinnaibu.monstercards.models.Trait;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class EditTraitsRecyclerViewAdapter extends ListAdapter<Trait, EditTraitsRecyclerViewAdapter.ViewHolder> {
private static final DiffUtil.ItemCallback<Trait> DIFF_CALLBACK = new DiffUtil.ItemCallback<Trait>() {
public class EditTraitsRecyclerViewAdapter extends RecyclerView.Adapter<EditTraitsRecyclerViewAdapter.ViewHolder> {
private final List<Trait> mValues;
@Override
public boolean areItemsTheSame(@NonNull @NotNull Trait oldItem, @NonNull @NotNull Trait newItem) {
return oldItem.equals(newItem);
}
@Override
public boolean areContentsTheSame(@NonNull @NotNull Trait oldItem, @NonNull @NotNull Trait newItem) {
return oldItem.equals(newItem);
}
};
private final ItemCallback mOnClick;
public EditTraitsRecyclerViewAdapter(List<Trait> items, ItemCallback onClick) {
mValues = items;
protected EditTraitsRecyclerViewAdapter(ItemCallback onClick) {
super(DIFF_CALLBACK);
mOnClick = onClick;
}
@@ -30,8 +42,8 @@ public class EditTraitsRecyclerViewAdapter extends RecyclerView.Adapter<EditTrai
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.mItem = mValues.get(position);
holder.mContentView.setText(mValues.get(position).name);
holder.mItem = getItem(position);
holder.mContentView.setText(holder.mItem.name);
holder.itemView.setOnClickListener(v -> {
if (mOnClick != null) {
mOnClick.onItemCallback(holder.mItem);
@@ -39,11 +51,6 @@ public class EditTraitsRecyclerViewAdapter extends RecyclerView.Adapter<EditTrai
});
}
@Override
public int getItemCount() {
return mValues.size();
}
public interface ItemCallback {
void onItemCallback(Trait trait);
}

View File

@@ -76,7 +76,7 @@ public class LibraryFragment extends MCFragment {
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(context, layoutManager.getOrientation());
recyclerView.addItemDecoration(dividerItemDecoration);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(requireContext(), adapter::deleteItem));
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(requireContext(), (position, direction) -> adapter.deleteItem(position), null));
itemTouchHelper.attachToRecyclerView(recyclerView);
}

View File

@@ -21,11 +21,14 @@ public class SwipeToDeleteCallback extends ItemTouchHelper.SimpleCallback {
private final Drawable icon;
private final ColorDrawable background;
private final Paint clearPaint;
private final OnDeleteCallback mOnDelete;
private final OnSwipeCallback mOnDelete;
private final OnMoveCallback mOnMove;
private final Context mContext;
public SwipeToDeleteCallback(Context context, OnDeleteCallback onDelete) {
super(0, ItemTouchHelper.LEFT);
public SwipeToDeleteCallback(Context context, OnSwipeCallback onDelete, OnMoveCallback onMove) {
super(onMove == null ? 0 : ItemTouchHelper.UP | ItemTouchHelper.DOWN, onDelete == null ? 0 : ItemTouchHelper.LEFT);
mOnDelete = onDelete;
mOnMove = onMove;
mContext = context;
icon = ContextCompat.getDrawable(mContext, R.drawable.ic_delete_white_36);
background = new ColorDrawable(context.getResources().getColor(R.color.red));
@@ -39,6 +42,11 @@ public class SwipeToDeleteCallback extends ItemTouchHelper.SimpleCallback {
@NonNull RecyclerView.ViewHolder viewHolder,
@NonNull RecyclerView.ViewHolder target
) {
if (mOnMove != null) {
int from = viewHolder.getAdapterPosition();
int to = target.getAdapterPosition();
return mOnMove.onMove(from, to);
}
return false;
}
@@ -46,7 +54,7 @@ public class SwipeToDeleteCallback extends ItemTouchHelper.SimpleCallback {
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
if (mOnDelete != null) {
int position = viewHolder.getAdapterPosition();
mOnDelete.onDelete(position);
mOnDelete.onSwipe(position, direction);
}
}
@@ -80,7 +88,11 @@ public class SwipeToDeleteCallback extends ItemTouchHelper.SimpleCallback {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
public interface OnDeleteCallback {
void onDelete(int position);
public interface OnSwipeCallback {
void onSwipe(int position, int direction);
}
public interface OnMoveCallback {
boolean onMove(int from, int to);
}
}