0

In implementing swipe-to-delete functionality I accidentally made my forground view of the viewholder transparent. In doing so I've uncovered a bug with my ItemTouchHandler not changing the background or hiding the text when it is supposed to.

It appears to me that my ItemTouchHelper is not being applied to every view in my ReycleViewAdapter and/or the views are not being updated or they all share a single parent of some sort, after many hours of attempted debugging I'm at my wit's end, I've included a handy gif of the issue.

The ItemTouchHelper's onDrawChild() and onDrawChildOver() methods are passed to my fragment in order to handle the actions

bug example

Code:

AllLists_fragment.java:

public class showAllLists_fragment extends android.support.v4.app.Fragment implements RecycleItemTouchHelper.RecyclerItemTouchHelperListener {

    private static final String TAG = "showAllLists";


    private RecyclerView mAllItemsView = null;
    private DataCommunication mData = null;
    private ItemTouchHelper.SimpleCallback itemTouchHelper = null;;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);
        View view = inflater.inflate(R.layout.show_all_lists_frag, container, false);
        this.mAllItemsView = (RecyclerView) view.findViewById(R.id.allItems);
        this.itemTouchHelper = new RecycleItemTouchHelper(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT, this);
        new ItemTouchHelper(this.itemTouchHelper).attachToRecyclerView(this.mAllItemsView);
        this.mAllItemsView.setAdapter(this.mData.getAdapter());
        this.mAllItemsView.setLayoutManager(new LinearLayoutManager(getActivity()){
            //
            @Override
            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
                try {
                    super.onLayoutChildren(recycler, state);
                } catch (IndexOutOfBoundsException e) {
                    Log.d(TAG, "Inconsistent Exception Caught -See stack trace");
                    e.printStackTrace();
                }
            }
        });
        viewShadow();

        return view;
    }

    @TargetApi(21)
    public void viewShadow(){
        this.mAllItemsView.setTranslationZ(100);
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            this.mData = (DataCommunication) context;
        } catch (ClassCastException e) {
            throw new ClassCastException(context.toString()
                    + " must implement DataCommunication");
        }
    }



    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if(isVisibleToUser){
            this.mData.getAdapter().notifyDataSetChanged();
            this.mData.refresh();

        }
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction, int position) {
        int noSwipeSize = mData.getAllToDo().size();
        if (direction == ItemTouchHelper.LEFT) {

            mData.deleteTask(mData.getAdapter().getResults().get(viewHolder.getAdapterPosition()));
            mData.getAdapter().notifyItemRangeRemoved(viewHolder.getAdapterPosition(), noSwipeSize);


        } else if (direction == ItemTouchHelper.RIGHT) {
            mData.completeTask(mData.getAdapter().getResults().get(viewHolder.getAdapterPosition()));
            mData.getAdapter().notifyItemRangeRemoved(viewHolder.getAdapterPosition(), noSwipeSize);

        }
    }

    @Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView, ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
        final View foregroundView = ((com.lab1.ac01220.bloomv2.ViewHolder) viewHolder).getForeground();
        if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
            if(dX < 0){
                this.mData.getViewHolder().getDeleteText().setVisibility(View.VISIBLE);
                this.mData.getViewHolder().getCompleteText().setVisibility(View.INVISIBLE);
                this.mData.getViewHolder().getBackground().setBackgroundColor(getResources().getColor(R.color.colorAccent));
            } else if(dX >0){
                this.mData.getViewHolder().getCompleteText().setVisibility(View.VISIBLE);
                this.mData.getViewHolder().getDeleteText().setVisibility(View.INVISIBLE);
                this.mData.getViewHolder().getBackground().setBackgroundColor(getResources().getColor(R.color.colorComplete));
            }
        }
        ItemTouchHelper.SimpleCallback.getDefaultUIUtil().onDraw(c, recyclerView, foregroundView, dX, dY,actionState, isCurrentlyActive);
    }

    @Override
    public void onChildDrawOver(Canvas c, RecyclerView recyclerView, ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
        final View foregroundView = ((com.lab1.ac01220.bloomv2.ViewHolder) viewHolder).getForeground();

        ItemTouchHelper.SimpleCallback.getDefaultUIUtil().onDrawOver(c, recyclerView, foregroundView, dX, dY,actionState, isCurrentlyActive);
        }



    }

