November 8, 2014 Android learning ------ implementation case of Android drawer effect ------ Animation learning chapter

I study Android in combination with the source code, which is more intuitive and very clear to see the effect. I think it's very good. Today's learning source code is the source code found online. Baidu search knows many places to download. The name of the online source code is Android drawer effect zip my blog is messy. If I don't understand this article,

Please read the previous article first. The addresses are:

Gesture recognition: http://blog.csdn.net/u014737138/article/details/40950431

Animation monitoring: http://blog.csdn.net/u014737138/article/details/40952373

onLayout function that needs to be overloaded to inherit LinearLayout class: http://blog.csdn.net/u014737138/article/details/40951087

The onFinishInflate function that needs to be overloaded to inherit the LinearLayout class: http://blog.csdn.net/u014737138/article/details/40951985

First of all, we need to talk about the whole process, that is, the principle of implementation. Before we talk about PPT, we should first tell the audience what to do next, what points to make, and what steps to follow?

First: we need to make it clear that to achieve the above drawer effect, we must first define a layout designed by ourselves,

The method we implement is to inherit the LinearLayout class

public class Panel extends LinearLayout {

Three things must be done to inherit this class:

1. The constructor needs to be rewritten:

Generally, when the function draw () and the function dispatch2 () are overloaded, they are used to draw images

3. Overload onLayout() function:

4. Overload onFinsihInflate() function:

1. Implementation of constructor:

	public Panel(Context context, AttributeSet attrs) {
		super(context, attrs);
		TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Panel);//Load properties from the layout file into this array
		mDuration = a.getInteger(R.styleable.Panel_animationDuration, 750); //Animation time
		mPosition = a.getInteger(R.styleable.Panel_position, BOTTOM); //Drawer position
		mLinearFlying = a.getBoolean(R.styleable.Panel_linearFlying, false);
		mOpenedHandle = a.getDrawable(R.styleable.Panel_openedHandle);//Open background
		mClosedHandle = a.getDrawable(R.styleable.Panel_closedHandle);//Close background
		a.recycle();//Remember to recycle when the array is used up
		mOrientation = (mPosition == TOP || mPosition == BOTTOM) ? VERTICAL
				: HORIZONTAL;//Direction, according to the position obtained from the layout file, it should be the vertical direction
		setOrientation(mOrientation);//Then set the layout file to this direction
		mState = State.READY;//The current drawer status is ready
		mGestureListener = new PanelOnGestureListener();//Gesture monitoring object
		mGestureDetector = new GestureDetector(mGestureListener);//Initialization of gesture recognition object
		mGestureDetector.setIsLongpressEnabled(false);//Gesture recognition does not accept long press message status processing
	}

Analyze and write some codes in this:

TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Panel);

//Load properties from the layout file into this array

About r.styleable The knowledge point of panel is mentioned in the previous topic. It will not be introduced here. You can read my article

The address is: http://blog.csdn.net/u014737138/article/details/40789899

Here we need to pay attention to what is in this XML file:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="Panel">

        <!-- Defines panel animation duration in ms. -->
        <attr name="animationDuration" format="integer" />
        <!-- Defines panel position on the screen. -->
        <attr name="position">

            <!-- Panel placed at top of the screen. -->
            <enum name="top" value="0" />
            <!-- Panel placed at bottom of the screen. -->
            <enum name="bottom" value="1" />
            <!-- Panel placed at left of the screen. -->
            <enum name="left" value="2" />
            <!-- Panel placed at right of the screen. -->
            <enum name="right" value="3" />
        </attr>
        <!-- Defines if flying gesture forces linear interpolator in animation. -->
        <attr name="linearFlying" format="boolean" />
        <!-- Defines opened handle (drawable/color). -->
        <attr name="openedHandle" format="reference|color" />
        <!-- Defines closed handle (drawable/color). -->
        <attr name="closedHandle" format="reference|color" />
    </declare-styleable>
    <declare-styleable name="SmoothButton">
        <attr name="transitionDrawable" format="reference" />
        <attr name="transitionDrawableLength" format="integer" />
        <attr name="transitionTextColorUp" format="color" />
        <attr name="transitionTextColorDown" format="color" />
    </declare-styleable>

</resources>

At attrs Two < declare styleable > tags are defined in the XML file:

