Android custom ViewGroup creates various styles of SlidingMenu

Source: Internet
Author: User
Tags gety

Android custom ViewGroup creates various styles of SlidingMenu

Watching the video course of Hongyang's QQ slide menu, I have a new understanding of the implementation of the animation effect during the slide, and it seems to have broken through the second pulse of Ren du, currently, You can implement a slide menu with any effect. Thank you !!

Hongyang uses HorizontalScrollView to implement the slide menu function. The advantage of HorizontalScrollView is that it solves the slide function and solves the slide conflict problem, which makes it very convenient to use, however, slide and conflict processing are both difficulties in android, and are the knowledge points we should master. With these knowledge, we can create what we want without relying on system APIs, therefore, in this article, I will directly customize the ViewGroup to implement the slide menu function.

First, let's take a look. The first one is the most common slide menu. We will first make this slide menu, and then implement the other two effects on this basis.

First

Second

Third

Implement the first slide menu, inherited from ViewGroup

Inheriting from ViewGroup, we need to make our own measurements, la S, achieve sliding effects, and handle sliding conflicts. These are some of the things that newbie cannot start with. I hope you can help me after reading this article.

The general idea of customizing ViewGroup is to override the onMeasure method, call measureChild in the onMeasure method to measure the child View, and then call setMeasuredDimension to measure the size of the Child View. Then rewrite the onLayout method and call the layout method of the sub-View in onLayout to determine the position of the sub-View. Next we will do the two work first.

Initially, our Content should be displayed on the screen, while the Menu should be displayed on the screen. This should be the case when the Menu is opened.

MMenuRightPadding is a distance from the Menu to the right of the screen, because after the Menu is opened, the Content will still be left in part, rather than completely hidden

