Want to support me financially or watch a video of how I did it:

Edu.csdn.net/course/deta…

An overview of the

A few days ago, I saw that someone implemented a subscription interface that mimics everyone’s American TV series. However, in terms of details and implementation methods, I personally think it is not the best posture. So I also began to masturbate a, also along with a probe probe interface, first look at GIF:

Explore the emperor over the brand that sense

Us TV Series Subscription interface

Here to spit out a slot, explore this design really like the feeling of the emperor over the brand, do not like the left slide, like the right slide.

Yyets Version features (needs) :

  • Animation: the most visible of the four layers, as the top layer of the card slides, each layer will shift & enlarge the animation, a feeling of supplementing to the top layer.
  • Animation: When you release the card, if it is not deemed deleted, there will be an animation of each layer below the top shrinking back to its original position.
  • Infinite loop: Imitate yomiyom, top card deleted, added to the bottom.

In addition to the above animation features, exploration version features (requirements) :

  • Roate changes: When sliding left or right, the top layer of the card will slowly rotate to the threshold Max of about 15 degrees.
  • Alpha: The delete button on the top card appears when you swipe left, and the Love button appears when you swipe right.
  • Obviously, the above animation also needs to be reset when you release it.

Our effect is basically the same as the original, how is it written? I’m not a clicker, as the headlines say:

  • Simple: The idea is simple, clear and easy to understand
  • Grace:performanceThere are no hidden dangers,LayoutManagerIt only loads on the display screenNumber of views visible.
  • Quick: UseItemTouchHelperHandles drag & slide delete logic with no more than 50 lines of core code. And packaged, four lines of code can be used.

Hand party benefits:

If you don’t want to see so much text and just want to use it, go to GayHub, Gradle import the relevant file or copy it. And here it is. Done.

        mRv.setLayoutManager(new OverLayCardLayoutManager());
        CardConfig.initConfig(this);
        ItemTouchHelper.Callback callback = new RenRenCallback(mRv, mAdapter, mDatas);
        ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback);
        itemTouchHelper.attachToRecyclerView(mRv);Copy the code

In addition, I calculated some parameters in the form of variables, so that it can be configured. If the boss asks you to display more layers of cards at the beginning, such as 6 layers, you only need to modify one parameter, the effect is shown in the picture:

6 layer View

Correct posture

Correct posture is:

  • usingLayoutManagerImplement card cascading layout, it is worth noting that layout only out of the interface may see those views.
  • collocationItemTouchHelper, which itself implements drag & slide delete logic, we just need to inonChildDraw()Draw animation andonSwiped()To process data sets (loop or delete).

So this article also fills in the holes of the LayoutManger series to achieve a cool layout. Let’s Go!

Reprint please indicate the source: juejin.cn/post/684490… Juejin. Cn/post / 684490… This article is from: [Zhang Xutong’s Rare earth mining] (juejin.cn/user/905653…) Code portal: Click star if you like. Thank you for your github.com/mcxtzhang/Z…

LayoutManager implements card stacking

In this case, LayoutManager is very simple. Because of ItemTouchHelper, LayoutManager doesn’t need to handle its sliding events at all, and the hardest thing to write in LayoutManager is View recycling and reuse during sliding. And layout new View processing.

I won’t go over the basics and groundwork for LayoutManager, but refer to my previous article: LayoutManager implements streaming layout

Only note

But even so, there is one caveat. We only want the layout out of the View that we might see on the interface. Because of the animation, so it is possible to see. We look at the interface of American TV series:

At the bottom of the details



View
TopView,Top-1View,Top-2View
TopView
Top-1View,Top-2View

As shown in GIF, when sliding TopView, top-1View and top-2View begin to zoom in slowly and move upward until they are filled with their upper View. The top-3View is now exposed.

So when we write the onLayoutChildren() method of LayoutManager, we only need the layout to display the last four views of the current data set.

The preceding parameters are as follows: Some configurations are included

  • The interface can display a maximum of several views
  • Scale differences between views at each level, translationY, etc

    public class CardConfig {
     // A maximum of several items can be displayed simultaneously on the screen
     public static int MAX_SHOW_COUNT;
     // The difference of Scale at each level is 0.05F, and the difference of translationY is about 7dp
     public static float SCALE_GAP;
     public static int TRANS_Y_GAP;
    
     public static void initConfig(Context context) {
         MAX_SHOW_COUNT = 6;
         SCALE_GAP = 0.05f;
         TRANS_Y_GAP = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 15, context.getResources().getDisplayMetrics()); }}Copy the code

LayoutManager: LayoutManager: LayoutManager: LayoutManager: LayoutManager: LayoutManager: LayoutManager: LayoutManager

