preface

There are a lot of Adapter packaging of RecyclerView. The one we talked about today is DslAdapter. Why do we say this?

Github address: github.com/angcyo/DslA…

The body of the

The design idea of this DslAdapter is to make it more convenient to write the layout, and use the STYLE of Dsl to write the code. The layout we usually consider as a RecyclerView type, and use the multi-type characteristics of RecyclerView to complete it at one time, which is particularly convenient. You can use it on Github, where I mainly study sideslip.

SwipeHelper

The SwipeHelper class is used to implement all aspects of this project. Let’s take a look at the implementation of this class.

class SwipeMenuHelper(var swipeMenuCallback: SwipeMenuCallback) : ItemDecoration(),
    OnChildAttachStateChangeListener {
        ...
    }
Copy the code

Inherit ItemDecoration and OnChildAttachStateChangeListener here 2 classes, including ItemDecoration is Item is decorated, a lot of functionality is done by this class.

ItemDecoration

This is the famous ItemDecoration class, used to add decoration to RecyclerView Item, specific decoration? For example, we usually use the division line is inherited from this, of course, more functions need to use the following 3 methods:

We must master the usage of this class. With this class, we can customize the decoration layout of Item. For details, please refer to other blogs, such as:

www.jianshu.com/p/fe94636ef…

Ok, so much for ItemDecoration.

OnChildAttachStateChangeListener

This is the Attach callback of the subview, this class is also very useful, when we deal with RecyclerView Item sliding invisible, this method callback is a good way to deal with logic, there are two main methods:

RecyclerView.OnItemTouchListener

In addition to the above two classes, you also need to know a RecyclerView built-in class, this class is very important, and RecyclerView is very important to process and intercept events.

From the introduction of the above figure, Touch events can be transmitted to RecyclerView itself or its ItemView processing before interception, here is easy to do, according to this idea, we can interception of Touch events, there is a need (sideshow) operation is interception, the other is not processed.

Gestures to monitor

The SimpleOnGestureListener interface needs to be implemented in order to determine whether an event is in a Down, Scroll or Fling state.

events

With this basic understanding, we are ready for logical combing and flow processing, namely interception of Touch events.

Click on the

For a click, the sequence of events is DOWN -> UP, which is easy to handle in the following cases

In the first case, no item is sideslip. In this case, it is an ordinary click event and needs to be passed to the Item for processing. In this case, neither DOWN nor UP need to be intercepted and processed.

The second one is that there is already an item that has been sideslip, no matter where it is clicked, the operation of sideslip button is always put away. In this case, the DOWN event needs to be processed and the operation of sideslip item is directly carried out, while the UP event is not processed. At this time, the sideslip button needs to be put away and the click event needs to be transferred to RecyclerView.

sliding

For sliding, the sequence of events is DOWN -> MOVE ->…. -> MOVE -> UP

If the next event is a MOVE event, the gesture listener class will continue to handle it. If the gesture listener class triggers the onScroll callback, it indicates that sliding has occurred. At this time, it is determined whether to intercept the event MOVE event and slide Item according to whether the Item sets the sideslip attribute and slide direction.

As you can see in the GIF below, the Item is shifted when the Scroll is triggered and the UP event is triggered to decide whether to open or close:

Note that the UP event is handled very carefully, not only in Scroll, where the total distance of the slide is greater than the threshold, but also in Fling.

1. For the Scroll, when the total distance _scroll is less than 0, it indicates that the Scroll is sliding to the left. In this case, we need to determine the last Scroll direction (there will be many times, the last Scroll before UP, note that the Scroll must be greater than SLOp, hand shaking does not count). If it is on the left, the sliding distance is greater than the threshold. If it is on the right, the sliding distance is greater than (width – threshold), which is considered as the intention to open the menu on the right.

For example, here, although at the end of the UP slide, the state is Scroll, but the last long distance Scroll is to the right, then the intention is also closed, unless the left sideslip has been very much, that is, greater than (sideslip width – threshold) to open sideslip.

