Android custom controls (Scroller)

Source: Internet
Author: User

Android custom controls (Scroller)

First look

The implementation method inherited from ViewGroup requires us to measure and layout ourselves to achieve sliding effect and handle sliding conflicts,
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. <喎?http: www.bkjia.com kf ware vc " target="_blank" class="keylink"> VcD4NCjxwPjxpbWcgYWx0PQ = "here write picture description" src = "http://www.bkjia.com/uploads/allimg/160414/0426255018-2.png" title = "\"/>

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.


  
      
       
    
   
  

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 an istview is also under the header.

<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=" hljs avrasm"><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.

@ 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;    }

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 ;}

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,

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.