public class OverLayCardLayoutManager extends RecyclerView.LayoutManager {
    @Override
    public RecyclerView.LayoutParams generateDefaultLayoutParams() {
        return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
    }
    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        detachAndScrapAttachedViews(recycler);
        int itemCount = getItemCount();
        if (itemCount >= MAX_SHOW_COUNT) {
            // Start at the bottom of the View and start at the top
            for (int position = itemCount - MAX_SHOW_COUNT; position < itemCount; position++) {
                View view = recycler.getViewForPosition(position);
                addView(view);
                measureChildWithMargins(view, 0.0);
                int widthSpace = getWidth() - getDecoratedMeasuredWidth(view);
                int heightSpace = getHeight() - getDecoratedMeasuredHeight(view);
                // We center childView in the layout. We can also center it horizontally
                layoutDecoratedWithMargins(view, widthSpace / 2, heightSpace / 2,
                        widthSpace / 2 + getDecoratedMeasuredWidth(view),
                        heightSpace / 2 + getDecoratedMeasuredHeight(view));
                /** * the TopView's Scale is 1, translationY 0 * the TopView's Scale is 1, translationY 0 * Always 1. * Top-1View Scale slowly changes to 1, translation also slowly restore 0 * top-2View Scale slowly changes to top-1view Scale, Translation also slowly changes only the translation of top-1view * top-3view Scale to change, the translation stays unchanged */

                // The last TopView (6) is level 0,
                int level = itemCount - position - 1;
                // No shrinkage or displacement is required except for the top layer
                if (level > 0 /*&& level < mShowCount - 1*/) {
                    // Each layer needs to be shrunk in the X direction
                    view.setScaleX(1 - SCALE_GAP * level);
                    // The first N layers, then the downward displacement and the Y direction narrowing
                    if (level < MAX_SHOW_COUNT - 1) {
                        view.setTranslationY(TRANS_Y_GAP * level);
                        view.setScaleY(1 - SCALE_GAP * level);
                    } else {// The NTH floor has the same downward displacement and y-direction shrinkage as the N-1 floor
                        view.setTranslationY(TRANS_Y_GAP * (level - 1));
                        view.setScaleY(1 - SCALE_GAP * (level - 1));
                    }
                }
            }
        }
    }

}Copy the code

At this point, our static interface has taken shape, so let’s get it moving:

Static interface

ItemTouchHelper implements dazzle sliding:

The basics of ItemTouchHelper, I recommend you to learn by yourself. There are a lot of articles on the web, so LET me briefly introduce them.

This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView.

It works with a RecyclerView and a Callback class, which configures what type of interactions

are enabled and also receives events when user performs these actions.

Depending on which functionality you support, you should override

