preface

The sideslip gesture is widely used on Android apps. It can be used in many ways, such as swiping drawers, swiping to delete, swiping to return, swiping to refresh, and swiping to cover. Because these use scenarios are too general, each of the gods of the eight immortals across the sea to show their abilities, each side slide scenarios are open source out of a lot of very practical framework, so that our business development is a lot of convenience.

At present, we need to introduce different sides-slide frames for each scenario. Since there are many sides-slide scenes in the App, we need to introduce multiple sides-slide frames in our project, and the use of each framework is different, which needs to be learned separately, and the learning cost of the team is high.

So the question is, is there a framework that solves all the sideslip requirements?

One framework for all sideslip needs? Are you sure you’re not kidding?

When we first learned the concept of object-oriented programming, we knew that to solve a software problem, we must first abstract it.

In view of the sideslip gesture, can we abstract the concept of it, what exactly is sideslip?

  • Narrow slide: Start from the edge of one side of the screen and slide away from that edge
  • Generalized sideslip: Swipe a finger in one direction after it is pressed on the screen

My understanding is that a broad sideslip includes a narrow sideslip, it’s just a matter of whether the trigger area is at the edge of the screen or not.

Thus, the concept of sideslip is clearly abstracted.

From this abstraction, we can see that the swivel gesture only deals with one of four directions at a timeCopy the code

If we encapsulate this abstract concept, process the recognition, interception and data of gesture events within the framework, and output the sideslip direction, distance and related callback in real time, theoretically we can achieve all the sideslip requirements.

As for the specific sideslip effect, those who have learned the strategy pattern know that each specific sideslip effect can be seen as a sideslip strategy.Copy the code

Said so mysterious, in the end how?

Chest up! Hold your breath! Sharpening the knife does not miss wood, standing on the shoulders of the giant you may be a little taller than the giant.

Google provides SlidingPaneLayout and DrawerLayout in the Android Support library for the requirements of the slide menu. The source code will find that both are based on the ViewDragHelper to implement. So what is ViewDragHelper?

ViewDragHelper is a utility class in the Android Support library. It helps with dragging and dropping controls. It is used as follows: Create a custom ViewGroup, add the control to the custom ViewGroup, and use the ViewDragHelper to handle the dragging of the control. You can use the Callback to specify the drag area and capture the logic of the child control.

By reading the source of ViewDragHelper, it is found that the view in the parent container of the dragging behavior of the encapsulation, by intercepting the parent container control gesture event, capture the need to drag the child control, and real-time according to the movement of the finger to change its coordinates, so as to achieve the dragging effect.

ViewDragHelper encapsulation is very elegant, is also very strong, and some open source framework is implemented based on ViewDragHelper of sideslip, for example: ikew0ng/SwipeBackLayout/daimajia/AndroidSwipeLayout

However, the ViewDragHelper encapsulates drag and drop of child controls, not slide. it calculates distances based on the top and left coordinates of the controls. It is possible to simulate a slide gesture by setting the drag range in one direction (landscape or portrait) to 0, but it does not fit our abstract definition of a slide gesture. Unable to resolve the effect of sideslip when the control is not moving.

For example, the side-slip return effect of MIUI system and the elastic stretching effect commonly used in the App produced by Xiaomi Company, etc

Stop with the nonsense and talk about sideslip

Now that sideslip is clearly abstracted, we can use the idea of ViewDragHelper to handle touch slideslip events: The capture and drag of the child control is changed to the capture of the sideslip direction and the calculation of the sideslip distance, and its Callback is transformed into the consumer of the sideslip distance (the specific sideslip effect depends on which way the consumer consumes the sideslip distance).

There are two key factors of sideslip behavior: sideslip direction and sideslip distance

With this in mind, I have encapsulated a smart sideswipe framework: SmartSwipe, which solves your (Chui) sideswipe needs (NIU). Please shout out its slogan!

For sideslip, this one is enough

Of course, this is bragging!