1.<declare-styleable name="Panel">

2.<declare-styleable name="SmoothButton">

Both are automatically generated in the class of R.styleable, It should be noted that there is a knowledge point: when the TypedArray array is used up, remember to recycle it at any time

How many things does this constructor handle?

1. Load properties

2. Determine the direction of layout

3. Determine the status of the drawer

4. Processing of gesture recognition transactions

We'll talk about these four things later. Let's finish what must be done first, that is, the functions that must be overloaded when we inherit the LinearLayout class:

2. Overload draw() function:

	@Override
	protected void dispatchDraw(Canvas canvas) {
		// String name = getResources().getResourceEntryName(getId());
		// Log.d(TAG, name + " ispatchDraw " + mState);
		// this is why 'mState' was added:
		// avoid flicker before animation start
		if (mState == State.ABOUT_TO_ANIMATE && !mIsShrinking) {
			int delta = mOrientation == VERTICAL ? mContentHeight
					: mContentWidth;
			if (mPosition == LEFT || mPosition == TOP) {
				delta = -delta;
			}
			if (mOrientation == VERTICAL) {
				canvas.translate(0, delta);
			} else {
				canvas.translate(delta, 0);
			}
		}
		if (mState == State.TRACKING || mState == State.FLYING) {
			canvas.translate(mTrackX, mTrackY);
		}
		super.dispatchDraw(canvas);
	}

The first thing this code judges is the state: draw the drawer only when it is ready. When is it ready? It must be that the current view containing drawers is opened by the user, that is, it is visible. That is, you can open it only when you see the drawer. If you don't see it, do you still talk about opening it?

The variable mIsShrinking: represents whether the current component is hidden. The existence of this variable is mainly to prevent us from not knowing the size of the component and whether the component is visible on the interface shown to us. The second reason is whether the drawer is visible

canvas.translate(0, delta);

//The translate(float x,float y) function moves the entire canvas x horizontally and Y vertically. The function of scrolling can be realized through the translate function.

Knowing the function of this function, let's look at the variable of delta type int:

			int delta = mOrientation == VERTICAL ? mContentHeight
					: mContentWidth;
			if (mPosition == LEFT || mPosition == TOP) {
				delta = -delta;
			}

If the drawer direction is vertical: delta is the height of the whole view

If the drawer direction is horizontal: delta is the width of the whole view

The next step is how to display the drawer on the canvas, also according to the direction:

			if (mOrientation == VERTICAL) {
				canvas.translate(0, delta);
			} else {
				canvas.translate(delta, 0);
			}

If the drawer direction is vertical: the canvas moves horizontally by 0 pixels, that is, it does not move horizontally; The canvas moves the height of the view vertically, that is, the vertical direction is the same as the interface

If the drawer direction is horizontal: move the canvas horizontally by pixels, that is, the horizontal direction is the same width as the interface; Move 0 pixels in the vertical direction, that is, do not move in the vertical direction

At this point, the position of the drawer is very clear. The above is the layout position of the drawer in the initialization state. If I click the button with my finger, I will pull the "buckle"

The layout needs to be redrawn, and this function will be reloaded. Then we must update the view at any time according to the status of the drawer:

		if (mState == State.TRACKING || mState == State.FLYING) {
			canvas.translate(mTrackX, mTrackY);
		}

When the drawer is open or closed, the process is like the process of scaling the canvas outside or inside,

Using code to handle this is the process of moving the canvas:

As for the coordinate at this time, it is the monitoring event in gesture recognition to process this coordinate

2. Overload onLayout() function:

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		super.onLayout(changed, l, t, r, b);
		mContentWidth = mContent.getWidth();
		mContentHeight = mContent.getHeight();
	}

Set the custom layout, that is, the width and height of the drawer. You can see from the function that the width and height are determined by the parameter mConten and assigned to mContentWidth

So we need to care about the meaning of these variables. Let's put it here first,

