SmartSwipe is an Android sideswipe framework that captures and distributes sideswipe events (up, down, left, and right gestures). SmartSwipe allows you to add any sideswipe effects you want to your controls.

Let’s see what it can do first!

If you already know what SmartSwipe does and just want to understand how it works, skip section 1 and go straight to section 2Copy the code

One, usage and demonstration

1.1 One line of code to achieve global sideslip return

// Copy the mobile QQ gesture slide back
SmartSwipeBack.activityStayBack(application, null);		
// A transparent slip-back that mimics the wechat band linkage effect
SmartSwipeBack.activitySlidingBack(application, null);	
// Side sliding door style closes the activity
SmartSwipeBack.activityDoorBack(application, null);		
// Side slide shutter style closes the activity
SmartSwipeBack.activityShuttersBack(application, null);	
// Return the bezier curve from MIUI
SmartSwipeBack.activityBezierBack(application, null);
Copy the code

Click here for more uses of sideslip return

Effect:

1.2 One line of code makes the page move

// Add an iOS elastic white space effect to the control:
// If you can't scroll vertically (or scroll to the top/bottom), if you continue to drag, the UI will show an elastic white effect, and smooth recovery after release
SmartSwipe.wrap(view)
    .addConsumer(new SpaceConsumer())
    .enableVertical();
Copy the code

Effect:

1.3 One Line of code makes the page elastic

// Add MIUI elastic stretching effect to the control:
// When you can't scroll vertically (or scroll to the top/bottom), if you continue to drag, the UI will appear elastic stretch effect, and smooth recovery after release
SmartSwipe.wrap(view)
    .addConsumer(new StretchConsumer())
    .enableVertical();
Copy the code

Effect:

1.4 Row generation adds drop-down refresh

//xxxMode the second parameter is false, indicating that the work direction is portrait: pull down refresh & pull up load more
// If the second parameter is set to true, the work direction is landscape: right to refresh and left to load more
SmartSwipeRefresh.drawerMode(view, false).setDataLoader(loader);
SmartSwipeRefresh.behindMode(view, false).setDataLoader(loader);
SmartSwipeRefresh.scaleMode(view, false).setDataLoader(loader);
SmartSwipeRefresh.translateMode(view, false).setDataLoader(loader);
Copy the code

Click here for more uses of the drop-down refresh

style rendering
drawerMode
behindMode
scaleMode
translateMode

1.5 One line of code to add a sliding menu

SmartSwipe.wrap(view)
    // Add a drawer effect that looks similar to DrawerLayout
    // DrawerLayout only supports left and right directions, while DrawerConsumer supports up, down and left directions
    .addConsumer(new DrawerConsumer())	
    // Set the horizontal (left and right) drawer to the same view (common slide to show the delete button function)
    .setHorizontalDrawerView(buttonsViewGroup) 
    .setScrimColor(0x2F000000) // Set the mask color
    .setShadowColor(0x80000000)	// Set the shadow color of the edge
    ;
Copy the code

Effect:

1.6 One line of code to add a sliding menu with linkage effect

SmartSwipe.wrap(view)
    .addConsumer(new SlidingConsumer())
    .setRelativeMoveFactor(0.3 F) // Linkage coefficient
    .setHorizontalDrawerView(buttonsView)
    .setScrimColor(0x2F000000);Copy the code

Effect:

1.7 Cool cover