2. For the Fling, there is no need to determine the sliding distance, the need to determine whether the sliding speed is greater than the threshold, and the need to determine the direction, when the slide is left and the speed direction is left or right and the speed direction is right is open side slide, other cases are closed side slide.

This is a left slide with a left speed, so it’s open sideslip.

In this case, it’s a left slide but the velocity is to the right, and it doesn’t matter how far you slide it doesn’t matter what the threshold is you’re doing a closed sideslip.

ItemView sliding

Now that we’ve seen the logic that sliding has triggered, we need to sort out how many situations we can slide on an ItemView and how we can slide on it.

In both cases, sliding is a long distance, so add a gradient value:

Y fun scrollSwipeMenuTo(ViewHolder: recyclerView. ViewHolder, x: Float = 0f, y: Float Float = 0f) { if (_lastValueAnimator? .isRunning == true) { return } _recyclerView? .apply {val startX = _scrollX val startY = _scrollY Let the sliding animation val. ValueAnimator = valueAnimator ofFloat (0 f, 1 f) valueAnimator. AddUpdateListener {val fraction: Float = it.animatedValue as Float val currentX = startX + (x - startX) * fraction val currentY = startY + (y - startY) * Fraction _scrollX = currentX _scrollY = currentY / / in the end is to invoke this method displacement ItemView swipeMenuCallback. OnSwipeTo (_recyclerView!!!!! , viewHolder, currentX, CurrentY)} valueAnimator. AddListener (onEnd = {the if (x = = 0 f && y = = 0 f) {/ / off the menu _swipeMenuViewHolder = null} else { _swipeMenuViewHolder = viewHolder } _lastValueAnimator = null }, onCancel = { _lastValueAnimator = null }) valueAnimator.duration = ItemTouchHelper.Callback.DEFAULT_SWIPE_ANIMATION_DURATION.toLong() valueAnimator.start() _lastValueAnimator = valueAnimator } }Copy the code

Since we’re calling the onSwipeTo method at the end, let’s see:

open fun onSwipeTo( recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, x: Float, y: Float ) { dslAdapterItem(recyclerView, viewHolder)? .run { itemSwipeMenuTo(viewHolder as DslViewHolder, x, y) } }Copy the code
*/ var itemSwipeMenuTo: (itemHolder: DslViewHolder, dX: Float, dY: Float) -> Unit = { itemHolder, dX, dY -> onItemSwipeMenuTo(itemHolder, dX, dY) }Copy the code
/ * * please send the menu, the layout in the position of the first child, and the layout of the [left] and [the top] are 0 * default UI effect is, TranslationX. * the default implementation supports only temporarily left/right sliding menu, * */ Open fun onItemSwipeMenuTo(itemHolder: DslViewHolder, dX: Float, dY: ItemView if (parent is ViewGroup && parent.childcount > 1) {Float) {val parent = itemholer.itemView if (parent is ViewGroup && parent.childcount > 1) { Val menuWidth = itemSwipeWidth(itemHolder) val tX = clamp(dX, -menuWidth.tofloat (), menuWidth.toFloat()) parent.forEach { index, child -> if (index == 0) { if (itemSwipeMenuType == SWIPE_MENU_TYPE_FLOWING) { if (dX > 0) { child.translationX = -menuWidth + tX } else { child.translationX = parent.mW() + tX } } else { if (dX > 0) { child.translationX = 0f } else {  child.translationX = (parent.mW() - menuWidth).toFloat() } } } else { child.translationX = tX } } } }Copy the code

A neat way to do this is to place the menu layout in the first child and then shift it as needed.

There are two types of sliding: one is when the slide button follows behind the ItemView, and the other is when the slide button is at the bottom of the ItemView. For these two types of sliding, the starting position of the displacement animation is different.

conclusion

Here is the principle of sideslip said almost, the specific principle can go to view the source code, there are GitHub address and implementation of the class. Through this sideslip study, I think the biggest gain is RecyclerView sideslip event interception and event processing, especially the difference between Scroll and Fling, the design is very clever.