3. Overload onFinsihInflate() function:

	@Override
	protected void onFinishInflate() {
		super.onFinishInflate();
		mHandle = findViewById(R.id.panelHandle);
		if (mHandle == null) {
			throw new RuntimeException(
					"Your Panel must have a View whose id attribute is 'R.id.panelHandle'");
		}
		mHandle.setOnTouchListener(touchListener);

		mContent = findViewById(R.id.panelContent);
		if (mContent == null) {
			throw new RuntimeException(
					"Your Panel must have a View whose id attribute is 'R.id.panelContent'");
		}

		// reposition children
		removeView(mHandle);
		removeView(mContent);
		if (mPosition == TOP || mPosition == LEFT) {
			addView(mContent);
			addView(mHandle);
		} else {
			addView(mHandle);
			addView(mContent);
		}

		if (mClosedHandle != null) {
			mHandle.setBackgroundDrawable(mClosedHandle);
		}
		mContent.setVisibility(GONE);
	}

This function is used to obtain the reference of the control on the drawer view,

Here are two IDS, which correspond to two components respectively. We need to see how this id is defined:

The first is in IDS Defined in XML:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <item name="panelHandle" type="id"/>
    <item name="panelContent" type="id"/>

</resources>

2. Then main. In the main layout file These two IDs are required in XML:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:panel="http://schemas.android.com/apk/res/com.wust.slidingdrawfromleft"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#767c9b"
    android:orientation="vertical" >

    <com.wust.slidingdrawfromleft.activity.Panel
        android:id="@+id/leftPanel1"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_gravity="left"
        android:layout_weight="1"
        panel:closedHandle="@drawable/left_switcher_collapsed_background"
        panel:openedHandle="@drawable/left_switcher_expanded_background"
        panel:position="left" >

        <Button
            android:id="@id/panelHandle"
            android:layout_width="33dip"
            android:layout_height="fill_parent" />

        <LinearLayout
            android:id="@id/panelContent"
            android:layout_width="wrap_content"
            android:layout_height="fill_parent"
            android:orientation="vertical" >

            <Button
                android:id="@+id/btn"
                android:layout_width="wrap_content"
                android:layout_height="fill_parent"
                android:background="#999932"
                android:gravity="center"
                android:padding="18dip"
                android:text="This"
                android:textColor="#eee"
                android:textSize="16dip"
                android:textStyle="bold" />
        </LinearLayout>
    </com.wust.slidingdrawfromleft.activity.Panel>

</LinearLayout>

You can see what two controls are used:

1. < button Android: id = "@ ID / panelhandle" means to click the button of the drawer, that is, the buckle like the real drawer

2. < LinearLayout Android: id = "@ ID / panelcontent" stands for the box of the drawer, that is, it is a rectangle with a large area like the real drawer, which can hold a lot of things

So let's take a look at the two controls shown on the rendering?

In other words, we know the role of two variables:

1.mHandle: represents the "buckle" of the drawer, that is, a button. When the user clicks, it can open the "drawer" and click again to close the "drawer"

2.mContent: represents the rectangle in the drawer, that is, a layout. Programmers can load their own layout style in it, and put many buttons, textviews, etc

Next, let's see what it does:

When the "buckle" button is found, monitor its monitoring events immediately:

mHandle.setOnTouchListener(touchListener);

Then there is the following rearrangement:

removeView(mHandle); removeView(mContent);

Delete this component first and reload it in the set direction:

If (mposition = = TOP | mposition = = LEFT) {/ / the direction is TOP or LEFT addView(mContent); addView(mHandle); }else {/ / the direction is BOTTOM or RIGHT addView(mHandle); addView(mContent); }

If the user doesn't click on the things in the front drawer, you can't show it to the user:

mContent.setVisibility(GONE);

At the same time, the background image of this drawer must be set

if (mClosedHandle != null) { mHandle.setBackgroundDrawable(mClosedHandle); }

After completing the above construction and necessary overloading, we will complete the implementation of the whole layout, that is, inherit LinearLayout. At least for now, we can show this effect,

What we need to deal with now is the listening event processing of the control, the calculation of the mouse, and so on

At this time, we first return to the Draw() function: there is a code that is the listening event of the button "buckle":

mHandle.setOnTouchListener(touchListener);// As mentioned above, this mhandle object represents the button of the buckle. Users can click it to close and open the drawer

The parameter is: touchListener. I won't repeat the monitoring events of the button here. Beginners can see my previous articles

Address: http://blog.csdn.net/u014737138/article/details/40478027

