Chapter 17 - Adding swipe to dismiss

Here’s my solution to the challenge in chapter 17.

Start with the following method to add the ItemTouchHelper to mCrimeRecyclerView in
CrimeListFragment.java.

// Challenge: Adding Swipe to Dismiss.
public void setCrimeRecyclerViewItemTouchListener() {
    ItemTouchHelper.SimpleCallback itemTouchCallback =
            new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT) {
                @Override
                public boolean onMove(RecyclerView recyclerView,
                                      RecyclerView.ViewHolder viewHolder,
                                      RecyclerView.ViewHolder target) {
                    return false;
                }

                @Override
                public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
                int position = viewHolder.getAdapterPosition();
		Crime crime = mAdapter.mCrimes.get(position);
		mDeleteCallBack.onCrimeIdSelected(crime.getId());
                }
            };

    ItemTouchHelper itemTouchHelper = new ItemTouchHelper(itemTouchCallback);
    itemTouchHelper.attachToRecyclerView(mCrimeRecyclerView);
}

I then declared mDeleteCallback in CrimeListFragment.java.

private OnDeleteCrimeListener mDeleteCallBack;

I made sure setCrimeRecyclerViewItemTouchListener() is called in onCreateView in
CrimeListFragment.java.

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    ...
    setCrimeRecyclerViewItemTouchListener();
....
    return view;
}

Here is the code for mDeleteCallBack.onCrimeIdSelected(crime.getId()) which is
found in CrimeListActivity.java.

public void onCrimeIdSelected(UUID crimeId) {
CrimeListFragment listFragment = (CrimeListFragment)
getSupportFragmentManager()
.findFragmentById(R.id.fragment_container);
listFragment.deleteCrime(crimeId);
listFragment.updateUI();

    FragmentManager fm = getSupportFragmentManager();
    fm.beginTransaction()
            .hide(newDetail)
            .commit();
}

I think, u missed two methods. But I have to thank you because I passed the challenge in the book perfectly.
And here is my code that I changed your code.

in CrimeListFragment
First of all, I defined the callbacks for OnDeleteCrimeListener and edit my onAttach and onDetach method.

      private OnDeleteCrimeListener mDeleteCalLBack;

     public interface OnDeleteCrimeListener {
            void onCrimeIdSelected(UUID crimeId);
        }
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {


    View v = inflater.inflate(R.layout.crime_activity_list, container, false);

    mRecyclerView = (RecyclerView) v.findViewById(R.id.frecycle_view);
    mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
    mRecyclerView.setHasFixedSize(true);
    setCrimeRecyclerViewItemTouchListener();

    return v;
    }
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        mCallback = (Callbacks) context;
        mDeleteCalLBack = (OnDeleteCrimeListener) context;
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mCallback = null;
        mDeleteCalLBack = null;
    }

Then, I created a method that I gonna use it in CrimeListActivitiy later.
Because, I am using the sqlLite.

public void deleteCrime(UUID crimeId) {
            Crime crime = CrimeLab.get(getActivity()).getCrime(crimeId);
            CrimeLab.get(getActivity()).deleteCrime(crimeDec);
        }

then I used your code.

 public void setCrimeRecyclerViewItemTouchListener() {

    ItemTouchHelper.SimpleCallback itemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
        @Override
        public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
            return false;
        }

        @Override
        public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
            int position = viewHolder.getAdapterPosition();
            Crime crime= mAdapter.mCrimeList.get(position);
            Log.d(TAG, "onSwiped: " + crime.getId());
            mDeleteCalLBack.onCrimeIdSelected(crime.getId());

        }
    };

    ItemTouchHelper iteItemTouchHelper = new ItemTouchHelper(itemTouchCallback);
    iteItemTouchHelper.attachToRecyclerView(mRecyclerView);
}

Finally, in CrimeListActivity, I implemented this.
CrimeListFragment.OnDeleteCrimeListener
Then,
UPDATE 07.12.17

   @Override
    public void onCrimeIdSelected(UUID crimeId) {
 
 CrimeFragment crimeFragment = (CrimeFragment) getSupportFragmentManager().findFragmentById(R.id.detail_fragment_container);
 CrimeListFragment listFragment = (CrimeListFragment)getSupportFragmentManager().findFragmentById(R.id.fragment_container);
       listFragment.deleteCrime(crimeId);
       listFragment.updateUI(); 
// FIXME:For two- panel detail errors. when I want to delete item in list using the swipe. I took an error that recyclerview.setNextAnim(int) on a null object reference. And I added this code. Now swipe to remove  perfect in the two-pane layout.
         if(crimeFragment==null){

            // here is empty. Because to delete an item from the list without selecting it in the two-pane layout.

                 }else {
  // when you remove list item with using swipe, the detail screen is disappear. 
listFragment.getActivity().getSupportFragmentManager().beginTransaction().remove(crimeFragment).commit();
}

    }

