Introduction to the

It’s common to use a sideside Activity to return, such as wechat. So how does it work. This article takes you through the implementation principle. I found an open source star with 2.6K on Github, and we analyzed how he implemented it

/ / star 2.6 k 'com. R0adkll: slidableactivity: at 2.0.5'Copy the code

Example of Slidr

It is very simple to use, first to set the transparent window background

    <style name="AppTheme"  parent="Theme.AppCompat.Light.DarkActionBar">
        <! -- Customize your theme here. -->
        <item name="android:textAllCaps">false</item>
        <item name="android:windowActionBar">false</item>
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowBackground">@android:color/transparent</item>
    </style>
Copy the code

then

/ / setContent (View View) after Slidr. Attach (this);Copy the code

Here’s how it works in three steps

Step 1 Repackage the interface

Slidr.class

    public static SlidrInterface attach(final Activity activity, final int statusBarColor1, final int statusBarColor2){
        //0 create a sliding nested interface SliderPanel
		final SliderPanel panel = initSliderPanel(activity, null);

        //7 Set the panel slide listener for when it becomes closed or opened
        // Listen for callbacks
        panel.setOnPanelSlideListener(new SliderPanel.OnPanelSlideListener() {
			...
            Open/close, etc
        });

		// Return the lock interface
		return initInterface(panel);
    }

	private static SliderPanel initSliderPanel(final Activity activity, final SlidrConfig config) {
		Decorview / / 3
		ViewGroup decorView = (ViewGroup)activity.getWindow().getDecorView();
        
        //4 Get the content of our layout and delete it
		View oldScreen = decorView.getChildAt(0);
		decorView.removeViewAt(0);

		//5 Setup the slider panel and attach it to the decor
        // Create a SliderPanel and add it to the DecorView
		SliderPanel panel = new SliderPanel(activity, oldScreen, config);
		panel.setId(R.id.slidable_panel);
		oldScreen.setId(R.id.slidable_content);
        
        //6 Add our interface layout to the SliderPanel and add the SliderPanel to the decorView
		panel.addView(oldScreen);
		decorView.addView(panel, 0);
		return panel;
	}

Copy the code

Step 2 UseViewDragHelper.classDealing with sliding gestures

SliderPanel.class

private void init(a){.../ / 1 ViewDragHelper created
    mDragHelper = ViewDragHelper.create(this, mConfig.getSensitivity(), callback);
    mDragHelper.setMinVelocity(minVel);
    mDragHelper.setEdgeTrackingEnabled(mEdgePosition);

    //2 Setup the Dimmer View
    mDimView = new View(getContext());
    mDimView.setBackgroundColor(mConfig.getScrimColor());
    mDimView.setAlpha(mConfig.getScrimStartAlpha());
    addView(mDimView);
}
Copy the code

Step 3 handles the drag of our interface in viewDragHelper.callback

Let’s make it clear that the ViewDragHelper only handles the relationship between ParentView and its child views, and does not traverse all the way to the top View. This is how capture for the ViewDragHelper is implemented

    @Nullable
    public View findTopChildUnder(int x, int y) {
        final int childCount = mParentView.getChildCount();
        for (int i = childCount - 1; i >= 0; i--) {
            final View child = mParentView.getChildAt(mCallback.getOrderedChildIndex(i));
            if (x >= child.getLeft() && x < child.getRight()
                    && y >= child.getTop() && y < child.getBottom()) {
                returnchild; }}return null;
    }
Copy the code

The focus is on the implementation of the ViewDragHelper.callback Callback in sliderPanel.class, The author implements many directions of sliding processing mLeftCallback, mRightCallback, mTopCallback, mBottomCallback, mVerticalCallback, mHorizontalCallback. Let’s take mLeftCallback for analysis

private ViewDragHelper.Callback mLeftCallback = new ViewDragHelper.Callback() {

    / / to capture the View
    @Override
    public boolean tryCaptureView(View child, int pointerId) {
        booleanedgeCase = ! mConfig.isEdgeOnly() || mDragHelper.isEdgeTouched(mEdgePosition, pointerId);// As mentioned earlier, our content is the top layer of the subview, mDecorView in this case refers to our contentView
        return child.getId() == mDecorView.getId() && edgeCase;
    }

    // Drag, and finally move through view.offsetLeftAndRight(offset)
    @Override
    public int clampViewPositionHorizontal(View child, int left, int dx) {
        return clamp(left, 0, mScreenWidth);
    }

    // Slide range
    @Override
    public int getViewHorizontalDragRange(View child) {
        return mScreenWidth;
    }

    // Release processing to determine whether to roll back to the screen
    @Override
    public void onViewReleased(View releasedChild, float xvel, float yvel) {
        super.onViewReleased(releasedChild, xvel, yvel);

        int left = releasedChild.getLeft();
        int settleLeft = 0;
        int leftThreshold = (int) (getWidth() * mConfig.getDistanceThreshold());
        boolean isVerticalSwiping = Math.abs(yvel) > mConfig.getVelocityThreshold();

        if(xvel > 0) {if(Math.abs(xvel) > mConfig.getVelocityThreshold() && ! isVerticalSwiping){ settleLeft = mScreenWidth; }else if(left > leftThreshold){ settleLeft = mScreenWidth; }}else if(xvel == 0) {if(left > leftThreshold){ settleLeft = mScreenWidth; }}// Scroll to left=0(normal layout) or left=mScreenWidth (scroll out of screen) to close the Activity
        mDragHelper.settleCapturedViewAt(settleLeft, releasedChild.getTop());
        invalidate();
    }

    // Convert the position percentage to determine the transparency of the indicator layer
    @Override
    public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
        super.onViewPositionChanged(changedView, left, top, dx, dy);
        float percent = 1f - ((float)left / (float)mScreenWidth);

        if(mListener ! =null) mListener.onSlideChange(percent);

        // Update the dimmer alpha
        applyScrim(percent);
    }

    // Callback to Slidr to handle the Activity state
    @Override
    public void onViewDragStateChanged(int state) {
        super.onViewDragStateChanged(state);
        if(mListener ! =null) mListener.onStateChanged(state);
        switch (state){
            case ViewDragHelper.STATE_IDLE:
                if(mDecorView.getLeft() == 0) {// State Open
                    if(mListener ! =null) mListener.onOpened();
                }else{
                    // State Closed here callback to Slidr processing activity. Finish ()
                    if(mListener ! =null) mListener.onClosed();
                }
                break;
            case ViewDragHelper.STATE_DRAGGING:

                break;
            case ViewDragHelper.STATE_SETTLING:

                break; }}};Copy the code

For mDragHelper. SettleCapturedViewAt (settleLeft, releasedChild getTop ()); Inside, scroller.class is used to aid rolling, so override View.computeScroll() in SliderPanel.

@Override
public void computeScroll(a) {
    super.computeScroll();
    if(mDragHelper.continueSettling(true)){
        ViewCompat.postInvalidateOnAnimation(this); }}Copy the code

conclusion

The overall scheme is shown in the following figure

In general, the principle is not complicated, just dragging the View through the ViewDragHelper.