Here is the practice in this case: it mainly monitors whether the drawer is opened or closed

	OnTouchListener touchListener = new OnTouchListener() {
		int initX;
		int initY;
		boolean setInitialPosition;

		public boolean onTouch(View v, MotionEvent event) {
			// Log.d(TAG, "state: " + mState + " x: " + event.getX() + " y: " +
			// event.getY());
			int action = event.getAction();
			if (action == MotionEvent.ACTION_DOWN) {
				initX = 0;
				initY = 0;
				if (mContent.getVisibility() == GONE) {
					// since we may not know content dimensions we use factors
					// here
					if (mOrientation == VERTICAL) {
						initY = mPosition == TOP ? -1 : 1;
					} else {
						initX = mPosition == LEFT ? -1 : 1;
					}
				}
				setInitialPosition = true;
			} else {
				if (setInitialPosition) {
					// now we know content dimensions, so we multiply factors...
					initX *= mContentWidth;
					initY *= mContentHeight;
					// ... and set initial panel's position
					mGestureListener.setScroll(initX, initY);
					setInitialPosition = false;
					// for offsetLocation we have to invert values
					initX = -initX;
					initY = -initY;
				}
				// offset every ACTION_MOVE & ACTION_UP event
				event.offsetLocation(initX, initY);
			}
			if (!mGestureDetector.onTouchEvent(event)) {
				if (action == MotionEvent.ACTION_UP) {
					// tup up after scrolling
					post(startAnimation);
				}
			}
			return false;
		}
	};

Event handling of OnTouchListener for gesture recognition: We can get the type of Touch event through getAction() method of MotionEvent, including ACTION_DOWN, ACTION_MOVE (move the force point after pressing the Touch screen), ACTION_UP (release the Touch screen)

Variables of this function: boolean setInitialPosition// If we click the direction, this variable represents opening the drawer. If we click again, this variable represents closing

				if (mContent.getVisibility() == GONE) {
					// since we may not know content dimensions we use factors
					// here
					if (mOrientation == VERTICAL) {
						initY = mPosition == TOP ? -1 : 1;
					} else {
						initX = mPosition == LEFT ? -1 : 1;
					}
				}

//If the current view is not visible, the main worry is: we don't know the size of the current response control. Need to make a judgment

When the drawer cannot be seen, we need to set the X and Y coordinates of the drawer canvas to (- 1, - 1)

setInitialPosition = true;// If the gesture is pressed, the direction is determined and set to true Close drawer:

				if (setInitialPosition) {//The direction is determined
					// now we know content dimensions, so we multiply factors...
					initX *= mContentWidth;//Initializes the size of the current response control: width and height
					initY *= mContentHeight;
					// ...  and set initial panel's position / / the position of the panel is set here
					mGestureListener.setScroll(initX, initY);//Trigger the gesture recognition event and set the range of gesture sliding
					setInitialPosition = false;//Then set the direction to false
					// for offsetLocation we have to invert values
					initX = -initX;
					initY = -initY;
				}
				// offset every ACTION_MOVE & ACTION_UP event
				event.offsetLocation(initX, initY);

This process corresponds to action_ MOVE & ACTION_ UP event

event. Offset location view for offset restore for next use

When we open the "ring buckle", we need to deal with the business, that is, open the drawer and hand it over to the following code:

			if (!mGestureDetector.onTouchEvent(event)) {//Gesture recognition captures gesture types
				if (action == MotionEvent.ACTION_UP) {//Release the touch screen)
					// tup up after scrolling
					post(startAnimation);
				}
			}

Function post(startAnimation); From the parameters, we can see that this is an animation. Why use animation? Let's talk about it with effect:

Not open

Open this process

Canvas is a slow moving process, where there is an animation process:

