Makes the swipe to delete callback more generic.

This commit is contained in:
2021-05-02 03:16:02 -07:00
parent ec518cd2db
commit d100624c32
3 changed files with 130 additions and 89 deletions

View File

@@ -1,19 +1,11 @@
package com.majinnaibu.monstercards.ui.library; package com.majinnaibu.monstercards.ui.library;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.navigation.NavDirections; import androidx.navigation.NavDirections;
import androidx.navigation.Navigation; import androidx.navigation.Navigation;
import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.ItemTouchHelper;
@@ -27,6 +19,7 @@ import com.majinnaibu.monstercards.data.MonsterRepository;
import com.majinnaibu.monstercards.models.Monster; import com.majinnaibu.monstercards.models.Monster;
import com.majinnaibu.monstercards.ui.MCFragment; import com.majinnaibu.monstercards.ui.MCFragment;
import com.majinnaibu.monstercards.ui.MonsterListRecyclerViewAdapter; import com.majinnaibu.monstercards.ui.MonsterListRecyclerViewAdapter;
import com.majinnaibu.monstercards.ui.shared.SwipeToDeleteCallback;
import com.majinnaibu.monstercards.utils.Logger; import com.majinnaibu.monstercards.utils.Logger;
import java.util.UUID; import java.util.UUID;
@@ -41,30 +34,10 @@ public class LibraryFragment extends MCFragment {
View root = inflater.inflate(R.layout.fragment_library, container, false); View root = inflater.inflate(R.layout.fragment_library, container, false);
FloatingActionButton fab = root.findViewById(R.id.fab); FloatingActionButton fab = root.findViewById(R.id.fab);
fab.setOnClickListener(view -> { assert fab != null;
Monster monster = new Monster(); setupAddMonsterButton(fab);
monster.name = "Unnamed Monster";
MonsterRepository repository = this.getMonsterRepository();
repository.addMonster(monster)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(() -> {
Snackbar.make(
getView(),
String.format("%s created", monster.name),
Snackbar.LENGTH_LONG)
.setAction("Action", (_view) -> {
navigateToMonsterDetail(monster.id);
})
.show();
}, throwable -> {
Logger.logError("Error creating monster", throwable);
Snackbar.make(getView(), "Failed to create monster", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
});
});
RecyclerView recyclerView = root.findViewById(R.id.monster_list); final RecyclerView recyclerView = root.findViewById(R.id.monster_list);
assert recyclerView != null; assert recyclerView != null;
setupRecyclerView(recyclerView); setupRecyclerView(recyclerView);
@@ -89,69 +62,40 @@ public class LibraryFragment extends MCFragment {
mTwoPane); mTwoPane);
recyclerView.setAdapter(adapter); recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteMonsterCallback(adapter)); ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(getContext(), (position) -> {
adapter.deleteItem(position);
}));
itemTouchHelper.attachToRecyclerView(recyclerView); itemTouchHelper.attachToRecyclerView(recyclerView);
} }
private void setupAddMonsterButton(@NonNull FloatingActionButton fab) {
fab.setOnClickListener(view -> {
Monster monster = new Monster();
monster.name = "Unnamed Monster";
MonsterRepository repository = this.getMonsterRepository();
repository.addMonster(monster)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(() -> {
Snackbar.make(
getView(),
String.format("%s created", monster.name),
Snackbar.LENGTH_LONG)
.setAction("Action", (_view) -> {
navigateToMonsterDetail(monster.id);
})
.show();
}, throwable -> {
Logger.logError("Error creating monster", throwable);
Snackbar.make(getView(), "Failed to create monster", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
});
});
}
protected void navigateToMonsterDetail(UUID monsterId) { protected void navigateToMonsterDetail(UUID monsterId) {
NavDirections action = LibraryFragmentDirections.actionNavigationLibraryToNavigationMonster(monsterId.toString()); NavDirections action = LibraryFragmentDirections.actionNavigationLibraryToNavigationMonster(monsterId.toString());
Navigation.findNavController(getView()).navigate(action); Navigation.findNavController(getView()).navigate(action);
} }
public static class SwipeToDeleteMonsterCallback extends ItemTouchHelper.SimpleCallback {
private final MonsterListRecyclerViewAdapter mAdapter;
private final Drawable icon;
private final ColorDrawable background;
private final Paint clearPaint;
public SwipeToDeleteMonsterCallback(MonsterListRecyclerViewAdapter adapter) {
super(0, ItemTouchHelper.LEFT);
mAdapter = adapter;
icon = ContextCompat.getDrawable(mAdapter.getContext(), R.drawable.ic_delete_white_36);
background = new ColorDrawable(Color.RED);
clearPaint = new Paint();
clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
@Override
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
return false;
}
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
mAdapter.deleteItem(position);
}
@Override
public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
View itemView = viewHolder.itemView;
int itemHeight = itemView.getBottom() - itemView.getTop();
boolean isCancelled = dX == 0 && !isCurrentlyActive;
if (isCancelled) {
c.drawRect(itemView.getRight() + dX, itemView.getTop(), itemView.getRight(), itemView.getBottom(), clearPaint);
return;
}
// Draw the red delete background
background.setBounds(itemView.getRight() + (int) dX, itemView.getTop(), itemView.getRight(), itemView.getBottom());
background.draw(c);
// Calculate position of delete icon
int iconHeight = icon.getIntrinsicHeight();
int iconWidth = icon.getIntrinsicWidth();
int iconTop = itemView.getTop() + (itemHeight - iconHeight) / 2;
int iconMargin = (itemHeight - iconHeight) / 2;
int iconLeft = itemView.getRight() - iconMargin - iconWidth;
int iconRight = itemView.getRight() - iconMargin;
int iconBottom = iconTop + iconHeight;
// Draw the icon
icon.setBounds(iconLeft, iconTop, iconRight, iconBottom);
icon.draw(c);
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
}
} }