SmartSwipe.wrap(coverView)
    .addConsumer(new ShuttersConsumer()) // Shutter effect
    .setScrimColor(0xAF000000)
    .enableAllDirections()
    .addListener(new SimpleSwipeListener() {
        @Override
        public void onSwipeOpened(SmartSwipeWrapper wrapper, SwipeConsumer consumer, int direction) {
            // The cover is automatically hidden or removed after it is openedwrapper.setVisibility(View.GONE); }});Copy the code

Effect:

SmartSwipe.wrap(coverView)
    .addConsumer(new DoorConsumer()) // Open door effect
    .setScrimColor(0xAF000000)
    .enableAllDirections()
    .addListener(new SimpleSwipeListener() {
        @Override
        public void onSwipeOpened(SmartSwipeWrapper wrapper, SwipeConsumer consumer, int direction) {
            // The cover is automatically hidden or removed after it is openedwrapper.setVisibility(View.GONE); }});Copy the code

Effect:

For more Settings on the cover, see Demo

Two, the implementation principle

2.1 Introduction to ViewDragHelper

ViewDragHelper is a utility class in Android’s official support library. It helps us handle control drags: first create a custom ViewGroup, add the dragged control to the custom ViewGroup, and use the ViewDragHelper to handle control drags.

ViewDragHelper intercepts the parent’s touch event, captures a child control for dragging, and relocates the child control in the parent by changing its left and top positions.

In the official support library, slide drawer related SlidingPaneLayout and DrawerLayout, and CoordinatorLayout related BottomSheetBehavior and SwipeDismissBehavior, You can see the ViewDragHelper.

However, the name of ViewDragHelper also indicates that it is used to handle dragging. The object to be dragged must be a child View. During the dragging process, the left and top of the child control need to be changed. MIUI system bezier curve sideslip return effect, mobile QQ sideslip return effect and elastic stretching effect commonly used in MIUI official app, etc.), but a little bit of force.

2.2 Refer to ViewDragHelper for sideslip processing

Can we abstract the concept of sideslip? What does sideslip mean?

  • Narrow sideslip: Start at an edge of the screen and slide away from that edge
  • Generalized sideslip: sliding your finger in a direction after pressing it down on the screen

My understanding is that broad sideslip includes narrow sideslip, just the difference between whether the trigger area is at the edge of the screen or not.

Since the swipe gesture can be abstracted explicitly, can we use ViewDragHelper’s idea of event blocking to encapsulate it in this way?

Intercept and analyze the touch event of the sideslip control, confirm whether to capture it as a sideslip gesture, and then calculate the real-time displacement of sideslip (the displacement of finger sliding, Instead of relying on the left and top of View) and then using different strategies to constantly consume the displacement of sideslip through the Strategy Pattern to perform the UI presentation of sideslip effect.Copy the code

The answer is yes!

2.3 Implementation principle of SmartSwipe

SmartSwipe, based on ViewDragHelper, modifys its capture and movement of child views into the parent View’s own touch event characterization (whether or not to capture), orientation (the sideslipping direction triggered by the captured event), and positioning (the distance the event moves in the sideslipping direction after capture). Sliding distance and will be assigned to SwipeConsumer consumption, SwipeConsumer control according to the distance of sideslip layout change accordingly.

The SmartSwipe package idea is as follows:

  • Wrap a ViewGroup around the View control that needs to handle the sideslip event (The wrapped control serves as itscontentView)
  • You can add some ancillary controls to the ViewGroup (Slide the drawer)
  • Intercepts the ViewGroup’s touch event and converts the touch event into a SwipeConsumer swipe distance for consumption
  • SwipeConsumer changes the layout of the control according to the sideshow distance
  • By inheriting SwipeConsumer, you can change the layout of the control in different ways (Such as:contentViewAnd the position of the accessory control, zoom, transparency, etc), so as to achieve a variety of sideslip effects.

Thus, SwipeConsumer can be easily inherited to realize the different UI rendering effects according to SwipeConsumer.

2.4 How do I Create a Custom SwipeConsumer?

Take the realization of elastic stretching effect in the application of imitated MIUI system built in the frame as an example

According to the sideslip distance, the contentView is zoomed and shifted to achieve elastic stretching effectCopy the code

The code is as follows:

public class StretchConsumer extends SwipeConsumer {
    @Override
    public void onDetachFromWrapper(a) {
        super.onDetachFromWrapper();
        View contentView = mWrapper.getContentView();
        if(contentView ! =null) {
            contentView.setScaleX(1);
            contentView.setScaleY(1);
            contentView.setTranslationX(0);
            contentView.setTranslationY(0); }}@Override
    public void onDisplayDistanceChanged(int distanceXToDisplay, int distanceYToDisplay, int dx, int dy) {
        View contentView = mWrapper.getContentView();
        if(contentView ! =null) {
            if (distanceXToDisplay >= 0 && isLeftEnable() || distanceXToDisplay <= 0 && isRightEnable()) {
                contentView.setScaleX(1 + Math.abs((float) distanceXToDisplay) / mWidth);
                contentView.setTranslationX(distanceXToDisplay / 2F);
            }
            if (distanceYToDisplay >= 0 && isTopEnable() || distanceYToDisplay <= 0 && isBottomEnable()) {
                contentView.setScaleY(1 + Math.abs((float) distanceYToDisplay) / mHeight);
                contentView.setTranslationY(distanceYToDisplay / 2F); }}}}Copy the code

That’s all the code for elastic stretching, easy enough, isn’t it?

It’s just as simple to use:

SmartSwipe.wrap(view) // Specify the target control
    .addConsumer(new StretchConsumer()) // Add elastic stretch effect
    .enableVertical(); // Specify two directions: up and down
Copy the code

Let’s see how to achieve the effect of QQ sideslip return

When the finger is released, decide whether to finish the current Activity according to the direction and speed of the slideCopy the code

The code is as follows:

public class StayConsumer extends SwipeConsumer {
    private int mMinVelocity = 1000;

    public StayConsumer(a) {
        // Can not determine whether to open by sliding distance
        setOpenDistance(Integer.MAX_VALUE)
                .setMaxSettleDuration(0); // Open without animation, time set to 0
    }

    @Override
    protected void onDisplayDistanceChanged(int distanceXToDisplay, int distanceYToDisplay, int dx, int dy) {
        // No changes to the contentView are required when sliding
    }

    @Override
    public void onSwipeReleased(float xVelocity, float yVelocity) {
        // When releasing, decide whether to open according to the speed and direction
        if (Math.abs(xVelocity) > Math.abs(yVelocity)) {
            if (mDirection == DIRECTION_LEFT && xVelocity >= mMinVelocity || (mDirection == DIRECTION_RIGHT && xVelocity <= -mMinVelocity)) {
                // Set it to open
                mCurSwipeDistanceX = getSwipeOpenDistance();
                mProgress = 1; }}else {
            if (mDirection == DIRECTION_TOP && yVelocity >= mMinVelocity || (mDirection == DIRECTION_BOTTOM && yVelocity <= -mMinVelocity)) {
                // Set it to open
                mCurSwipeDistanceY = getSwipeOpenDistance();
                mProgress = 1; }}super.onSwipeReleased(xVelocity, yVelocity);
    }

    public int getMinVelocity(a) {
        return mMinVelocity;
    }

    // Users can set the minimum rate threshold
    public StayConsumer setMinVelocity(int minVelocity) {
        if (minVelocity > 0) {
            this.mMinVelocity = minVelocity;
        }
        return this; }}Copy the code

Isn’t that easy too!

Click here for detailed steps to create a custom SwipeConsumer

summary

This article introduces the application and implementation principle of SmartSwipe sideswipe framework, and introduces the method of customizing sideswipe effect through two examples.

However, the example in this paper is a relatively simple sideslip effect. As for the introduction of complex sideslip effect, if readers need it, I will write another article to introduce it separately. If necessary, please leave a message to me!

In addition, Star an open source project is the best encouragement and support for it!