# # # 1. The overview
Recently, I have been leading interns to do a project, and I found that I haven’t written a blog for a long time, and the update will be quite frequent these days. Today, when I was playing QQ, I found that the sliding effect of THE QQ homepage menu had changed, so I couldn’t help but realize it at night!
# # # 2. implementation
2.1. 2.1.1 Customizing viewGroups 2.1.2FrameLayout + GestureDetector 2.2.3 Use Google’s own DrawerLayout to modify it 2.2.4 Inherit from the HorizontalScrollView *** You can look at the Android custom ViewGroup to create a variety of styles of SlidingMenu, This way inherited from the ViewGroup, I think it is ok but still more cumbersome to deal with things are also more, so I will use the last 2.2.4* way to achieve, someone said to directly go to the Internet to download a source code can be, this I can only GG.
2.3. Customize SlidingMenu extends HorizontalScrollView and then write the layout file. This is used the same way as ScrollView, except that it scrolls horizontally
/** * description: * Created by Xinfeng on 2016/11/1. * QQ: 240336124 * Email: 240336124 @qq.com * Version: Public class SlidingMenu extends HorizontalScrollView {public SlidingMenu(Context Context) {super(Context); } public SlidingMenu(Context context, AttributeSet attrs) { super(context, attrs); } public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); }}Copy the code
2.4. After running it, we find the layout is completely messed up and it is obviously match_parent, but it doesn’t work, so we need to specify the width of menu and content with code: menu width = screen width – custom right width of content = screen width
/** * description: * Created by Xinfeng on 2016/11/1. * QQ: 240336124 * Email: [email protected] * Version: 1.0 */ public Class SlidingMenu extends HorizontalScrollView {private View mMenuView; private View mContentView; private int mMenuWidth; public SlidingMenu(Context context) { this(context, null); } public SlidingMenu(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); / / get the width of the right of the custom to set aside TypedArray array = context. ObtainStyledAttributes (attrs, R.s tyleable. SlidingMenu);floatrightPadding = array.getDimension( R.styleable.SlidingMenu_rightPadding,dip2px(50)); MMenuWidth = (int) (getScreenWidth() -rightPadding); mMenuWidth = (int) (getScreenWidth() -rightPadding); array.recycle(); } /** * convert dip to pixels */ privatefloat dip2px(int dip) {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,dip,getResources().getDisplayMetrics());
}
@Override
protected void onFinishInflate() { super.onFinishInflate(); Container = (ViewGroup) this.getChildAt(0); int containerChildCount = container.getChildCount();ifThrow new IllegalStateException(containerChildCount>2){// Only two layouts are allowed inside: Menu and Content"The SlidingMenu root layout allows only two layouts below the LinearLayout, the menu layout and the home content layout."); } // 2. Get menu and content layout mMenuView = container.getChildat (0); mContentView = container.getChildAt(1); / / 3. Specify the content and the width of the menu layout / / width = 3.1 menu screen width - the width of the right of the custom to set aside mMenuView. GetLayoutParams (). The width = mMenuWidth; / / width = 3.2 contents of the screen width mContentView. GetLayoutParams (). The width = getScreenWidth (); } /** * get the screen width */ public intgetScreenWidth() {
Resources resources = this.getResources();
DisplayMetrics dm = resources.getDisplayMetrics();
returndm.widthPixels; }}Copy the code
The current effect is that you can swipe, and the layout width of the menu and main page content is normal
2.5 Next, let the menu slide to the closed state at the beginning, and lift the finger to judge whether the menu is open or closed and do the corresponding processing onLayout() onTouch() smoothScrollTo(), Toggle menu states when fingers are fast using the GestureDetector gesture processing class:
/** * description: * Created by Xinfeng on 2016/11/1. * QQ: 240336124 * Email: [email protected] * Version: 1.0 */ public Class SlidingMenu extends HorizontalScrollView {private View mMenuView; private View mContentView; private int mMenuWidth; Private GestureDetector mGestureDetector; Private Boolean mMenuIsOpen =false; public SlidingMenu(Context context) { this(context, null); } public SlidingMenu(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); / / get the width of the right of the custom to set aside TypedArray array = context. ObtainStyledAttributes (attrs, R.s tyleable. SlidingMenu);floatrightPadding = array.getDimension( R.styleable.SlidingMenu_rightPadding, dip2px(50)); MMenuWidth = (int) (getScreenWidth() -rightPadding); mMenuWidth = (int) (getScreenWidth() -rightPadding); array.recycle(); MGestureDetector = new GestureDetector(context,new GestureListener()); } /** * convert dip to pixels */ privatefloat dip2px(int dip) {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, dip, getResources().getDisplayMetrics());
}
@Override
protected void onFinishInflate() { super.onFinishInflate(); Container = (ViewGroup) this.getChildAt(0); int containerChildCount = container.getChildCount();ifThrow new IllegalStateException(containerChildCount > 2) {// Only two layouts are allowed inside: Menu and Content"The SlidingMenu root layout allows only two layouts below the LinearLayout, the menu layout and the home content layout."); } // 2. Get menu and content layout mMenuView = container.getChildat (0); mContentView = container.getChildAt(1); / / 3. Specify the content and the width of the menu layout / / width = 3.1 menu screen width - the width of the right of the custom to set aside mMenuView. GetLayoutParams (). The width = mMenuWidth; / / width = 3.2 contents of the screen width mContentView. GetLayoutParams (). The width = getScreenWidth (); } @override public Boolean onTouchEvent(MotionEvent ev) {// Handle finger swipesif(mGestureDetector.onTouchEvent(ev)){
return mGestureDetector.onTouchEvent(ev);
}
switch (ev.getAction()) {
caseACTION_UP: int currentScrollX = getScrollX();if(currentScrollX > mMenuWidth / 2) {closeMenu(); }else{// Open the menu openMenu(); }return false;
}
returnsuper.onTouchEvent(ev); } /** * open menu */ private voidopenMenu() {
smoothScrollTo(0, 0);
mMenuIsOpen = true; } /** * close the menu */ private voidcloseMenu() {
smoothScrollTo(mMenuWidth, 0);
mMenuIsOpen = false; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); // After the layout is specified, the sublayout will be rearranged, and when it is finished, the menu will scroll to the invisible stateif(changed) { scrollTo(mMenuWidth, 0); }} /** * get the screen width */ public intgetScreenWidth() {
Resources resources = this.getResources();
DisplayMetrics dm = resources.getDisplayMetrics();
return dm.widthPixels;
}
private class GestureListener extends GestureDetector.SimpleOnGestureListener{
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, floatVelocityY) {// The method to call back when the finger is quickly swiped log.e ("TAG",velocityX+""); // If the menu is open and a quick swipe to the left toggles the menu stateif(mMenuIsOpen){
if(velocityX<-500){
toggleMenu();
return true; }}else{// If the menu is closed and a quick swipe to the right toggles the menu stateif(velocityX>500){
toggleMenu();
return true; }}return false; }} /** * Toggle menu state */ private voidtoggleMenu() {
if(mMenuIsOpen){
closeMenu();
}else{ openMenu(); }}}Copy the code
At this point we can switch menus and handle a quick finger swipe. We can’t wait to see what happens
2.6. Implement the animation effect of the drawer style on the left side of the menu. Monitor the scrolling callback method onScrollChanged()
@Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); // l is the x distance of the current scroll. This method will be called repeatedly while scrolling""); MMenuView. SetTranslationX (l * 0.8 f); }Copy the code
2.7. To achieve the shadow transparency effect of the menu on the right side, this is going to add a layer of shadow on the content layout of the main page, using ImageView, then our content View needs to be changed
/** * description: * Created by Xinfeng on 2016/11/1. * QQ: 240336124 * Email: 240336124 @qq.com * Version: 1.0 */ public Class SlidingMenu extends HorizontalScrollView {private static Final String TAG ="HorizontalScrollView"; private Context mContext; Private View mMenuView; private View mMenuView; Private int mMenuWidth; private int mMenuWidth; Private GestureDetector mGestureDetector; Private Boolean mMenuIsOpen =false; // 7(4). ImageView private ViewGroup mContentView; ImageView private ImageView mShadowIv; public SlidingMenu(Context context) { this(context, null); } public SlidingMenu(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); / / 4.1 calculating the width of the menu on the left / / 4.4.1 for the width of the right of the custom to set aside TypedArray array = context. ObtainStyledAttributes (attrs, R.s tyleable. SlidingMenu);floatrightPadding = array.getDimension( R.styleable.SlidingMenu_rightPadding, dip2px(50)); MMenuWidth = (int) (getScreenWidth() - rightPadding); array.recycle(); MGestureDetector = new GestureDetector(context,new GestureListener()); this.mContext = context; } /** * convert dip to pixels */ privatefloat dip2px(int dip) {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, dip, getResources().getDisplayMetrics());
}
@Override
protected void onFinishInflate() { super.onFinishInflate(); // specify menu and content View width // 4.2.1. Container = (ViewGroup) this.getChildat (0); int containerChildCount = container.getChildCount();ifThrow new IllegalStateException(containerChildCount > 2) {// Only two layouts are allowed inside: Menu and Content"The SlidingMenu root layout allows only two layouts below the LinearLayout, the menu layout and the home content layout."); } // 4.2.2. Get menu and content layout mMenuView = container.getChildat (0); MContentView = new FrameLayout(mContext); // Add a shadow effect to the content. ViewGroup.LayoutParams contentParams = new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT); OldContentView = container.getChildat (1); oldContentView = container.getChildat (1); container.removeView(oldContentView); McOntentview.addview (oldContentView); McOntentview.addview (oldContentView); MShadowIv = new ImageView(mContext); mShadowIv.setBackgroundColor(Color.parseColor("# 99000000")); mContentView.addView(mShadowIv); // add the new View with shadows to the LinearLayout container.addView(mContentView); / / 4.2.3 specified content and the width of the menu layout width / / 4.2.3.1 = screen width - the width of the right of the custom to set aside mMenuView. GetLayoutParams (). The width = mMenuWidth; / / 4.2.3.2 contents width = screen width mContentView. GetLayoutParams (). The width = getScreenWidth (); */ @override public Boolean onTouchEvent(MotionEvent ev)if(mGestureDetector.onTouchEvent(ev)){
return mGestureDetector.onTouchEvent(ev);
}
switch (ev.getAction()) {
caseInt currentScrollX = getScrollX();if(currentScrollX > mMenuWidth / 2) {// }else{// 5.1.2 Open the openMenu(); }return false;
}
returnsuper.onTouchEvent(ev); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); // l is the x distance of the current scroll. This method will be called repeatedly while scrolling""); / / 6. The menu on the left side of the drawer style of animation effects mMenuView. SetTranslationX (l * 0.8 f); // 7. Add a shadow effect to the content - calculate the gradientfloatgradientValue = l * 1f / mMenuWidth; Add a shadow effect to the content - give the shaded View a transparency value of 0-1floatshadowAlpha = 1 - gradientValue; mShadowIv.setAlpha(shadowAlpha); } /** * 5.1.2 Open menu */ private voidopenMenu() {
smoothScrollTo(0, 0);
mMenuIsOpen = true; } /** * 5.1.1 Close the menu */ private voidcloseMenu() {
smoothScrollTo(mMenuWidth, 0);
mMenuIsOpen = false; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); // After the layout is specified, the sublayout will be rearranged, and when it is finished, the menu will scroll to the invisible stateif(changed) { scrollTo(mMenuWidth, 0); }} /** * get the screen width */ public intgetScreenWidth() {
Resources resources = this.getResources();
DisplayMetrics dm = resources.getDisplayMetrics();
returndm.widthPixels; } / 5.3 processing fingers slip quickly * * * * / private class GestureListener extends GestureDetector. SimpleOnGestureListener {@ Override public boolean onFling(MotionEvent e1, MotionEvent e2,float velocityX, floatVelocityY) {log. e(TAG,velocityX+)""); // 5.3.1 If the menu is open and the state of the menu is switched by a quick swipe to the leftif(mMenuIsOpen){
if(velocityX<0){
toggleMenu();
return true; }}else{// 5.3.2 If the menu is closed and a quick swipe to the right toggles the menu stateif(velocityX>0){
toggleMenu();
return true; }}return false; }} /** * Toggle menu state */ private voidtoggleMenu() {
if(mMenuIsOpen){
closeMenu();
}else{ openMenu(); }}}Copy the code
Let’s look at the end result. It’s not a lot of code.
Attached to the video address: http://pan.baidu.com/s/1dEA0NLZ