RecycleitemTouchHelper.java

    public RecycleItemTouchHelper(int dragDirs, int swipeDirs, RecyclerItemTouchHelperListener listener) {
        super(dragDirs, swipeDirs);
        this.listener = listener;
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        return false;
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        this.listener.onSwiped(viewHolder, direction, viewHolder.getAdapterPosition());
    }

    @Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {

        this.listener.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);


    }

    @Override
    public void onChildDrawOver(Canvas c, RecyclerView recyclerView,
                                RecyclerView.ViewHolder viewHolder, float dX, float dY,
                                int actionState, boolean isCurrentlyActive) {
        this.listener.onChildDrawOver(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
    }

    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        final View foregroundView = ((ViewHolder) viewHolder).getForeground();
        getDefaultUIUtil().clearView(foregroundView);
    }

    @Override
    public int convertToAbsoluteDirection(int flags, int layoutDirection) {
        return super.convertToAbsoluteDirection(flags, layoutDirection);
    }


    public interface RecyclerItemTouchHelperListener {
        void onSwiped(RecyclerView.ViewHolder viewHolder, int direction, int position);

        void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive);
        void onChildDrawOver(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive);
    }
}

RecycleViewAdapter.java:

public class RecycleViewAdapter extends RecyclerView.Adapter<ViewHolder>{

    private LayoutInflater inflater = null;
    private List<GlobalLists> results = null;
    private DataCommunication mData = null;
    public RecycleViewAdapter(Context context, List<GlobalLists> results){
        this.inflater = LayoutInflater.from(context);
        this.results = results;
        this.mData = (DataCommunication)context;
    }



    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = inflater.inflate(R.layout.custom_row, parent, false);
        ViewHolder holder = new ViewHolder(view);
        this.mData.setViewHolder(holder);
        return holder;
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        GlobalLists item = this.results.get(position);
        holder.setRowTitle(item.getTitle());
        //Grab the first 14 characters of the contents if the row is not null
        if(item.getContents() != null){
            StringBuffer currentContent = new StringBuffer(item.getContents().substring(0, Math.min(item.getContents().length(),28)));
            if(item.getContents().length() > 28) currentContent.append("...");
            holder.setRowContent(currentContent.toString());
        }


    }

    public void reloadAdapterData(List<GlobalLists> refresh){
        this.results = refresh;
    }

    @Override
    public int getItemCount() {
        return this.results == null || this.results.isEmpty() ? 0 : this.results.size();
    }

    public  List<GlobalLists> getResults(){
        return this.results;
    }



}
Adrian Coutsoftides
  • 1,203
  • 1
  • 16
  • 38

1 Answers1

0

The problem was that I was simply setting the first ViewHolder to be a global variable and only anacting changes on that. Instead I should have been getting the current View being draw in onChildDraw and performing functions on that:

Was:

 @Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView, ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
        final View foregroundView = ((com.lab1.ac01220.bloomv2.ViewHolder) viewHolder).getForeground();
        if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
            if(dX < 0){
                this.mData.getViewHolder().getDeleteText().setVisibility(View.VISIBLE);
                this.mData.getViewHolder().getCompleteText().setVisibility(View.INVISIBLE);
                this.mData.getViewHolder().getBackground().setBackgroundColor(getResources().getColor(R.color.colorAccent));
            } else if(dX >0){
                this.mData.getViewHolder().getCompleteText().setVisibility(View.VISIBLE);
                this.mData.getViewHolder().getDeleteText().setVisibility(View.INVISIBLE);
                this.mData.getViewHolder().getBackground().setBackgroundColor(getResources().getColor(R.color.colorComplete));
            }
        }
        ItemTouchHelper.SimpleCallback.getDefaultUIUtil().onDraw(c, recyclerView, foregroundView, dX, dY,actionState, isCurrentlyActive);
    }

Should be:

@Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView, ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
        com.lab1.ac01220.bloomv2.ViewHolder current = (com.lab1.ac01220.bloomv2.ViewHolder) viewHolder;
        final View foregroundView = ((com.lab1.ac01220.bloomv2.ViewHolder) viewHolder).getForeground();
        if (isCurrentlyActive) {
            if(dX < 0){
                current.getDeleteText().setVisibility(View.VISIBLE);
                current.getCompleteText().setVisibility(View.INVISIBLE);
                current.getBackground().setBackground(getResources().getDrawable(R.drawable.ic_rounded_rectangle_accent));
            } else if(dX >0){
                current.getCompleteText().setVisibility(View.VISIBLE);
                current.getDeleteText().setVisibility(View.INVISIBLE);
                current.getBackground().setBackground(getResources().getDrawable(R.drawable.ic_rounded_rectangle_complete));
            }
        }
        ItemTouchHelper.SimpleCallback.getDefaultUIUtil().onDraw(c, recyclerView, foregroundView, dX, dY,actionState, isCurrentlyActive);
    }
Adrian Coutsoftides
  • 1,203
  • 1
  • 16
  • 38