An overview of the

Recently in the use of cool dog music found that cool dog music home page slide effect is good, can not help hand itch, just want to achieve a look

Effect analysis

  1. We can see that there are two parts (menu page, theme page) so we use a custom ViewGroup here
  2. When you swipe to the left, you get an animation that zooms in and out, and you basically know that you need to handle the touch event, and the animation of the touch
  3. Fast sliding on/off processing (GestureDetector packing processing class)

Implementation approach

  1. Here we choose to inherit the HorizontalScrollView horizontal scrolling layout, and then write the layout file. This is the same as the HorizontalScrollView, first scroll to the main page, and in onTouch control the layout scaling animation based on the sliding distance

implementation

public KGSlidingMenu(Context context) {
    this(context, null);
}

public KGSlidingMenu(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

public KGSlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}
Copy the code

After running it, we found that the layout was wrong and completely confused. Here we set the width of the menu page and theme page in the code

  • Menu page width here we define a property menu_width
  • Theme page width = screen width
private View mMenuView; private View mContainerView; private int mMenuWidth; Public KGSlidingMenu(Context Context) {this(Context, null); } public KGSlidingMenu(Context context, AttributeSet attrs) { this(context, attrs, 0); } public KGSlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.KGSlidingMenu); mMenuWidth = Math.round(ta.getDimension(R.styleable.KGSlidingMenu_menu_width, 325)); ta.recycle(); } @override protected void onFinishInflate() {// execute super.onfinishinflate () in onCreate; // Specify width and height: LinearLayout ViewGroup mRootView = (ViewGroup) LinearLayout View = (ViewGroup) getChildAt(0); if (mRootView == null) { throw new NullPointerException("child can not be null !!!" ); } if (mRootView.getChildCount() ! = 2) { throw new RuntimeException("Only two can be placed View !!!" ); } mMenuView = mRootView.getChildAT (0); mContainerView = mRootView.getChildAt(1); / / to the menu layout Settings specified width Give the container layout Settings screen high/wide/only through layoutParams set wide high mMenuView. GetLayoutParams () width = mMenuWidth; mContainerView.getLayoutParams().width = getScreenWidth(mContext); } public int getScreenWidth(Context mContext) {WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics displayMetrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(displayMetrics); return displayMetrics.widthPixels; }Copy the code

The current effect is that it can be swiped, and the layout width of the menu and main page content is normal.

  1. On entry, the menu is open, normally closed
  2. When the finger is raised, the current position should be used to determine whether to open or close the menu
  3. There is no corresponding animation when sliding

  • The first point is that when you first come in, the menu is opened. This point we can scroll to the corresponding position of KGSlidingMenu when the layout is finished. The specific implementation is as follows
Override protected void onLayout(Boolean changed, int l, int t, int r, int b) {Override protected void onLayout(Boolean changed, int l, int t, int r, int b) l, t, r, b); ScrollTo (mMenuWidth, 0); scrollTo(mMenuWidth, 0); }Copy the code
  • The second point is that when the finger is raised, the menu should be opened or closed according to the current position. To judge whether the menu is open or closed, compare the distance we slide with the width of the menu

    1). When closing, slide to the right. If the sliding distance is less than half of the menu width, this is when we lift the finger, we should close the menu, and when it is more than half of the menu width, we should open the menu.

    2). When opening, slide to the left. If the sliding distance is less than half of the menu width, this is when we lift the finger, we should open the menu, and when it is more than half of the menu width, we should close the menu.

  • So we need to handle the corresponding touch slide event in onTouchEvent()

@Override public boolean onTouchEvent(MotionEvent event) { if (mGestureDetector ! = null) { if (mGestureDetector.onTouchEvent(event)) return true; } if (onInterceptTouchEvent(event)) return true; If (motionEvent.action_UP == event.getAction()) {int mCurrentScrollX = getScrollX(); float mCurrentScrollY = getScrollY(); Math.abs(mCurrentScrollY) * 2/3) {if (mCurrentScrollX < mMenuWidth / 2) {// Not scrolled to half the width of mMenuView // open the menu openMenu(); } else {// closeMenu closeMenu(); } } return false; } return super.onTouchEvent(event); } private void closeMenu() { smoothScrollTo(mMenuWidth, 0) } private void openMenu() { smoothScrollTo(0, 0); }Copy the code
  • The third point is that there is no corresponding animation when you slide and you can see that through the effect

    1). The animation effect on the right is the zoom effect

    2). The animation on the left has gradients, zooming and displacement

    The progress and effect of these animations are determined by the distance of the slide, so we need to get the distance of the slide

@override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); Float scale = 1f * l/mMenuWidth; float scale = 1f * l/mMenuWidth; Float rightScaleValue = 0.85f + 0.15f * scale; By default, viewCompat.setPivotX (mContainerView, 0) is scaled at the center of the view; ViewCompat.setPivotY(mContainerView, mContainerView.getMeasuredHeight() / 2); ViewCompat.setScaleX(mContainerView, rightScaleValue); ViewCompat.setScaleY(mContainerView, rightScaleValue); // Set the opacity of the menu on the right from translucent to transparent 0. Float leftAlphaValue = (1-scale) * 0.5f + 0.5f; float leftAlphaValue = (1-scale) * 0.5f + 0.5f; ViewCompat.setAlpha(mMenuView, leftAlphaValue); Float leftScaleValue = (1-scale) * 0.15f + 0.85f; float leftScaleValue = (1-scale) * 0.15f + 0.85f; ViewCompat.setScaleX(mMenuView, leftScaleValue); ViewCompat.setScaleY(mMenuView, leftScaleValue); / / start exit is on the right rather than left. / / set translation / / ViewCompat setTranslationX (mMenuView, l); Effect of drawer ViewCompat. SetTranslationX (mMenuView, 0.2 f * l); }Copy the code

At this point we can see that the effect is basically formed, now let’s deal with the details of the optimization

  • If our touch event responds to a quick swipe, we don’t want to do onTouchEvent, otherwise we’ll get conflicting events, so we need to add this code to onTouchEvent
if (mGestureDetector ! = null) { if (mGestureDetector.onTouchEvent(event)) return true; // Do not execute the finger lift event in onTouch.Copy the code
mGestureDetector = new GestureDetector(mContext, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onFling(MotionEvent e1, MotionEvent e2, Float velocityX, float velocityY) {float velocityX, float velocityY) {float velocityX, float velocityY) {float velocityX, float velocityY) { If (math. abs(velocityX) < math. abs(velocityY) * 2/3) return false; If (isMenuOpen) {// If (velocityX < 0) {closeMenu(); return true; If (velocityX > 0) {openMenu(); return true; } } return false; }});Copy the code
  • If you pay attention to the view, you can find that when the menu is opened, we click the right theme part of the menu, it will not respond to the related events of the theme page, but will only close the menu. Only when the menu is closed, will trigger the related events of the right theme part. Therefore, we need to check whether the menu is closed by intercepting onInterceptTouchEvent, so we need to add an identifier to the open and close menu method aboveisMenuOpenIn this case, we directly closed the menu after the event interception, so there is no need to perform the operation related to the finger lifting in onTouchEvent.
if (onInterceptTouchEvent(event)) return true;
Copy the code
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    if (isMenuOpen && event.getX() > mMenuView.getWidth() && event.getAction() == MotionEvent.ACTION_UP) {
        closeMenu();
        return true;
    }
    return false;
}
Copy the code

So that’s it. Let’s take a look