The framework just encapsulates the capture and distribution of sideslip behavior events and the processing of multi-point alternate slideslip. The specific sideslip effect (the strategy of consuming sideslip distance) needs to be realized by yourself. Ah… Wait! Boobs! Don’t go yet! That’s not all. SmartSwipe has more than a dozen common sideslide effects built in, as evidenced by GIFs:

1. A line of code makes the page move

// Copy the iOS elastic white space effect:
// In the case of sideslip, it is shown as elastic white space effect, and automatically recovers after the end
SmartSwipe.wrap(view)
    .addConsumer(new SpaceConsumer())
    .enableVertical(); // Work direction: vertical
Copy the code

2. A line of code makes the page elastic

// Copy MIUI elastic tensile effect:
// In the case of sideslip, it shows the effect of elastic stretching and automatically recovers after the end
SmartSwipe.wrap(view)
    .addConsumer(new StretchConsumer())
    .enableVertical(); // Work direction: vertical
Copy the code

3. Add slide drawer with one line of code

The drawer is displayed above the main view, similar to DrawerLayout

SmartSwipe.wrap(view)
    .addConsumer(new DrawerConsumer())    // The drawer effect
    // You can set the horizontal (left and right) drawer as the same view
    // You can also set different views for different directions
    .setHorizontalDrawerView(menuLayout) 
    .setScrimColor(0x2F000000) // Set the color of the mask
    .setShadowColor(0x80000000)    // Set the edge shadow color
    ;
Copy the code

4. Add slide drawer with linkage effect with one line of code

The drawer is displayed under the main view

SmartSwipe.wrap(view)
    .addConsumer(new SlidingConsumer())
    .setHorizontalDrawerView(textView)
    .setScrimColor(0x2F000000)
    // Set the linkage coefficient
    // 0: no linkage, the visual effect is: the bottom drawer is displayed after the main body is moved
    // 0~1: half linkage. The visual effect is: the drawer view has a relative movement effect with the main body according to the linkage coefficient
    // 1: Full linkage, visual effect: the drawer moves with the main body (Pixel by pixel)
    .setRelativeMoveFactor(0.5 F);Copy the code

5. Add slide transparency with one line of code

Sideslip transparent effect, after sideslip can show the view blocked by it, can be used for sideslip deletion, can also be used to make cover effect