{@link Callback#onMove(RecyclerView, ViewHolder, ViewHolder)} and / or

{@link Callback#onSwiped(ViewHolder, int)}.

Translation + Summary:

This is a utility class for RecyclerView extension slide disappear (delete) and Drag & drop effects. It needs to work with RecyclerView and Callback. The Callback class defines which interactions are allowed and receives the corresponding interaction events depending on which functionality you want (slide disappear (delete) and Drag & Drop), You need to override Callback#onMove(RecyclerView, ViewHolder, ViewHolder)—–drag & drop Callback#onSwiped(ViewHolder, int). —– Slide disappear (delete)

To summarize entry-level usage, there are three steps:

  • Define a Callback: ItemTouchHelper. Callback Callback = new ItemTouchHelper. SimpleCallback (int, int), the two int representing to listen on the direction of which a few drag-and-drop, sliding event. Commonly used: ItemTouchHelper DOWN | ItemTouchHelper. UP | ItemTouchHelper. LEFT | ItemTouchHelper. RIGHT

  • ItemTouchHelper = new ItemTouchHelper(Callback);

  • Associated ItemTouchHelper and RecyclerView: ItemTouchHelper attachToRecyclerView (mRv)

After these three steps, the ItemTouchHelper will automatically do the sliding disappear and drag & Drop functions for us.

Sliding delete

In our case, we need to swipe away (delete), so our Callback doesn’t need to focus on the onMove() method. And we need to swipe up, down, left and right to remove the effect. Construct Callback as follows, passing up, down, and left:

ItemTouchHelper.Callback callback = new ItemTouchHelper.SimpleCallback(0,
                ItemTouchHelper.DOWN | ItemTouchHelper.UP | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT)Copy the code

The onSwiped() method is called back after the swipe and delete action has taken place. That is, we swipe the card and then release our hand. ItemTouchHelper determines that our gesture is delete and automatically animates the card out of the screen while calling the onSwiped() method. So we need to write something like this:

            @Override
            public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
                //★ Implement loop points
                SwipeCardBean remove = mDatas.remove(viewHolder.getLayoutPosition());
                mDatas.add(0, remove);
                mAdapter.notifyDataSetChanged();
            }Copy the code

Here we complete the loop:

  • Use the currently deletedViewtheViewHoldergetPosition
  • Delete the mapping from the datasetPositionThe data source
  • The data source is also inserted at the top of the dataset.
  • callnotifyDataSetChanged()The notification list is refreshed

    And so we’re done,Requirements for a circular list.

Here’s why we call notifyDataSetChanged(). Read the official document:

ItemTouchHelper moves the items’ translateX/Y properties to reposition them

The sliding deletion implemented by ItemTouchHelper actually hides the sliding View. It’s not really deleted.

In section 5 of the LayoutManager article implementing Streaming Layouts, we mentioned that notifyDataSetChanged() calls back the onLayoutChildren() function, and in this function, we rearrange, That is, the View is removed from the layout slide, and a new View is added to the bottom layer.

Well, JavaBean also take a look, no highlights:

public class SwipeCardBean {
    private int postition;/ / position
    private String url;
    private String name;
    }Copy the code

ItemTouchHelper handles complex judgments, such as speed, whether the sliding distance reaches the deletion threshold, removing successfully removed animations, unremoving reset animations, and so on. That’s why I say using ItemTouchHelper is the right pose because it’s easy & quick. Now let’s animate the slide.

Sliding animation

We need to rewrite the Callback’s onChildDraw() method, which has more arguments:

* @param c The canvas which RecyclerView is drawing its children * @param recyclerView The RecyclerView to which ItemTouchHelper is attached to * @param viewHolder The ViewHolder which is being interacted by the User or it was interacted and simply animating to its original position * @param dX The amount of horizontal displacement caused by User's action * @param dY The amount of vertical displacement caused by user's action * @param actionState  type of interaction on the View. Is either {@link #ACTION_STATE_DRAG} or {@link #ACTION_STATE_SWIPE}. * @param IsCurrentlyActive Specifies whether the event is currently being controlled by the user or false it is simply animating back to its original state.Copy the code

Useful for us have dX dX, you can judge the sliding direction, and calculate the sliding proportion, so as to control the scale, displacement animation degree.

In this paper, the scaling and displacement of View is actually the inverse operation of the operation in LayoutManager. It is worth noting that the last layer, namely top-3View, remains unchanged on the Y-axis:

@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);
                // Calculate the fraction in the animation according to the sliding DXdy
                double swipValue = Math.sqrt(dX * dX + dY * dY);
                double fraction = swipValue / getThreshold(viewHolder);
                // The boundary correction is up to 1
                if (fraction > 1) {
                    fraction = 1;
                }
                // Scale each ChildView
                int childCount = recyclerView.getChildCount();
                for (int i = 0; i < childCount; i++) {
                    View child = recyclerView.getChildAt(i);
                    // The last TopView (6) is level 0,
                    int level = childCount - i - 1;
                    if (level > 0) {
                        child.setScaleX((float) (1 - SCALE_GAP * level + fraction * SCALE_GAP));

                        if (level < MAX_SHOW_COUNT - 1) {
                            child.setScaleY((float) (1- SCALE_GAP * level + fraction * SCALE_GAP)); child.setTranslationY((float) (TRANS_Y_GAP * level - fraction * TRANS_Y_GAP)); }}}}Copy the code

The getThreshold(viewHolder) function returns a threshold for whether or not to be recycled. This is explained at the end of this article:

            // The threshold of whether the horizontal direction can be reclaimed
            public float getThreshold(RecyclerView.ViewHolder viewHolder) {
                return mRv.getWidth() * getSwipeThreshold(viewHolder);
            }Copy the code

Explore the realization of the effect

At the beginning of the article masturbated here should be over, the group out of a Ma Xiaotiao, told me to explore and this slightly different, I hope to achieve. Well, all right. I haven’t heard of Tantan, so I’ll download one first. loading-install-open…….. Ouch ah, ten minutes later, I was still sliding to see the beauty forgot what to do, was the girl saw fat beat me. Okay, I’ll cover my face and continue to analyze.

There are two differences between Tantan and YYEts:

  • Roate changes: When sliding left or right, the top layer of the card will slowly rotate to the threshold Max of about 15 degrees.
  • Alpha: The delete button on the top card appears when you swipe left, and the Love button appears when you swipe right.

I think it’s easy, too. Here we go. Five minutes for takeout. Modify points:

  • Add “X” & “love” to layout.
  • inonChildDraw()Modify TopView’s Rotate & Alpha proportionately

Listening to the direction

There is also a slight difference, the up slide can no longer be deleted, so we can only pass in left and right:

ItemTouchHelper.Callback callback = new ItemTouchHelper.SimpleCallback(0,
                ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT)Copy the code

Layout adds two buttons

slightly

onChildDraw()