UPDATE 07.12.17
And I added a method that Drawable for visuality.
After the onSwiped method, you can use it.

@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
  
 // get item
 View itemview = viewHolder.itemView;
 // get an icon from drawable folder
 Drawable deleteIcon = ContextCompat.getDrawable(getContext(), R.drawable.ic_menu_delete);
// get height and width sizes from layout
 float IcontHeight = deleteIcon.getIntrinsicHeight();
 float IconWidth = deleteIcon.getIntrinsicWidth();
// get item's bottom and Top size 
 float itemHeight = itemview.getBottom() - itemview.getTop();

 if (actionState == ACTION_STATE_SWIPE) {   // user is swipe
 Log.d(TAG, "////////////////////////////////////////////////");
 Log.d(TAG, "ACTION STATE SWAP is true: ");

 Resources r = getResources();   // as you read 
 Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);    // create paint object
  // get layout all values from inflate crime_fragment_list.xml 
 RectF layout = new RectF(itemview.getLeft(), itemview.getTop(), itemview.getRight(), itemview.getBottom());
 // set color
 paint.setColor(r.getColor(R.color.colorAccent));
 // drawing canvas
 c.drawRect(layout, paint);

   // to calculate deleteIcon object that necessary values
 int deleteIconTop = (int) (itemview.getTop() + (itemHeight - IcontHeight) / 2);
 int deleteIconBottom = (int) (deleteIconTop + IcontHeight);
 int deleteIconMargin = (int) ((itemHeight - IcontHeight) / 2);
 int deleteIconLeft = (int)(itemview.getRight() - deleteIconMargin - IconWidth);
 int deleteIconRight = (int) itemview.getRight() - deleteIconMargin;
  // then set boundry that get values above
 deleteIcon.setBounds(deleteIconLeft,deleteIconTop,deleteIconRight,deleteIconBottom);
 // to add canvas
 deleteIcon.draw(c);
 
 /*c.save(); */
// you can find explanation here (1**)
 getDefaultUIUtil().onDraw(c, recyclerView, viewHolder.itemView, dX, dY, actionState, isCurrentlyActive);

 } else {
 Log.d(TAG, "////////////////////////////////////////////////");
 Log.d(TAG, "ACTION STATE SWAP is false: ");
      }
}

(1**) explanation

My code is

public void updateUI() {
    final CrimeLab crimeLab = CrimeLab.get(getActivity());
    List<Crime> crimes = crimeLab.getCrimes();

    if(mAdapter == null) {
        mAdapter = new CrimeAdapter(crimes);
        mCrimeRecyclerView.setAdapter(mAdapter);
		initItemTouchHelper();
    } else {
        mAdapter.setCrimes(crimes);
        mAdapter.notifyDataSetChanged();
    }

    updateSubtitle();
}

private void initItemTouchHelper() {
	ItemTouchHelper itemTouchHelper = new ItemTouchHelper(
			new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.END) {
				@Override
				public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
					return false;
				}

				@Override
				public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
					if(direction == ItemTouchHelper.END) {
						CrimeLab cl = CrimeLab.get(getActivity());
						cl.deleteCrime(
								mAdapter.mCrimes
										.get(viewHolder.getAdapterPosition()).getId()
						);
						updateUI();
					}
				}
			}
	);
	itemTouchHelper.attachToRecyclerView(mCrimeRecyclerView);
}

Can somebody explain why do i need to use callbacks like 2 user above?
I think it is redundant because my code still don’t make Fragment dependent from Activity.
If i wrong, please explain it to me.
Sorry for bad English.

your doubt got me thinking …
since here we are just deleting the crime , callback or no callback might not matter. But if you open it as a master detail on tablet…then the CrimeFragment on right does not update as soon as we swipe the crime from CrimeListFragment present at the left.
so i tried to use the same code as onCrimeSelected callback inside onCrimeIdSelected (as done by Iscodex) to renew the CrimeFragment layout but since we are deleting it i got confused as to which Id will be passed to be displayed(incase there are more than 1 crimes before) or just hide view when there are no crimes.