View File

@@ -0,0 +1,87 @@
package com.majinnaibu.monstercards.ui.shared;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import com.majinnaibu.monstercards.R;
public class SwipeToDeleteCallback extends ItemTouchHelper.SimpleCallback {
public interface OnDeleteCallback {
void onDelete(int position);
}
private final Drawable icon;
private final ColorDrawable background;
private final Paint clearPaint;
private final OnDeleteCallback mOnDelete;
private final Context mContext;
public SwipeToDeleteCallback(Context context, OnDeleteCallback onDelete) {
super(0, ItemTouchHelper.LEFT);
mOnDelete = onDelete;
mContext = context;
icon = ContextCompat.getDrawable(mContext, R.drawable.ic_delete_white_36);
background = new ColorDrawable(context.getResources().getColor(R.color.red));
clearPaint = new Paint();
clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
@Override
public boolean onMove(
@NonNull RecyclerView recyclerView,
@NonNull RecyclerView.ViewHolder viewHolder,
@NonNull RecyclerView.ViewHolder target
) {
return false;
}
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
if (mOnDelete != null) {
int position = viewHolder.getAdapterPosition();
mOnDelete.onDelete(position);
}
}
@Override
public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
View itemView = viewHolder.itemView;
int itemHeight = itemView.getBottom() - itemView.getTop();
boolean isCancelled = dX == 0 && !isCurrentlyActive;
if (isCancelled) {
c.drawRect(itemView.getRight() + dX, itemView.getTop(), itemView.getRight(), itemView.getBottom(), clearPaint);
return;
}
// Draw the red delete background
background.setBounds(itemView.getRight() + (int) dX, itemView.getTop(), itemView.getRight(), itemView.getBottom());
background.draw(c);
// Calculate position of delete icon
int iconHeight = icon.getIntrinsicHeight();
int iconWidth = icon.getIntrinsicWidth();
int iconTop = itemView.getTop() + (itemHeight - iconHeight) / 2;
int iconMargin = (itemHeight - iconHeight) / 2;
int iconLeft = itemView.getRight() - iconMargin - iconWidth;
int iconRight = itemView.getRight() - iconMargin;
int iconBottom = iconTop + iconHeight;
// Draw the icon
icon.setBounds(iconLeft, iconTop, iconRight, iconBottom);
icon.draw(c);
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
}

View File

@@ -4,4 +4,14 @@
<color name="colorPrimaryDark">#661A10</color> <color name="colorPrimaryDark">#661A10</color>
<!-- <color name="colorAccent">#188B9B</color>--> <!-- <color name="colorAccent">#188B9B</color>-->
<color name="colorAccent">#995500</color> <color name="colorAccent">#995500</color>
<!-- fruit system colors -->
<color name="red">#FF3B30</color>
<color name="orange">#FF9500</color>
<color name="yellow">#FFCC00</color>
<color name="green">#4CD964</color>
<color name="tealBlue">#5AC8FA</color>
<color name="blue">#007AFF</color>
<color name="purple">#5855D6</color>
<color name="pink">#FF2D55</color>
</resources> </resources>