Public class MySlidingMenu extends ViewGroup {public MySlidingMenu (Context context) {this (context, null, 0);} public MySlidingMenu (Context context, AttributeSet attrs) {this (context, attrs, 0);} public MySlidingMenu (Context context, AttributeSet attrs, int defStyleAttr) {super (context, attrs, metrics); DisplayMetrics metrics = new DisplayMetrics (); WindowManager wm = (WindowManager) context. getSystemService (Context. WINDOW_SERVICE); wm. getdefadisplay display (). getMetrics (metrics); // obtain the screen width and height. mScreenWidth = metrics. widthPixels; mScreenHeight = metrics. heightPixels; // set the distance between Menu and the right of the screen. convertToDp converts 100 in the code to 100dp mMenuRightPadding = convertToDp (context, 100);} @ Override protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {// get the Menu, and the Menu is 0th children mMenu = (ViewGroup) getChildAt (0); // get the Content, and the Content is 1st children mContent = (ViewGroup) getChildAt (1); // set the Menu width to the screen width minus the mMenuWidth from the Menu to the right of the screen = mMenu. getLayoutParams (). width = mScreenWidth-mMenuRightPadding; // set the width of Content to the width of the screen. mContentWidth = mContent. getLayoutParams (). width = mScreenWidth; // measure Menu measureChild (mMenu, widthMeasureSpec, heightMeasureSpec); // measure Content measureChild (mContent, widthMeasureSpec, heightMeasureSpec); // measure yourself, your own width is the Menu width plus the Content width, and the height is the screen height setMeasuredDimension (mMenuWidth + mContentWidth, mScreenHeight);} @ Override protected void onLayout (boolean changed, int l, int t, int r, int B) {// place the Menu position. Based on the figure above, you can determine the upper and lower coordinates of mMenu. layout (-mMenuWidth, 0, 0, mScreenHeight); // the position where Content is placed. layout (0, 0, mScreenWidth, mScreenHeight);}/*** converts the number of incoming data to dp */private int convertToDp (Context context, int num) {return (int) TypedValue. applyDimension (TypedValue. COMPLEX_UNIT_DIP, num, context. getResources (). getDisplayMetrics ());}}

At present, the positions of the two sub-views in the slide menu should look like this.

Next, write the xml layout file.

The layout file of the Left menu of left_menu.xml, which is a ListView


  
     
      
   
  

The Item layout of ListView is left_menu_item.xml.

<Code class = "hljs xml"> <! -- {Cke_protected} {C} % 3C! % 2D % 2D % 3 Fxml % 20 version % 3D % 221.0% 20 encoding % 3D % 22utf-8% 22% 3F % 2D % 2D % 3E --> <linearlayout xmlns: android = "http://schemas.android.com/apk/res/android" android: orientation = "horizontal" android: layout_width = "match_parent" android: gravity = "center_vertical" android: layout_height = "match_parent"> <imageview android: id = "@ + id/menu_imageview" android: layout_width = "wrap_content" android: layout_height = "wrap_content" android: src = "@ drawable/menu_1" android: padding = "20dp"> <textview android: id = "@ + id/menu_textview" android: layout_width = "wrap_content" android: layout_height = "wrap_content" android: text = "menu 1" android: textcolor = "#000000" android: textsize = "20sp"> </textview> </imageview> </linearlayout> </code>

Let's write the layout file content in the content area. in xml, there is a header, and the header has an ImageView. This ImageView is the menu switch. When we click it, the menu is automatically switched, and the header is also a listview.

<code class=" hljs xml"><!--{cke_protected}{C}%3C!%2D%2D%3Fxml%20version%3D%221.0%22%20encoding%3D%22utf-8%22%3F%2D%2D%3E--><linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">    <linearlayout android:layout_width="match_parent" android:layout_height="65dp" android:background="#000000" android:gravity="center_vertical" android:orientation="horizontal">        <imageview android:id="@+id/menu_toggle" android:layout_width="40dp" android:layout_height="40dp" android:src="@drawable/toggle" android:paddingleft="10dp">    </imageview></linearlayout>        <listview android:id="@+id/content_listview" android:layout_width="match_parent" android:layout_height="wrap_content" android:dividerheight="0dp" android:divider="@null" android:scrollbars="none"></listview></linearlayout></code>

The layout file of the content item is content_item.xml.

<code class=" hljs xml"><!--{cke_protected}{C}%3C!%2D%2D%3Fxml%20version%3D%221.0%22%20encoding%3D%22utf-8%22%3F%2D%2D%3E--><linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:gravity="center_vertical" android:background="#ffffff" android:layout_height="match_parent">    <imageview android:id="@+id/content_imageview" android:layout_width="80dp" android:layout_height="80dp" android:src="@drawable/content_1" android:layout_margin="20dp">    <textview android:id="@+id/content_textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Content - 1" android:textcolor="#000000" android:textsize="20sp"></textview></imageview></linearlayout></code>

In activity_main.xml, we add menu and content to our slidingMenu.

<code class="language-xml hljs "><relativelayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#aaaaaa"><com.example.user.slidingmenu.myslidingmenu android:id="@+id/slidingmenu" android:layout_width="wrap_content" android:layout_height="match_parent">        <include android:id="@+id/menu" layout="@layout/left_menu">        <include android:id="@+id/content" layout="@layout/content"></include></include></com.example.user.slidingmenu.myslidingmenu></relativelayout></code>

This is the effect now.

The left menu is hidden outside the left side of the screen, but it cannot be moved yet. To implement the slide function, we can use the scrollTo and scrollBy methods of View, the difference between the two methods is that scrollTo directly moves the view to the specified position, and scrollBy moves an offset relative to the current position. Therefore, we should override the onTouchEvent method, it is used to calculate an offset of the current finger and then use the scrollBy method to move 1.1 points to form an animation of the view that can follow the finger movement.

Before writing code, let's clear the obstacles first. Let's first figure out what these coordinates are.



Now, after figuring out the coordinates, we will be much simpler. Let's look at the onTouchEvent method. <喎?http: www.bkjia.com kf ware vc " target="_blank" class="keylink"> VcD4NCjxwcmUgY2xhc3M9 "brush: java;"> @ Override public boolean onTouchEvent (MotionEvent event) {int action = event. getAction (); switch (action) {case MotionEvent. ACTION_DOWN: mLastX = (int) event. getX (); mLastY = (int) event. getY (); break; case MotionEvent. ACTION_MOVE: int currentX = (int) event. getX (); int currentY = (int) event. getY (); // get the offset int dx = currentX-mLastX in the x direction; if (dx <0) {// slide to the left // control the boundary, if the Menu is fully displayed, then the white edge will appear on the left of the Menu and the border control if (getScrollX () + Math. abs (dx)> = 0) {// directly move to the (0, 0) position without the white side scrollTo (0, 0 );} else {// The Menu is not fully displayed. // In fact, dx is still-dx here. You don't have to remember it. // you can use dx first and then run it, if you find that the direction of the // movement is the opposite, you can add a negative number here to perform scrollBy (-dx, 0) ;}} else {// sliding to the right // border control, if the Content is fully displayed, then the white edge will appear on the right of the Content, and the boundary control if (getScrollX ()-dx <=-mMenuWidth) will be implemented) {// directly move to the position (-mMenuWidth, 0) without the white side scrollTo (-mMenuWidth, 0 );} else {// The Content is not fully displayed. // move scrollBy (-dx, 0) ;}} mLastX = currentX; mLastY = currentY; break;} return true ;}

Currently, our SlidingMenu still cannot slide horizontally, but listview can slide vertically because our SlidingMenu does not intercept events by default, the event will be passed to the sub-View for execution, that is, the ListView passed to the Content will be executed, so the listview can be slide. For simplicity, we should first override the onInterceptTouchEvent method, we will return true to allow SlidingMenu to intercept the event, and our SlidingMenu will be able to slide, but ListView will not be able to slide. We will handle sliding conflicts later. Now we will first implement the SlidingMenu function.

@Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        return true;    }

Now, we can freely slide our SlidingMenu and perform well-defined boundary control. Now we can add another function, that is, when the Menu is opened greater than 1/2, release the finger, menu is automatically opened. When the Menu is opened less than 1/2, the Menu is automatically closed after you release your finger. The automatic slide function is implemented by scroroller.

We initialize a scroroller In the constructor.

public MySlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        ...        mScroller = new Scroller(context);        ...    }