// Side slip delete
SmartSwipe.wrap(view)
    .addConsumer(new TranslucentSlidingConsumer())
    .enableHorizontal() // Enable side-slip on both sides
    .addListener(new SimpleSwipeListener(){
        @Override
        public void onSwipeOpened(SmartSwipeWrapper wrapper, SwipeConsumer consumer, int direction) {
            // Remove sideslip when open
            ViewParent parent = wrapper.getParent();
            if (parent instanceof ViewGroup) {
                ((ViewGroup) parent).removeView(wrapper);
            }
            //adapter.removeItem(getAdapterPosition()); // Can also be used to remove this from recyclerView}});Copy the code

6. Add sideslip gesture recognition with one line of code

During sideslip, the main view stays still, and when the finger is released, the direction and speed of the slide are identified to determine whether the corresponding sideslip logic is executed.

// Demo: use StayConsumer to do the activity slide back
SmartSwipe.wrap(this)
    .addConsumer(new StayConsumer())
    .enableAllDirections()
    .addListener(new SimpleSwipeListener(){
        @Override
        public void onSwipeOpened(SmartSwipeWrapper wrapper, SwipeConsumer consumer, int direction) { finish(); }});Copy the code

7. Add the louver effect with one line of code

The main view opens like a shutter when sliding sideways, transparently showing the lower view.

Can be used to make cover, wheel, etc

// Use ShuttersConsumer to remove the shutters
SmartSwipe.wrap(view)
    .addConsumer(new ShuttersConsumer())
    .enableHorizontal() // Enable side-slip on both sides
    .addListener(new SimpleSwipeListener(){
        @Override
        public void onSwipeOpened(SmartSwipeWrapper wrapper, SwipeConsumer consumer, int direction) {
            // Remove sideslip when open
            ViewParent parent = wrapper.getParent();
            if (parent instanceof ViewGroup) {
                ((ViewGroup) parent).removeView(wrapper);
            }
            //adapter.removeItem(getAdapterPosition()); // Can also be used to remove this from recyclerView}});Copy the code

8. Add a line of code to open the door

When sliding sideways, the main view opens like a door from the middle to the sides (up, down, or left and right), transparently showing the view below it

Can be used to make cover, wheel, etc

// Use DoorConsumer to remove the blinds
SmartSwipe.wrap(view)
    .addConsumer(new DoorConsumer())
    .enableHorizontal() // Enable side-slip on both sides
    .addListener(new SimpleSwipeListener(){
        @Override
        public void onSwipeOpened(SmartSwipeWrapper wrapper, SwipeConsumer consumer, int direction) {
            // Remove sideslip when open
            ViewParent parent = wrapper.getParent();
            if (parent instanceof ViewGroup) {
                ((ViewGroup) parent).removeView(wrapper);
            }
            //adapter.removeItem(getAdapterPosition()); // Can also be used to remove this from recyclerView}});Copy the code

9. Add bezier curve return effect with one line of code

When sideslip, a bezier curve return effect is displayed on the edge of the sideslip control

It can be used for activity return, fragment return, and webView return/forward

// The activity slides back
SmartSwipe.wrap(this)
    .addConsumer(new BezierBackConsumer())
    .enableAllDirections()
    .addListener(new SimpleSwipeListener() {
        @Override
        public void onSwipeOpened(SmartSwipeWrapper wrapper, SwipeConsumer consumer, int direction) { finish(); }});Copy the code

10. Add a line of code to mimic wechat Activity linkage side slide return effect

That’s right, an effect designed for an activity to slide back sideways, with linkage

// The activity slides back
SmartSwipe.wrap(this)
    .addConsumer(new ActivitySlidingBackConsumer(this))
    // Set the linkage coefficient
    .setRelativeMoveFactor(0.5 F)
    // Specify the direction that can be slid back. For example: enableLeft() Only the left side can be slid back
    .enableAllDirections() 
    ;
Copy the code

11. Adds a single line of code to the Activity shutter slide back effect

Yes, it’s also an effect designed for an activity to slide back sideways, showing the previous activity transparently

// The activity slides back
SmartSwipe.wrap(this)
    .addConsumer(new ActivityShuttersBackConsumer(this))
    .setScrimColor(0x7F000000)
    .enableAllDirections()
    ;
Copy the code

12. Add a single line of code to the Activity door slide return effect

Yes, this is also an effect for an activity to slide back sideways, transparently showing the previous activity

// The activity slides back
SmartSwipe.wrap(this)
    .addConsumer(new ActivitySlidingBackConsumer(this))
    .setRelativeMoveFactor(0.5 F)
    .enableAllDirections()
    ;
Copy the code

Why is it all one line of code? Do you want some more?

Most of the use in SmartSwipe can be done in one line of code via chip-programming. The API design style is as follows:

SmartSwipe.wrap(...) 		//view or Activity
	.addConsumer(...) 		/ / add the consumer
	.enableDirection(...) 	// Specify which direction the consumer receives the sideslip event
	.setXxx(...) 			// Some other Settings are optional.addListener(...) ;//[Optional] Add listener to consumer
Copy the code

In addition to the basic slippage effect, the tool classes SmartSwipeBack and SmartswiperRefresh are encapsulated for developer convenience

One line of code to achieve the global Activity sideslip return

  • All activities slide back in a single line of code
  • Optional styles: door opening, shutter, imitation wechat, imitation QQ and imitation MIUI Bezier curve
  • No need for transparent themes
  • There is no need to inherit a specific Activity
  • There is no need to hack into the XML layout file
  • You don’t need to hack into BaseActivity
  • Support for full screen sideslip and/or edge sideslip return
  • Support up/down/left/right four directions to slide back
// Copy the mobile phone QQ gesture slide back
SmartSwipeBack.activityStayBack(application, null);		
// Copy the transparent sideslip return with linkage effect of wechat
SmartSwipeBack.activitySlidingBack(application, null);	
// Turn off the activity
SmartSwipeBack.activityDoorBack(application, null);		
// Turn off the activity
SmartSwipeBack.activityShuttersBack(application, null);	
// Copy the MIUI system bezier curve return effect
SmartSwipeBack.activityBezierBack(application, null);
Copy the code

One line of code adds the drop-down refresh function

Can be used with any view

//xxxMode The second parameter is false, indicating that the working direction is vertical: refresh down & load more up
// If the second parameter is set to true, the work direction is landscape: pull right to refresh & pull 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
style rendering
drawerMode
behindMode
scaleMode
translateMode

Header and Footer can use third-party cool custom view, such as: ArrowHeader based on Ifxcyr/ArrowDrawable, the renderings are as follows:

It looks pretty diǎo, but you don’t have the side slide effect I want

To do this, you need to customize SwipeConsumer as follows:

    1. Create a new class that inherits SwipeConsumer
    1. [Optional] Do some initialization in the constructor (properties that require context to be initialized can be initialized in the onAttachToWrapper method)
    1. [Optional] If there is additional capture logic, you can override the parenttryAcceptMovingandtryAcceptSettlingmethods
    1. [Optional] Override the onSwipeAccepted method. Since the sideside event is captured and the mDirection of the sideside event is determined, some initialization work can be done for the event
    1. Optional. RewriteclampDistanceHorizontalandclampDistanceHorizontalMethod, sideslip can be implemented only when certain conditions are met
    1. Override the onDisplayDistanceChanged method to perform a specific UI rendering of the slide
    1. [Optional] If the UI rendering effect contains movement of the layout control, you need to override the onLayout method, which also positions the control layout according to the logic after the slide
    1. Override the onDetachFromWrapper method to restore the scene and remove any trace of changes to the current Consumer

Take frame built in StretchConsumer as an example

The contentView is scaled and panned according to the sideslip distance to achieve an 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

So that’s all the code for elastic stretching. Easy, right?

So maybe all the sideslip effects can be achieved?

Can achieve all the sideslip effect only exists in theory, certainly need to continue to improve, open source is also hope to use the power of the open source community to improve it, make Android sideslip easier!

How did you do that?

The general package for SmartSwipe is as follows:

  • Wrap a ViewGroup around the control View that needs to handle the slide event, with the wrapped control as itscontentView, you can add some subsidiary controls to the ViewGroup (such as slide drawer)
  • Intercept the ViewGroup’s touch event and convert the Touch event to the SwipeConsumer for consumption
  • SwipeConsumer in the course of the consumption sideside event, rightcontentViewAnd the UI presentation (position, zoom, transparency, etc.) of the subsidiary controls for reasonable processing, so as to achieve various sideslip effects.

Define the roles of the key classes in the framework:

  • SmartSwipe:
    • Frame entry class
    • Use to wrap a View and return the SmartSwipeWrapper that wraps it
  • SmartSwipeWrapper:
    • Custom control, inherited from ViewGroup, more efficient than FrameLayout
    • The view to which the sideslip effect is added is called the view it wrapscontentView
    • With it, developers no longer need to customize the ViewGroup, making it easier to use
  • SwipeConsumer:
    • Base class for sideslip strategies
    • Complete some common logic, such as: sideslip event capture strategy, sideslip distance away calculation, etc
    • The setting of public properties and the corresponding logical processing
  • SwipeListener:
    • Sideslip life cycle listener class
    • Added to SwipeConsumer, each callback function is triggered in SwipeConsumer’s lifecycle
  • SwipeHelper:
    • Modified according to ViewDragHelper

1. Associate SmartSwipeWrapper, SwipeHelper, and SwipeConsumer

Similar to using ViewDragHelper, SwipeHelper is used in SmartSwipeWrapper to handle touch events

public class SmartSwipeWrapper extends ViewGroup {
    protected SwipeHelper mHelper;
    // Omit the number of code...
    
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mHelper.shouldInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mHelper.processTouchEvent(event);
        return true; }}Copy the code

SwipeHelper intercepts SwipeHelper events, which need to be passed to SwipeConsumer for consumption, so you need to associate SwipeConsumer with SwipeHelper (SwipeConsumer takes the role of Callback in ViewDragHelper).

public class SmartSwipeWrapper extends ViewGroup {
    protected SwipeConsumer mConsumer;
    protected SwipeHelper mHelper;
    // Omit the number of code...
    
    public <T extends SwipeConsumer> T addConsumer(T consumer) {
        if(consumer ! =null) {
            mConsumer = consumer;
            mHelper = SwipeHelper.create(this, consumer.getSensitivity(), consumer, consumer.getInterpolator());
            // Omit the number of code...
        }
        returnconsumer; }}Copy the code

Thus, the SmartSwipeWrapper, SwipeHelper, and SwipeConsumer are related

2. Intercept touch events in SwipeHelper and

public class SwipeHelper {
    // SwipeConsumer object associated with the current SwipeHelper
    private final SwipeConsumer mSwipeConsumer;
    
    // The current X axis sideslip distance
    private int mClampedDistanceX;
    // The current Y-axis sideslip distance
    private int mClampedDistanceY;
    
    // Omit the number of code...

    public boolean shouldInterceptTouchEvent(MotionEvent ev) {
        final int action = ev.getActionMasked();
        // Omit the number of code...

        switch (action) {
            case MotionEvent.ACTION_DOWN: {
                // Omit the number of code...
                if (mDragState == STATE_SETTLING || mDragState == STATE_NONE_TOUCH) {
                    trySwipe(pointerId, true, x, y, 0.0);
                }
                break;
            }
            case MotionEvent.ACTION_POINTER_DOWN: {
                // Omit the number of code...
                if (mDragState == STATE_SETTLING || mDragState == STATE_NONE_TOUCH) {
                    trySwipe(pointerId, true, x, y, 0.0);
                }
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                // Omit the number of code...
                for (int i = 0; i < ev.getPointerCount(); i++) {
                    // Omit the number of code...
                    if (trySwipe(pointerId, false, downX, downY, dx, dy)) {
                        break; }}break;
            }
            // Omit the number of code...
        }
        return mDragState == STATE_DRAGGING;
    }

    public void processTouchEvent(MotionEvent ev) {
        final int action = ev.getActionMasked();
        // Omit the number of code...

        switch (action) {
            case MotionEvent.ACTION_DOWN: {
                // Omit the number of code...
                trySwipe(pointerId, mDragState == STATE_SETTLING || mDragState == STATE_NONE_TOUCH, x, y, 0.0);
                break;
            }

            case MotionEvent.ACTION_POINTER_DOWN: {
                // Omit the number of code...
                trySwipe(pointerId, true, x, y, 0.0);
                break;
            }

            case MotionEvent.ACTION_MOVE: {
                if (mDragState == STATE_DRAGGING) {
                    // Omit the number of code...
                    dragTo(mClampedDistanceX + idx, mClampedDistanceY + idy, idx, idy);
                } else {
                    for (int i = 0; i < pointerCount; i++) {
                        // Omit the number of code...
                        if (trySwipe(pointerId, false, downX, downY, dx, dy)) {
                            break;
                        }
                    }
                    saveLastMotion(ev);
                }
                break;
            }

            case MotionEvent.ACTION_POINTER_UP: {
                final int pointerId = ev.getPointerId(actionIndex);
                if (mDragState == STATE_DRAGGING && pointerId == mActivePointerId) {
                    // Omit the number of code...
                    for (int i = 0; i < pointerCount; i++) {
                        // Omit the number of code...
                        if (trySwipe(id, true, mInitialMotionX[id], mInitialMotionX[id], 0.0)) {
                            newActivePointer = mActivePointerId;
                            break; }}// Omit the number of code...
                }
                break;
            }
            // Omit the number of code...}}private boolean trySwipe(int pointerId, boolean settling, float downX, float downY, float dx, float dy) {
        // Omit the number of code...
        boolean swipe;
        if (settling) {
            // After release, when the animation is played, the finger starts to slide again to determine whether to capture
            swipe = mSwipeConsumer.tryAcceptSettling(pointerId, downX, downY);
        } else {
            // Determine whether to capture the touch event as a sideslip by sliding the position and distance
            swipe = mSwipeConsumer.tryAcceptMoving(pointerId, downX, downY, dx, dy);
        }
        if (swipe) {
            // Omit the number of code...
            
            // Make sure the capture is successful, and then consume the sidesslip event by mSwipeConsumer
            // You can do some preparatory work in the onSwipeAccepted method
            mSwipeConsumer.onSwipeAccepted(pointerId, settling, initX, initY);
            // Initialize the sideslip distance
            mClampedDistanceX = mSwipeConsumer.clampDistanceHorizontal(0.0);
            mClampedDistanceY = mSwipeConsumer.clampDistanceVertical(0.0);
            setDragState(STATE_DRAGGING);
            return true;
        }
        return false;
    }
    // Omit the number of code...
}
Copy the code

SwipeHelper intercepts touch events by taking over the SmartSwipeWrapper onInterceptTouchEvent and onTouchEvent. When the touch events such as ACTION_DOWN and ACTION_MOVE are triggered, SwipeConsumer can determine whether to capture the touch events by passing the status of the touch events to trySwipe.

SwipeConsumer decides to capture the Touch event as a slide gesture, and its onSwipeAccepted method is called, where you can do some preparatory work for the slide

3. SwipeConsumer determines whether and how to consume Touch events

public abstract class SwipeConsumer {
    
    protected int mDirection;
    // Omit the number of code...

    /** Determine whether to capture (consume this slide event) after releasing the animation playback state or the alternate swipe state. */
    public boolean tryAcceptSettling(int pointerId, float downX, float downY) {
        // Omit the number of code...
        returnisDirectionEnable(mDirection) && ! isDirectionLocked(mDirection); }/** Stationary state according to the move distance to determine whether to capture (whether to consume the slide event) */
    public boolean tryAcceptMoving(int pointerId, float downX, float downY, float dx, float dy) {
        // Omit the number of code...
        int dir = DIRECTION_NONE;
        boolean handle = false;
        if (Math.abs(dx) > Math.abs(dy)) {
            if (dx > 0 && isLeftEnable()) {
                dir = DIRECTION_LEFT;
                handle = true;
            } else if (dx < 0 && isRightEnable()) {
                dir = DIRECTION_RIGHT;
                handle = true; }}else {
            if (dy > 0 && isTopEnable()) {
                dir = DIRECTION_TOP;
                handle = true;
            } else if (dy < 0 && isBottomEnable()) {
                dir = DIRECTION_BOTTOM;
                handle = true; }}// Omit the number of code...
        if (handle) {
            if (isDirectionLocked(dir)) {
                handle = false;
            } else{ mDirection = dir; }}returnhandle; }}Copy the code

SwipeConsumer determines whether to capture the touch event as a sideside event based on its Settings (whether the sideside direction is enabled, locked, etc.) and the coordinate and direction of the touch event.

How does SwipeConsumer consume the sideside distance

public abstract class SwipeConsumer {
    // Omit the number of code...
    
    // Consumption sideslip distance
    public void onSwipeDistanceChanged(int clampedDistanceX, int clampedDistanceY, int dx, int dy) {
        // Omit the number of code...
        
        // The sideslip distance does change
        if(clampedDistanceX ! = mCurSwipeDistanceX || clampedDistanceY ! = mCurSwipeDistanceY) {// Record the actual sideslip distance
            mCurSwipeDistanceX = clampedDistanceX;
            mCurSwipeDistanceY = clampedDistanceY;
            // Omit the number of code...
            
            // Calculate the sideslip progress: current sideslip distance/maximum sideslip distance
            switch (mDirection) {
                case DIRECTION_LEFT: case DIRECTION_RIGHT:
                    mProgress = Math.abs((float) mCurSwipeDistanceX / mSwipeOpenDistance);
                    break;
                case DIRECTION_TOP: case DIRECTION_BOTTOM:
                    mProgress = Math.abs((float) mCurSwipeDistanceY / mSwipeOpenDistance);
                    break;
                default:}if ((mDirection & DIRECTION_HORIZONTAL) > 0) { // Lateral sideslip
                int realDistanceX = clampedDistanceX;
                if(mSwipeDistanceCalculator ! =null) { // Set the sideslip distance calculator
                    // Use the sideslip distance calculator to process the current sideslip distance
                    realDistanceX = mSwipeDistanceCalculator.calculateSwipeDistance(clampedDistanceX, mProgress);
                }
                // Calculate the sideslip distance and delta value for the UI display
                dx = realDistanceX - mCurDisplayDistanceX;
                dy = 0;
                mCurDisplayDistanceX = realDistanceX;
            } else if ((mDirection & DIRECTION_VERTICAL) > 0) {
                int realDistanceY = clampedDistanceY;
                if(mSwipeDistanceCalculator ! =null) {
                    realDistanceY = mSwipeDistanceCalculator.calculateSwipeDistance(clampedDistanceY, mProgress);
                }
                dx = 0;
                dy = realDistanceY - mCurDisplayDistanceY;
                mCurDisplayDistanceY = realDistanceY;
            }
            // Use the processed sideslip distance for UI display. This method is abstract and implemented in subclasses
            onDisplayDistanceChanged(mCurDisplayDistanceX, mCurDisplayDistanceY, dx, dy);
        }
        // Omit the number of code...
    }
    
    /** * defines an abstract method * subclass to display the UI according to the processed sideslip distance */
    protected abstract void onDisplayDistanceChanged(int distanceXToDisplay, int distanceYToDisplay, int dx, int dy);
}
Copy the code

Implement the onDisplayDistanceChanged abstract method in the subclass, where the UI is displayed according to the slide distance.

Through the above analysis, we can know: The work of defining sidesslides (whether and whether they are captured), orienting (the sidesslide direction triggered by the captured event), and quantifying (the distance they move in the sidesslide direction after the event is captured) is done in the base class SwipeConsumer. The exact sideshang effect is done in the onDisplayDistanceChanged method implemented in the subclass.

However, many sideslip effects require modification in the layout and Draw layers of the control

4. OnLayout, onDraw and dispatchDraw of SwipeConsumer

By default, SmartSwipeWrapper has only one child: contentView. If the slide effect requires additional child controls (such as a slide drawer), the specific SwipeConsumer class performs the layout. SwipeConsumer can also accept draw-related callbacks

public class SmartSwipeWrapper extends ViewGroup {

    protected SwipeConsumer mConsumer;
    protected View mContentView;
    // Omit the number of code...
    
    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        if(mConsumer ! =null) { mConsumer.dispatchDraw(canvas); }}@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if(mConsumer ! =null) { mConsumer.onDraw(canvas); }}@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // Omit the number of code...
        if(mConsumer ! =null) { mConsumer.onMeasure(widthMeasureSpec, heightMeasureSpec); }}@Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        boolean layoutByConsumer = false;
        if(mConsumer ! =null) {
            layoutByConsumer = mConsumer.onLayout(changed, left, top, right, bottom);
        }
        if(! layoutByConsumer) {// The default layout is just the contentView
            if(mContentView ! =null) {
                mContentView.layout(0.0, mContentView.getMeasuredWidth(), mContentView.getMeasuredHeight()); }}}}Copy the code

You are tired of talking and I am tired of watching. It’s time to sum up

SmartSwipe is a sideswipe processing framework, not a specific sideswipe effect, although it provides more than a dozen common sideswipe effects internally.

We can also use it to achieve more complex sideslip effects. You can also use these effects to achieve some interesting features (e.g., different side slide effects in demo for cover, side slide delete, pulldown refresh, etc.)

The SmartSwipe framework has been introduced, demonstrated, and the core workflow has been completed. Due to the limited space, the introduction is relatively brief, you can follow the following link address to view the source code and use gitbook form carefully written documentation tutorial:

Source: github.com/luckybilly/…

Documents: luckybilly. Making. IO/SmartSwipe -… (Carefully written in gitbook form)

Demo download: github.com/luckybilly/…

Finally: check out my Github page for more projects you might be interested in 🙂 github.com/luckybilly