Well, at the beginning of the Implementation of ItemTouchHelper
, I chose to use only the Swipe function from it. Everything worked - with the help of Swipe I could delete items in RecyclerView
etc. But then I thought I'd add up-down movement so that the user could change the order in which the item appears in the list.
Then the problems began - the user can change the order in which items are displayed, but can no longer:
RecyclerView
(even if changing item up-down position is disabled)onSwiped
does not return the side (ItemTouchHelper.LEFT, ItemTouchHelper.RIGHT
) in which the item was swipedI changed the View on which is trigger OnStartDragListener from _view to just ImageView and I can scroll now but there are now other problems:
onSwiped
does not return the side (ItemTouchHelper.LEFT, ItemTouchHelper.RIGHT
) in which the item was swipedFIXED
I Changed
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int dragFlags =
mDraggable
? ItemTouchHelper.UP
| ItemTouchHelper.DOWN
| ItemTouchHelper.START
| ItemTouchHelper.END
: 0;
if (mDraggable) {
dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
} else {
dragFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
}
return makeMovementFlags(dragFlags, 0);
}
To
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int dragFlags = 0;
int swipeFlags = 0;
if (mDraggable) {
dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
return makeMovementFlags(dragFlags, 0);
} else {
swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(0, swipeFlags);
}
}
RecyclerView Adapter:
package tw.codeassist.plus;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.Typeface;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.core.view.MotionEventCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.google.gson.Gson;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import tw.codeassist.plus.SwipeToDeleteCallback;
public class CodeAdapter extends RecyclerView.Adapter<CodeAdapter.ViewHolder> implements SwipeToDeleteCallback.ItemTouchHelperAdapter {
ArrayList<HashMap<String, Object>> _data;
Context context;
SharedPreferences snippets;
OnItemClickListener mItemClickListener;
private static final int TYPE_ITEM = 0;
//private final LayoutInflater mInflater;
private final tw.codeassist.plus.SwipeToDeleteCallback.OnStartDragListener mDragStartListener;
public CodeAdapter(
android.content.Context appCont,
ArrayList<HashMap<String, Object>> _arr,
tw.codeassist.plus.SwipeToDeleteCallback.OnStartDragListener dragListner) {
_data = _arr;
context = appCont;
mDragStartListener = dragListner;
snippets = context.getSharedPreferences("snippets", Activity.MODE_PRIVATE);
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater _inflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View _v = _inflater.inflate(R.layout.codesnippets, null);
RecyclerView.LayoutParams _lp =
new RecyclerView.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
_v.setLayoutParams(_lp);
return new ViewHolder(_v);
}
@Override
public void onBindViewHolder(ViewHolder _holder, final int _position) {
View _view = _holder.itemView;
final androidx.cardview.widget.CardView cardview1 = _view.findViewById(R.id.cardview1);
final LinearLayout main_linear = (LinearLayout) _view.findViewById(R.id.main_linear);
final LinearLayout color_linear = (LinearLayout) _view.findViewById(R.id.color_linear);
final LinearLayout linear3 = (LinearLayout) _view.findViewById(R.id.linear3);
final TextView title_txt = (TextView) _view.findViewById(R.id.title_txt);
final TextView desc_txt = (TextView) _view.findViewById(R.id.desc_txt);
title_txt.setTypeface(
Typeface.createFromAsset(context.getAssets(), "fonts/product_sans_bold.ttf"), 0);
desc_txt.setTypeface(
Typeface.createFromAsset(context.getAssets(), "fonts/product_sans_regular.ttf"), 0);
title_txt.setText(_data.get((int) _position).get("Title").toString());
desc_txt.setText(_data.get((int) _position).get("Description").toString());
color_linear.setBackgroundColor(
Color.parseColor(_data.get((int) _position).get("Color").toString()));
RecyclerView.LayoutParams _lp =
new RecyclerView.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
_view.setLayoutParams(_lp);
_view.setOnTouchListener(
new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
mDragStartListener.onStartDrag(_holder);
}
return false;
}
});
}
@Override
public int getItemCount() {
return _data.size();
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, ItemTouchHelperViewHolder {
public ViewHolder(View v) {
super(v);
v.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (mItemClickListener != null) {
mItemClickListener.onItemClick(v, getAdapterPosition());
}
}
@Override
public void onItemSelected() {
itemView.setBackgroundColor(Color.LTGRAY);
}
@Override
public void onItemClear() {
itemView.setBackgroundColor(0);
}
}
@Override
public void onItemDismiss(int position) {
_data.remove(position);
notifyItemRemoved(position);
}
@Override
public boolean onItemMove(int fromPosition, int toPosition) {
Log.d("onItemMove", "Log position" + fromPosition + " " + toPosition);
if (fromPosition < _data.size() && toPosition < _data.size()) {
if (fromPosition < toPosition) {
for (int i = fromPosition; i < toPosition; i++) {
Collections.swap(_data, i, i + 1);
}
} else {
for (int i = fromPosition; i > toPosition; i--) {
Collections.swap(_data, i, i - 1);
}
}
Collections.reverse(_data);
snippets.edit().putString("CodeSnippets", new Gson().toJson(this._data)).commit();
notifyItemMoved(fromPosition, toPosition);
}
return true;
}
public void showMessage(String message) {
Toast.makeText((Activity) context, message, Toast.LENGTH_SHORT).show();
}
public void updateList(ArrayList<HashMap<String, Object>> list) {
_data = list;
notifyDataSetChanged();
}
public interface OnItemClickListener {
public void onItemClick(View view, int position);
}
public void setOnItemClickListener(final OnItemClickListener mItemClickListener) {
this.mItemClickListener = mItemClickListener;
}
public void removeAt(int position) {
_data.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, _data.size());
}
public interface ItemTouchHelperViewHolder {
void onItemSelected();
void onItemClear();
}
}
ItemTouchHelper Callback
package tw.codeassist.plus;
import android.app.Activity;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.util.DisplayMetrics;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.Toast;
import androidx.fragment.app.FragmentActivity;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import com.google.gson.Gson;
public class SwipeToDeleteCallback extends ItemTouchHelper.Callback {
private CodeAdapter mAdapter;
Context context;
private SharedPreferences snippets;
private Drawable icon;
private Drawable icon2;
private GradientDrawable background; // ColorDrawable Before
boolean mDraggable = true;
public SwipeToDeleteCallback(Context con, CodeAdapter adapter, boolean canEdit) {
mAdapter = adapter;
context = con;
setDraggable(canEdit);
DisplayMetrics dm = new DisplayMetrics();
((Activity) con).getWindowManager().getDefaultDisplay().getMetrics(dm);
float densityScale = dm.density;
// DELETE ICON
Drawable dr = con.getResources().getDrawable(R.drawable.ic_delete);
Bitmap bitmap = ((BitmapDrawable) dr).getBitmap();
icon =
new BitmapDrawable(
con.getResources(),
Bitmap.createScaledBitmap(
bitmap,
(int) (150 * densityScale / 3),
(int) (150 * densityScale / 3),
true));
// COPY ICON
Drawable dr2 = con.getResources().getDrawable(R.drawable.ic_copy);
Bitmap bitmap2 = ((BitmapDrawable) dr2).getBitmap();
icon2 =
new BitmapDrawable(
con.getResources(),
Bitmap.createScaledBitmap(
bitmap2,
(int) (200 * densityScale / 3),
(int) (200 * densityScale / 3),
true));
// TO INIT THE background
int startColor = Color.WHITE;
int endColor = Color.RED;
background =
new GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT, new int[] {startColor, endColor});
background.setCornerRadius(20);
}
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int dragFlags =
mDraggable
? ItemTouchHelper.UP
| ItemTouchHelper.DOWN
| ItemTouchHelper.START
| ItemTouchHelper.END
: 0;
if (mDraggable) {
dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
} else {
dragFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
}
return makeMovementFlags(dragFlags, 0);
}
public void setDraggable(boolean value) {
mDraggable = value;
}
@Override
public boolean isLongPressDragEnabled() {
return true;
}
@Override
public boolean onMove(
RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder,
RecyclerView.ViewHolder target) {
if (mDraggable) {
mAdapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
return true;
} else {
return false;
}
}
void showMessage(String message) {
Toast.makeText(((Activity) context), message, Toast.LENGTH_SHORT).show();
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
showMessage(String.valueOf(direction));
if (direction == 8) { // RIGHT TO LEFT
// copy
VibrateNotify(200);
String getCode = mAdapter._data.get((int) position).get("Code").toString();
CharSequence code = getCode;
LinearLayout linearSnack =
(LinearLayout) ((FragmentActivity) context).findViewById(R.id.linear1);
((ClipboardManager) context.getSystemService(context.CLIPBOARD_SERVICE))
.setPrimaryClip(ClipData.newPlainText("", code));
com.google.android.material.snackbar.Snackbar.make(
context,
linearSnack,
"Code Copied!",
com.google.android.material.snackbar.Snackbar.LENGTH_LONG)
.setAction(
"",
new View.OnClickListener() {
@Override
public void onClick(View _view) {}
})
.show();
mAdapter.notifyItemChanged(position);
} else if (direction == 4) { // 4 = LEFT TO RIGHT
// delete
mAdapter.removeAt(position);
VibrateNotify(500);
snippets = context.getSharedPreferences("snippets", context.MODE_PRIVATE);
snippets.edit().putString("CodeSnippets", new Gson().toJson(mAdapter._data)).commit();
mAdapter.notifyItemRemoved(position);
}
}
public void VibrateNotify(long duration) {
Vibrator v = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
// Vibrate for 500 milliseconds
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
v.vibrate(VibrationEffect.createOneShot(duration, VibrationEffect.EFFECT_DOUBLE_CLICK));
} else {
// deprecated in API 26
v.vibrate(duration);
}
}
@Override
public void onChildDraw(
Canvas c,
RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder,
float dX,
float dY,
int actionState,
boolean isCurrentlyActive) {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
View itemView = viewHolder.itemView;
int backgroundCornerOffset = 15; // so background is behind the rounded corners of itemView
int alpha = 128;
int colorGreen = Color.parseColor("#FF0000FF");
icon.setAlpha(alpha);
icon2.setAlpha(alpha);
int iconMargin = (itemView.getHeight() - icon.getIntrinsicHeight()) / 3;
int iconTop = itemView.getTop() + (itemView.getHeight() - icon.getIntrinsicHeight()) / 2;
int iconBottom = iconTop + icon.getIntrinsicHeight();
if (dX > 0) { // Swiping to the right
int iconLeft = itemView.getLeft() + iconMargin; // icon.getIntrinsicWidth()
int iconRight = iconLeft + icon.getIntrinsicWidth(); // itemView.getLeft()
icon2.setBounds(iconLeft, iconTop, iconRight, iconBottom);
int START = 0xFF90A4AE;
int END = Color.TRANSPARENT;
background =
new GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT, new int[] {START, END});
background.setCornerRadius(20);
background.setBounds(
itemView.getLeft(),
itemView.getTop(),
itemView.getLeft() + ((int) dX) + backgroundCornerOffset,
itemView.getBottom());
} else if (dX < 0) { // Swiping to the left
int iconLeft = itemView.getRight() - iconMargin - icon.getIntrinsicWidth();
int iconRight = itemView.getRight() - iconMargin;
icon.setBounds(iconLeft, iconTop, iconRight, iconBottom);
int START = Color.TRANSPARENT;
int END = 0xFFE57373;
background =
new GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT, new int[] {START, END});
background.setCornerRadius(20);
background.setBounds(
itemView.getRight() + ((int) dX) - backgroundCornerOffset,
itemView.getTop(),
itemView.getRight(),
itemView.getBottom());
} else { // view is unSwiped
icon.setBounds(0, 0, 0, 0);
icon2.setBounds(0, 0, 0, 0);
background.setBounds(0, 0, 0, 0);
}
background.setStroke(25, Color.TRANSPARENT);
// background.setPadding(0, paddingTop, 0, paddingBottom); //left, top, right, bottom
background.draw(c);
icon.draw(c);
icon2.draw(c);
}
public interface OnStartDragListener {
void onStartDrag(RecyclerView.ViewHolder viewHolder);
}
public interface ItemTouchHelperAdapter {
boolean onItemMove(int fromPosition, int toPosition);
void onItemDismiss(int position);
}
}
Fragment where the RecyclerView is:
package tw.codeassist.plus;
import android.animation.*;
import android.app.*;
import android.app.Activity;
import android.content.*;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.*;
import android.graphics.*;
import android.graphics.Typeface;
import android.graphics.drawable.*;
import android.media.*;
import android.net.*;
import android.os.*;
import android.text.*;
import android.text.style.*;
import android.util.*;
import android.view.*;
import android.view.View;
import android.view.View.*;
import android.view.animation.*;
import android.webkit.*;
import android.widget.*;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.*;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.*;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import com.github.angads25.filepicker.*;
import com.github.dhaval2404.colorpicker.*;
import com.google.android.flexbox.*;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.zip4j.*;
import io.github.rosemoe.sora.*;
import java.io.*;
import java.text.*;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.regex.*;
import org.json.*;
public class TutorialsFragmentActivity extends Fragment
implements SwipeToDeleteCallback.OnStartDragListener {
private FloatingActionButton _fab;
private CodeAdapter codeAdapter;
private ArrayList<HashMap<String, Object>> dataToShow = new ArrayList<>();
public LinearLayout linear1;
private TextView textview1;
private TextView goneTxt;
private TextView editBttn;
private RecyclerView recyclerView;
private LinearLayout noCodesView;
TutorialsFragmentActivity tfa = this;
private SharedPreferences.OnSharedPreferenceChangeListener listener;
private Intent intent = new Intent();
private SharedPreferences snippets;
private SharedPreferences settings;
private ItemTouchHelper itemTouchHelper;
boolean isEditMode = false;
@NonNull
@Override
public View onCreateView(
@NonNull LayoutInflater _inflater,
@Nullable ViewGroup _container,
@Nullable Bundle _savedInstanceState) {
View _view = _inflater.inflate(R.layout.tutorials_fragment, _container, false);
initialize(_savedInstanceState, _view);
com.google.firebase.FirebaseApp.initializeApp(getContext());
initializeLogic();
return _view;
}
private void initialize(Bundle _savedInstanceState, View _view) {
_fab = _view.findViewById(R.id._fab);
settings = getContext().getSharedPreferences("settings", Activity.MODE_PRIVATE);
noCodesView = _view.findViewById(R.id.linearNoCodes);
linear1 = _view.findViewById(R.id.linear1);
goneTxt = _view.findViewById(R.id.txGone);
editBttn = _view.findViewById(R.id.edit);
textview1 = _view.findViewById(R.id.textview1);
recyclerView = _view.findViewById(R.id.recyclerView);
snippets = getContext().getSharedPreferences("snippets", Activity.MODE_PRIVATE);
listener =
new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
if (key.equals("CodeSnippets")) {
if (settings.getString("ShouldRefresh", "") == "true") {
itemTouchHelper.attachToRecyclerView(null);
dataToShow =
new Gson()
.fromJson(
snippets.getString("CodeSnippets", ""),
new TypeToken<
ArrayList<
HashMap<
String,
Object>>>() {}.getType());
Collections.reverse(dataToShow);
codeAdapter = new CodeAdapter(getContext(), dataToShow, tfa);
recyclerView.setAdapter(codeAdapter);
//recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
itemTouchHelper =
new ItemTouchHelper(
new SwipeToDeleteCallback(
getContext(), codeAdapter, false));
itemTouchHelper.attachToRecyclerView(recyclerView);
settings.edit().putString("ShouldRefresh", "false").commit();
} else {
}
}
}
};
snippets.registerOnSharedPreferenceChangeListener(listener);
goneTxt.setVisibility(View.INVISIBLE);
editBttn.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View _view) {
if (!isEditMode) {
isEditMode = true;
editBttn.setText("Close");
goneTxt.setText("Close");
} else {
isEditMode = false;
editBttn.setText("Edit");
goneTxt.setText("Edit");
}
itemTouchHelper.attachToRecyclerView(null);
itemTouchHelper =
new ItemTouchHelper(
new SwipeToDeleteCallback(
getContext(), codeAdapter, isEditMode));
itemTouchHelper.attachToRecyclerView(recyclerView);
}
});
_fab.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View _view) {
editBttn.setText("Edit");
goneTxt.setText("Edit");
isEditMode = false;
itemTouchHelper.attachToRecyclerView(null);
itemTouchHelper =
new ItemTouchHelper(
new SwipeToDeleteCallback(
getContext(), codeAdapter, isEditMode));
itemTouchHelper.attachToRecyclerView(recyclerView);
intent.setClass(
getContext().getApplicationContext(), AddSnippetActivity.class);
startActivity(intent);
}
});
}
@Override
public void onResume() {
}
@Override
public void onStart() {
super.onStart();
}
public void onPause() {
super.onPause();
}
private void initializeLogic() {
{
android.graphics.drawable.GradientDrawable SketchUi =
new android.graphics.drawable.GradientDrawable();
int clrs[] = {0xFFFFFFFF, 0xFFE0E0E0};
SketchUi =
new android.graphics.drawable.GradientDrawable(
android.graphics.drawable.GradientDrawable.Orientation.TOP_BOTTOM,
clrs);
linear1.setBackground(SketchUi);
}
noCodesView.setVisibility(View.GONE);
textview1.setTypeface(
Typeface.createFromAsset(getContext().getAssets(), "fonts/product_sans_bold.ttf"),
0);
dataToShow =
new Gson()
.fromJson(
snippets.getString("CodeSnippets", ""),
new TypeToken<ArrayList<HashMap<String, Object>>>() {}.getType());
Collections.reverse(dataToShow);
codeAdapter = new CodeAdapter(this.getContext(), dataToShow, this);
recyclerView.setAdapter(codeAdapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
itemTouchHelper =
new ItemTouchHelper(new SwipeToDeleteCallback(getContext(), codeAdapter, false));
itemTouchHelper.attachToRecyclerView(recyclerView);
removeScrollBar(recyclerView);
if (dataToShow.size() == 0) {
noCodesView.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
} else {
noCodesView.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
}
}
public void removeScrollBar(View _view) {
_view.setVerticalScrollBarEnabled(false);
_view.setHorizontalScrollBarEnabled(false);
}
@Override
public void onStartDrag(RecyclerView.ViewHolder viewHolder) {
itemTouchHelper.startDrag(viewHolder);
}
}
FIXED
I Changed
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int dragFlags =
mDraggable
? ItemTouchHelper.UP
| ItemTouchHelper.DOWN
| ItemTouchHelper.START
| ItemTouchHelper.END
: 0;
if (mDraggable) {
dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
} else {
dragFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
}
return makeMovementFlags(dragFlags, 0);
}
To
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int dragFlags = 0;
int swipeFlags = 0;
if (mDraggable) {
dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
return makeMovementFlags(dragFlags, 0);
} else {
swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(0, swipeFlags);
}
}