Then rewrite the computeScroll method. This method is a required method to ensure that scroroller automatically slides. This is a template method, and it is better to do so wherever it goes.

 @Override    public void computeScroll() {        if (mScroller.computeScrollOffset()){            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());            invalidate();        }    }

Then we can judge in onTouchEvent ACTION_UP to determine the number of currently opened menus.

Case MotionEvent. ACTION_UP: if (getScrollX () <-mMenuWidth/2) {// Open Menu // call the startScroll method. The first parameter is the start X coordinate, the second parameter // is the starting Y coordinate, the third parameter is the X direction offset, and the fourth parameter is the Y direction offset. startScroll (getScrollX (), 0,-mMenuWidth-getScrollX (), 0,300); // you can specify an enabled ID, isOpen = true will be used when the Enable and disable function is automatically enabled; // do not forget to call this method for re-painting, otherwise there is no animation effect invalidate ();} else {// Disable Menu // same as mScroller. startScroll (getScrollX (), 0,-getScrollX (), 0,300); isOpen = false; invalidate ();} break;

How can we calculate dx and dy Based on startX and startY in startScroll? In fact, it is also very simple. For example, if we want to move the startX coordinates to 30 and we want to move to-100, then startX + dx =-100-> dx =-100-startX-> dx =-130

Now we can achieve the animation effect of automatically sliding after the fingers are released.
Now we also need to click a triangle in the upper left corner of the content. If the current menu is not opened, it will be automatically opened. If it is already enabled, the function will be automatically disabled, we need to use Scroller to automatically slide the effect. startScroll Method

/*** Click the switch to open and close the Menu. if the current menu is enabled, it is disabled. if the current menu is disabled, the */public void toggleMenu () {if (isOpen) is enabled) {closeMenu () ;}else {openMenu () ;}/ *** Disable menu */private void closeMenu () {// The startScroll method is also used, dx and dy are computed in the same way as mScroller. startScroll (getScrollX (), 0,-getScrollX (), 0,500); invalidate (); isOpen = false;}/*** Open menu */private void openMenu () {mScroller. startScroll (getScrollX (), 0,-mMenuWidth-getScrollX (), 0,500); invalidate (); isOpen = true ;}

Then we can get the imageview of the triangle in the top left corner of the content in MainActivity, set a click event for it, and call our toggleMenu method.

mMenuToggle.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                mSlidingMenu.toggleMenu();            }        });
Handle slide conflicts

Because our menu and content are listview, listview supports vertical sliding, while our slidingMenu supports horizontal sliding, conflicts may occur. Just now, we directly returned true in onInterceptTouchEvent. Therefore, SlidingMenu intercepts all events, and ListView cannot receive any events, so ListView cannot slide, to solve this slide conflict, we only need to determine whether the current sliding is horizontal or vertical. If it is horizontal sliding, let SlidingMenu intercept the event. If it is vertical sliding, it will not intercept the event, send the event to the ListView of the sub-View for execution.