This function is a function of a View class, which will call the thread to execute. Regardless of this function, let's see the animation and how we do it:

	Runnable startAnimation = new Runnable() {
		public void run() {
			// this is why we post this Runnable couple of lines above:
			// now its save to use mContent.getHeight() && mContent.getWidth()
			TranslateAnimation animation;
			int fromXDelta = 0, toXDelta = 0, fromYDelta = 0, toYDelta = 0;
			if (mState == State.FLYING) {
				mIsShrinking = (mPosition == TOP || mPosition == LEFT)
						^ (mVelocity > 0);
			}
			int calculatedDuration;
			if (mOrientation == VERTICAL) {
				int height = mContentHeight;
				if (!mIsShrinking) {
					fromYDelta = mPosition == TOP ? -height : height;
				} else {
					toYDelta = mPosition == TOP ? -height : height;
				}
				if (mState == State.TRACKING) {
					if (Math.abs(mTrackY - fromYDelta) < Math.abs(mTrackY
							- toYDelta)) {
						mIsShrinking = !mIsShrinking;
						toYDelta = fromYDelta;
					}
					fromYDelta = (int) mTrackY;
				} else if (mState == State.FLYING) {
					fromYDelta = (int) mTrackY;
				}
				// for FLYING events we calculate animation duration based on
				// flying velocity
				// also for very high velocity make sure duration >= 20 ms
				if (mState == State.FLYING && mLinearFlying) {
					calculatedDuration = (int) (1000 * Math
							.abs((toYDelta - fromYDelta) / mVelocity));
					calculatedDuration = Math.max(calculatedDuration, 20);
				} else {
					calculatedDuration = mDuration
							* Math.abs(toYDelta - fromYDelta) / mContentHeight;
				}
			} else {
				int width = mContentWidth;
				if (!mIsShrinking) {
					fromXDelta = mPosition == LEFT ? -width : width;
				} else {
					toXDelta = mPosition == LEFT ? -width : width;
				}
				if (mState == State.TRACKING) {
					if (Math.abs(mTrackX - fromXDelta) < Math.abs(mTrackX
							- toXDelta)) {
						mIsShrinking = !mIsShrinking;
						toXDelta = fromXDelta;
					}
					fromXDelta = (int) mTrackX;
				} else if (mState == State.FLYING) {
					fromXDelta = (int) mTrackX;
				}
				// for FLYING events we calculate animation duration based on
				// flying velocity
				// also for very high velocity make sure duration >= 20 ms
				if (mState == State.FLYING && mLinearFlying) {
					calculatedDuration = (int) (1000 * Math
							.abs((toXDelta - fromXDelta) / mVelocity));
					calculatedDuration = Math.max(calculatedDuration, 20);
				} else {
					calculatedDuration = mDuration
							* Math.abs(toXDelta - fromXDelta) / mContentWidth;
				}
			}

			mTrackX = mTrackY = 0;
			if (calculatedDuration == 0) {
				mState = State.READY;
				if (mIsShrinking) {
					mContent.setVisibility(GONE);
				}
				postProcess();
				return;
			}

			animation = new TranslateAnimation(fromXDelta, toXDelta,fromYDelta, toYDelta);
			animation.setDuration(calculatedDuration);
			animation.setAnimationListener(animationListener);
			if (mState == State.FLYING && mLinearFlying) {
				animation.setInterpolator(new LinearInterpolator());
			} else if (mInterpolator != null) {
				animation.setInterpolator((android.view.animation.Interpolator) mInterpolator);
			}
			startAnimation(animation);
		}
	};

We need to focus on four variables:

int fromXDelta = 0, toXDelta = 0, fromYDelta = 0, toYDelta = 0;

What do these four variables represent,? We have defined a moving animation object above. The four parameters are required for the construction of TranslateAnimation:

The previous article introduces: Address: http://blog.csdn.net/u014737138/article/details/40861929

fromXDelta  At the beginning of the animation X Position on coordinates  
toXDelta    At the end of the animation X Position on coordinates    
fromYDelta  At the beginning of the animation Y Position on coordinates  
toYDelta    At the end of the animation Y Position on coordinates  

Animation start coordinates to end coordinates, that is, many of our codes calculate the coordinates of animation.

	if (mState == State.FLYING) {
				mIsShrinking = (mPosition == TOP || mPosition == LEFT)
						^ (mVelocity > 0);
			}

If the state is moving, we use the speed to determine whether it is hidden. mIsShrinking is a Boolean variable int calculatedDuration;// The duration of animation, that is, how long the drawer is opened and the whole process of changing the two pictures

Vertical direction: duration

				if (mState == State.FLYING && mLinearFlying) {
					calculatedDuration = (int) (1000 * Math
							.abs((toYDelta - fromYDelta) / mVelocity));
					calculatedDuration = Math.max(calculatedDuration, 20);
				} else {
					calculatedDuration = mDuration
							* Math.abs(toYDelta - fromYDelta) / mContentHeight;
				}