On the basis of the above yyets extension, the effect above, TopView is not any operation. Here we just need to do something extra to TopView:

            @Override
            public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
                ...
                for (int i = 0; i < childCount; i++) {
                    View child = recyclerView.getChildAt(i);
                    // The last TopView (6) is level 0,
                    int level = childCount - i - 1;
                    if (level > 0) {... }else {
                        Rotate &alpha is added to the first layer
                        // But he distinguishes between left and right
                        float xFraction = dX / getThreshold(viewHolder);
                        // The boundary correction is up to 1
                        if (xFraction > 1) {
                            xFraction = 1;
                        } else if (xFraction < - 1) {
                            xFraction = - 1;
                        }
                        //rotate
                        child.setRotation(xFraction * MAX_ROTATION);

                        // Feel for yourself Alpha
                        if (viewHolder instanceof ViewHolder) {
                            ViewHolder holder = (ViewHolder) viewHolder;
                            if (dX > 0) {
                                // Show the left side, compare the heart
                                holder.setAlpha(R.id.iv_love, xFraction);
                            } else {
                                // Show the right side and roll off
                                holder.setAlpha(R.id.iv_del, -xFraction);
                            }
                        }
                    }
                }
            }Copy the code

When I did, I thought it was over, but it turned out to be more complicated than we thought. NotifyDataSetChanged () refreshes the interface after deletion, but TopView is still tilted, and the love and delete ICONS appear. This is clearly not in line with expectations. So we need to reset it in onSwiped() :

            @Override
            public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
            ...
                Rotate &alpha is added to the first layer
                // Reset the rotate
                viewHolder.itemView.setRotation(0);
                // Feel for yourself Alpha
                if (viewHolder instanceof ViewHolder) {
                    ViewHolder holder = (ViewHolder) viewHolder;
                    holder.setAlpha(R.id.iv_love, 0);
                    holder.setAlpha(R.id.iv_del, 0); }}Copy the code

Ok, we’re done. The effect is the same as the first article. Go with the product UI.

Finding the threshold

It took me a while to find the threshold because I wanted to be consistent with the behavior of the system. That is, when delete, like the icon full display, when the top-1View display is complete, let go of TopView will be recycled. This determines that our scaling and displacement thresholds cannot be arbitrarily set, so we have to go to the source code to find the answer.

    // The threshold of whether the horizontal direction can be reclaimed
    public float getThreshold(RecyclerView.ViewHolder viewHolder) {
        return mRv.getWidth() * getSwipeThreshold(viewHolder);
    }Copy the code

The slider delete operation is triggered by the touch event and should be triggered by ACTION_UP, so in the ItemTouchHelper source, search for onTouch: ACTION_UP:,-> void select(ViewHolder selected, Int actionState)-> animationType = ANIMATION_TYPE_SWIPE_SUCCESS; If (swipeDir > 0)-> final int swipeDir = prevActionState == ACTION_STATE_DRAG? 0 : swipeIfNecessary(prevSelected); -> int swipeIfNecessary(ViewHolder viewHolder)->

 if ((swipeDir = checkHorizontalSwipe(viewHolder, flags)) > 0) {
                return swipeDir;
  }Copy the code

CheckHorizontalSwipe (viewHolder, flags)-> checkHorizontalSwipe(viewHolder, flags)-> checkHorizontalSwipe(viewHolder, flags)-> checkHorizontalSwipe(viewHolder, flags)

final float threshold = mRecyclerView.getWidth() * mCallback
        .getSwipeThreshold(viewHolder);Copy the code

So I’ll just copy it.

conclusion

Code portal: Click star if you like. Thank you for your github.com/mcxtzhang/Z…

Want to support me financially or want to see how I did it via video: edu.csdn.net/course/deta…

This article uses LayoutManager to load the number of views visible on the screen, with ItemTouchHelper handling drag-and-slide delete logic, and no more than 50 lines of core code. And packaged, four lines of code can be used.

Remember LayoutManager, we just write, layout out of the View that we might see on the interface.

For ItemTouchHelper, which itself implements drag-and-slide delete logic, we just animate onChildDraw() and process the dataset (loop or delete) in onSwiped().

In the future when your boss asks you to do this, you just need to:

        CardConfig.initConfig(this);
        ItemTouchHelper.Callback callback = new RenRenCallback(mRv, mAdapter, mDatas);
        ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback);
        itemTouchHelper.attachToRecyclerView(mRv);Copy the code

If you need to customize special parameters, such as displaying 6 layers:

         CardConfig.MAX_SHOW_COUNT = 6;Copy the code

Reprint please indicate the source: juejin.cn/post/684490… This article is from: [Zhang Xutong’s Rare earth mining] (juejin.cn/user/905653…) Code portal: Click star if you like. Thank you for your github.com/mcxtzhang/Z…

Just built a QQ to engage in gay communication group: 557266366 inside now no one. Well, that’s it.