@ Override public boolean onInterceptTouchEvent (MotionEvent ev) {boolean intercept = false; int x = (int) ev. getX (); int y = (int) ev. getY (); switch (ev. getAction () {case MotionEvent. ACTION_DOWN: intercept = false; break; case MotionEvent. ACTION_MOVE: int deltaX = (int) ev. getX ()-mLastXIntercept; int deltaY = (int) ev. getY ()-mLastYIntercept; if (Math. abs (deltaX)> Math. abs (deltaY) {// transverse sliding intercept = true;} else {// vertical sliding intercept = false;} break; case MotionEvent. ACTION_UP: intercept = false; break;} mLastX = x; mLastY = y; mLastXIntercept = x; mLastYIntercept = y; return intercept ;}

Now we have solved the sliding conflict. We can slide both the SlidingMenu horizontally and the ListView vertically, so the first SlidingMenu has been implemented, let's take a look at how to implement the other two methods.

Implement SlidingMenu In the second QQ V6.2.3 Style


This SlidingMenu is consistent with QQ v6.2.3's slide Menu style. We found that the sliding speed between Menu and Content is different. In fact, we can modify the Menu offset to achieve this effect.

At this time, the Menu offset is 2/3 of mMenuWidth. When we slowly open the Menu, we modify the Menu offset to 0.

In this way, a speed difference is achieved. You only need to add the following code to the ACTION_MOVE and computeScroll of onTouchEvent.

mMenu.setTranslationX(2*(mMenuWidth+getScrollX())/3);

Let's analyze. At the beginning, mMenuWidth + getScrollX = mMenuWidth, multiplied by 2/3, and the result is 2/3 of mMenuWidth. When we slide to the Menu, mMenuWidth + getScrollX = 0, this achieves our results.

Why add this line of code in computeScroll, because when we slide, if our fingers leave the screen, ACTION_MOVE will definitely not be executed, but when our fingers leave the screen, there will be an animation that is automatically opened or closed, so this animation should continue to set the Menu offset, so we need to add this line of code in computeScroll.

Well, we have implemented the effect. You only need to set the Menu offset. Is it very simple?

Implement SlidingMenu in the third QQ V5.0 Style


In this effect, the Menu has an offset effect, the transparency changes, and the magnified effect. Content has a narrow effect.
First, we need a variable to record the percentage of the current menu opened.

Here, we should note that the value obtained by getScrollX is a negative value, so we need to take the value of getScrollX to the absolute value during calculation, and then calculate this value in onTouchEvent MOVE, at the same time, we also need to calculate this value in the computeScroll method, because when our fingers are raised, we may execute an animation that is automatically opened or closed, so the computation in the MOVE will definitely stop, but in the animation execution process, scroroller is at work, computeScroll will be executed until the animation ends, so we need to perform computeScroll calculation in the same way.

scale = Math.abs((float)getScrollX()) / (float) mMenuWidth;

The scale value is [0, 1], so we can set the menu offset based on this value.
We can set the setScaleX and setScaleY of the View to zoom in and out the View. Of course, the zoom ratio should be changed based on our scale value. First, our Menu has a zoom in effect, we will designate the Menu to zoom in from 0.7 to 1.0, so we can write it like this

mMenu.setScaleX(0.7f + 0.3f*scale);        mMenu.setScaleY(0.7f + 0.3f*scale);

Transparency ranges from 0 to 1, so we can use the scale value directly.

        mMenu.setAlpha(scale);

I also set an offset for Menu, which can be calculated by myself.

mMenu.setTranslationX(mMenuWidth + getScrollX() - (mMenuWidth/2)*(1.0f-scale));

After setting the Menu, let's set the Content. The Content size is reduced from 1.0 to 0.7, So we write

mContent.setScaleX(1 - 0.3f*scale);        mContent.setPivotX(0);        mContent.setScaleY(1.0f - 0.3f * scale);

MContent. set0000tx (0) indicates that the X axis coordinate of the Content zooming center is 0.

We can extract this change process as a method.

private void slidingMode3(){        mMenu.setTranslationX(mMenuWidth + getScrollX() - (mMenuWidth/2)*(1.0f-scale));        mMenu.setScaleX(0.7f + 0.3f*scale);        mMenu.setScaleY(0.7f + 0.3f*scale);        mMenu.setAlpha(scale);        mContent.setScaleX(1 - 0.3f*scale);        mContent.setPivotX(0);        mContent.setScaleY(1.0f - 0.3f * scale);    }

Add this method to ACTION_MOVE and computeScroll of onTouchEvent.

We can see that all sliding styles are determined by modifying the value of Menu or Content's translationX or scaleX scaleY based on the first one. Therefore, we can create various slidingmenus.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.