Distance / speed = time. I believe you can understand this code, Vertical direction: coordinate calculation:

				int height = mContentHeight;
				if (!mIsShrinking) {
					fromYDelta = mPosition == TOP ? -height : height;
				} else {
					toYDelta = mPosition == TOP ? -height : height;
				}
				if (mState == State.TRACKING) {
					if (Math.abs(mTrackY - fromYDelta) < Math.abs(mTrackY
							- toYDelta)) {
						mIsShrinking = !mIsShrinking;
						toYDelta = fromYDelta;
					}
					fromYDelta = (int) mTrackY;
				} else if (mState == State.FLYING) {
					fromYDelta = (int) mTrackY;
				}

Snap coordinates:

private PanelOnGestureListener mGestureListener;//Gesture monitoring object

Its definition:
	class PanelOnGestureListener implements OnGestureListener {//This class implements the gesture listener, so you need to overload the necessary functions
		float scrollY;//Scrolling coordinates Y
		float scrollX;//Scrolling coordinate X

		public void setScroll(int initScrollX, int initScrollY) {//Set function
			scrollX = initScrollX;
			scrollY = initScrollY;
		}

		public boolean onDown(MotionEvent e) {//Gesture press / / the user lightly touches the touch screen, which is composed of 1 MotionEvent ACTION_DOWN trigger 
			scrollX = scrollY = 0;
			if (mState != State.READY) {//The current state is not ready. The animation operation will begin soon
				// we are animating or just about to animate
				return false;
			}
			mState = State.ABOUT_TO_ANIMATE;//The state changes to preparing to place animation
			mIsShrinking = mContent.getVisibility() == VISIBLE;//If the screen is currently visible, return true, otherwise return false, that is, we can display the drawer effect only after entering the interface with drawer effect
			if (!mIsShrinking) {
				// this could make flicker so we test mState in dispatchDraw()
				// to see if is equal to ABOUT_TO_ANIMATE
				mContent.setVisibility(VISIBLE);//If the current screen is not visible, we set it to visible
			}
			return true;
		}
		// After the user presses the touch screen and moves quickly, it is released by 1 MotionEvent ACTION_DOWN, multiple actions_ Move, 1 ACTION_UP trigger
		public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
				float velocityY) {//Sliding processing function
			mState = State.FLYING;//The status is sliding
			mVelocity = mOrientation == VERTICAL ? velocityY : velocityX;//The speed setting depends on whether the direction is Y direction or X direction. If it is vertical, it is Y direction. If it is not vertical or horizontal, it is x direction
			post(startAnimation);//Execute animation
			return true;
		}
		// The user long presses the touch screen and multiple motionevent actions_ Down trigger  
		public void onLongPress(MotionEvent e) {//The gesture is a long press and does not need to be handled
			// not used
		}
		
		// The user presses the touch screen and drags it by one MotionEvent ACTION_DOWN, multiple actions_ Move trigger
		public boolean onScroll(MotionEvent e1, MotionEvent e2,
				float distanceX, float distanceY) {//roll
			mState = State.TRACKING;//Gestures are moving
			float tmpY = 0, tmpX = 0;//Temporary variable
			if (mOrientation == VERTICAL) {//Is a vertical scroll
				scrollY -= distanceY;//The coordinate Y of the scroll is always changing
				if (mPosition == TOP) {//If the current position is at the top
					tmpY = ensureRange(scrollY, -mContentHeight, 0);//Specify the range within which Y can be moved
				} else {//The direction is the bottom
					tmpY = ensureRange(scrollY, 0, mContentHeight);
				}
			} else {//If horizontal
				scrollX -= distanceX;//It refers to the coordinates in the X direction
				if (mPosition == LEFT) {//The direction is left
					tmpX = ensureRange(scrollX, -mContentWidth, 0);
				} else {//The direction is to the right
					tmpX = ensureRange(scrollX, 0, mContentWidth);
				}
			}
			// When the values of X and Y directions are determined, you need to see whether this value is equal to the coordinates of your finger movement, that is, set the gesture coordinates
			if (tmpX != mTrackX || tmpY != mTrackY) {
				mTrackX = tmpX;//Set the X coordinate in finger movement
				mTrackY = tmpY;//Set the Y coordinate in finger movement
				invalidate();//update the view
			}
			return true;
		}

		/*  
		* The user lightly touches the touch screen, which has not been released or dragged by one MotionEvent ACTION_DOWN trigger  
		* Note the difference from onDown(), which emphasizes that there is no state of loosening or dragging  
		*/  
		public void onShowPress(MotionEvent e) {
			// not used
		}
		// The user (after touching the touch screen) releases it, which is controlled by one MotionEvent ACTION_UP trigger   
		public boolean onSingleTapUp(MotionEvent e) {
			// simple tap: click
			post(startAnimation);
			return true;
		}
	}

About GestureListener, please see my article above

After the coordinates are calculated, we start the animation:

			animation = new TranslateAnimation(fromXDelta, toXDelta,fromYDelta, toYDelta);
			animation.setDuration(calculatedDuration);
			animation.setAnimationListener(animationListener);
			if (mState == State.FLYING && mLinearFlying) {
				animation.setInterpolator(new LinearInterpolator());
			} else if (mInterpolator != null) {
				animation.setInterpolator((android.view.animation.Interpolator) mInterpolator);
			}
			startAnimation(animation);

Here we need to learn the monitoring events of animation. Please see the previous article

Handling of listening events for Animation:

	private AnimationListener animationListener = new AnimationListener() {
		public void onAnimationEnd(Animation animation) {//End of animation
			mState = State.READY;
			if (mIsShrinking) {
				mContent.setVisibility(GONE);
			}
			postProcess();
		}

		public void onAnimationRepeat(Animation animation) {//Animation restart
		}

		public void onAnimationStart(Animation animation) {//Animation start
			mState = State.ANIMATING;
		}
	};

The picture changes are:

Not open

Open postProcess():

	private void postProcess() {
		if (mIsShrinking && mClosedHandle != null) {
			mHandle.setBackgroundDrawable(mClosedHandle);
		} else if (!mIsShrinking && mOpenedHandle != null) {
			mHandle.setBackgroundDrawable(mOpenedHandle);
		}
		// invoke listener if any
		if (panelListener != null) {
			if (mIsShrinking) {
				panelListener.onPanelClosed(Panel.this);
			} else {
				panelListener.onPanelOpened(Panel.this);
			}
		}
	}

The panelListener variable is a callback function interface type of user-defined view:

	/**
	 * Callback invoked when the panel is opened/closed.
	 */
	public static interface OnPanelListener {
		/**
		 * Invoked when the panel becomes fully closed.
		 */
		public void onPanelClosed(Panel panel);


		/**
		 * Invoked when the panel becomes fully opened.
		 */
		public void onPanelOpened(Panel panel);
	}

Variable definitions required for the whole program:

	private boolean mIsShrinking;//Shrinking means hiding drawers
	private int mPosition;//Position, that is, the position of the drawer, left, right, up and down
	private int mDuration;//Time required to open and close a drawer
	private boolean mLinearFlying;
	private View mHandle;//The subcomponent button on the view, that is, the "buckle"
	private View mContent;//Rectangle representing drawer
	private Drawable mOpenedHandle;//Show drawer background
	private Drawable mClosedHandle;//Close drawer background
	private float mTrackX;//x coordinate of gesture drag
	private float mTrackY;
	private float mVelocity;//speed

	private OnPanelListener panelListener;//Callback function listening object

	public static final int TOP = 0;
	public static final int BOTTOM = 1;
	public static final int LEFT = 2;
	public static final int RIGHT = 3;

	private enum State {
		ABOUT_TO_ANIMATE, ANIMATING, READY, TRACKING, FLYING,
	};

	private State mState;//Corresponding to the above four dynamics
	private Interpolator mInterpolator;//Interpolator
	private GestureDetector mGestureDetector;//gesture recognition 
	private int mContentHeight;//Drawer height
	private int mContentWidth;//Width of drawer
	private int mOrientation;//direction
	private PanelOnGestureListener mGestureListener;//Gesture monitoring object

That's it. It's too long. It's been written for a long time and hasn't been sorted out. The ability to write articles needs to be improved. Let's see It's estimated that you didn't speak clearly, and some made mistakes. Please point out the mistakes in your study

Source address: http://download.csdn.net/detail/u014737138/8139417

Added by lynwell07 on Mon, 07 Mar 2022 08:57:32 +0200