Android custom view achieves drawer effect
illustrate
- This custom view does not handle multi-touch problems
- The view moves with the fingers, instead of using the traditional scrollBy method, it makes the subviews scrolling effect (menuLeft, 0, menuLeft + menuWidth, menuHeight);
- Correspondingly, since the scrollBy method is not used, the getScrollX value is not generated, so the smooth scrolling effect after the finger leaves cannot be completed through the startScroller method. Instead, the applyTransformation method of Animation animation is used to complete the interpolation, thereby achieving the animation effect.
The main algorithm is: the current value of the animation = starting value + (target value - starting value) *interpolatedTime
Among them, interpolatedTime is a number of 0.0f~1.0f. The system has calculated it by itself (the default is linearly changed). Of course, you can write the interpolation yourself.
/** * Since scrollBy cannot be used above, the Scroller class cannot be used here to complete smooth movement. Fortunately, we have animations */ class MyAnimation extends Animation { private int viewCurrentLfet; private int viewStartLfet; private int viewTargetLfet; private int viewWidth; private View view; private int cha; public MyAnimation(View view, int viewStartLfet, int viewTargetLfet, int viewWidth) { = view; = viewStartLfet; = viewTargetLfet; = viewWidth; cha = viewTargetLfet - viewStartLfet; setDuration((cha)); } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { (interpolatedTime, t); viewCurrentLfet = (int) (viewStartLfet + cha * interpolatedTime); (viewCurrentLfet, 0, viewCurrentLfet + viewWidth, menuHeight); } }
Complete code
package ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; /** * Created by a on 2016/8/15. */ public class ChouTiView extends ViewGroup { private View mainView; private View menuView; private int menuWidth; private int downX; private int lastX; private int moveX; private int deltaX; private int menuLeft; private int mainLeft; private int menuHeight; private int mainWidth; private int mainHeight; private int menuLeftBorder; private int mainLeftBorder; private int menuRightBorder; private int mainRightBorder; private int mMaxVelocity; private VelocityTracker mVelocityTracker; private int mPointerId; private float velocityX; private float velocityY; public ChouTiView(Context context) { super(context); init(); } public ChouTiView(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { // 0. Obtain the maximum speed this time mMaxVelocity = (getContext()).getMaximumFlingVelocity(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { (widthMeasureSpec, heightMeasureSpec); (widthMeasureSpec, heightMeasureSpec); (widthMeasureSpec, heightMeasureSpec); // Get the correct width of the subview (only get specific numeric values), but you cannot get the height in this way, because here match-parent is -1 menuWidth = ().width; menuLeft = (int) (-menuWidth * 0.5); menuLeftBorder = (int) (-menuWidth * 0.5); menuRightBorder = 0; mainLeft = 0; mainLeftBorder = 0; mainRightBorder = menuWidth; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { menuHeight = b; mainWidth = r; mainHeight = b; (l, t, r, b); (menuLeft, t, menuLeft + menuWidth, b); } @Override protected void onFinishInflate() { (); mainView = getChildAt(1); menuView = getChildAt(0); } @Override public boolean onTouchEvent(MotionEvent event) { final int action = (); acquireVelocityTracker(event); //1. Add MotionEvent to VelocityTracker final VelocityTracker verTracker = mVelocityTracker; switch (action) { case MotionEvent.ACTION_DOWN: //2. Find the id of the first contact. At this time, there may be multiple contacts, but at least one // Get the finger id with index 0 mPointerId = (0); downX = (int) (); lastX = downX; break; case MotionEvent.ACTION_MOVE: // Get the index corresponding to the current finger id. Although when ACTION_DOWN, we select the index as 0 by default // The finger, but when there is a second finger touching and the previously effective finger up, we will adjust the effective finger // There may be multiple fingers on the screen, and we need to ensure that the movement of the same finger is used. // Therefore, we cannot use() to obtain the index here final int pointerIndex = (mPointerId); moveX = (int) (pointerIndex); deltaX = moveX - lastX; // Indicate the increment caused by touch movement on the left side of menu and main menuLeft = (int) (menuLeft + deltaX * 0.43);//Let the menu move slowly mainLeft = mainLeft + deltaX; // Let the menu move according to the increments of the fingers, considering the boundary problem of both sides (to achieve the movement effect by constantly laying out)// Why is scrollBy not applicable, because scrollBy moves the outer large view, now the requirement is to move two small views in this large view separately// If scrollBy, the menu and main page will be moved at the same time, and there will be no misalignment effect.// You will think, let the little view scrollBy by itself, this is not OK.// Because if a small view, for example, menu calls scrollBy, the menu's own border will move.// It seems that the text inside the menu is moving, but the menu is not moving in the big view on the outer layer.// It's difficult to say, but it really can't be used to scrollBy if (menuLeft >= menuRightBorder) { menuLeft = menuRightBorder; } else if (menuLeft <= menuLeftBorder) { menuLeft = menuLeftBorder; } (menuLeft, 0, menuLeft + menuWidth, menuHeight); // Let the main page move according to the finger increments, considering the boundary issues on both sides if (mainLeft >= mainRightBorder) { mainLeft = mainRightBorder; } else if (mainLeft <= mainLeftBorder) { mainLeft = mainLeftBorder; } (mainLeft, 0, mainLeft + mainWidth, mainHeight); lastX = moveX; break; case MotionEvent.ACTION_UP: //3. Find the pseudo-instantaneous speed (1000, mMaxVelocity); velocityX = (mPointerId); ("qwe", velocityX + "/" + mMaxVelocity); if (velocityX > 1000) { smoothToMenu(); } else if (velocityX < -2000) { smoothToMain(); } else { // Determine the position of letting go. If the menu width is greater than 1/2.5, open the menu, otherwise open the main page if (mainLeft > menuWidth / 2.5) { ("qqq", "Show Menu"); smoothToMenu(); } else { ("qqq", "Show main page"); smoothToMain(); } } // 4.ACTION_UP releases VelocityTracker and gives it to other controls to use releaseVelocityTracker(); break; case MotionEvent.ACTION_CANCEL: // 4.ACTION_UP releases VelocityTracker and gives it to other controls to use releaseVelocityTracker(); case MotionEvent.ACTION_POINTER_UP: // Get the index of the fingers leaving the screen int pointerIndexLeave = (); int pointerIdLeave = (pointerIndexLeave); if (mPointerId == pointerIdLeave) { // The current valid fingers that leave the screen need to be readjusted here and VelocityTracker needs to be reset int reIndex = pointerIndexLeave == 0 ? 1 : 0; mPointerId = (reIndex); // Adjust the touch position to prevent jumps downX = (int) (reIndex); // y = (reIndex); releaseVelocityTracker(); } releaseVelocityTracker(); break; } return true; } private void smoothToMain() { MyAnimation menuAnimation = new MyAnimation(menuView, menuLeft, menuLeftBorder, menuWidth); MyAnimation mainAnimation = new MyAnimation(mainView, mainLeft, mainLeftBorder, mainWidth); AnimationSet animationSet = new AnimationSet(true); (menuAnimation); (mainAnimation); startAnimation(animationSet); //Remember to update the left state of menu and main, which affects the animation when touching the finger again, otherwise it will change suddenly menuLeft = menuLeftBorder; mainLeft = mainLeftBorder; } private void smoothToMenu() { MyAnimation menuAnimation = new MyAnimation(menuView, menuLeft, menuRightBorder, menuWidth); MyAnimation mainAnimation = new MyAnimation(mainView, mainLeft, mainRightBorder, mainWidth); AnimationSet animationSet = new AnimationSet(true); (menuAnimation); (mainAnimation); startAnimation(animationSet); //Remember to update the left state of menu and main, which affects the animation when touching the finger again, otherwise it will change suddenly menuLeft = menuRightBorder; mainLeft = mainRightBorder; } /** * @param event Add MotionEvent to VelocityTracker * @see #obtain() * @see #addMovement(MotionEvent) */ private void acquireVelocityTracker(final MotionEvent event) { if (null == mVelocityTracker) { mVelocityTracker = (); } (event); } /** * Release VelocityTracker * * @see #clear() * @see #recycle() */ private void releaseVelocityTracker() { if (null != mVelocityTracker) { (); (); mVelocityTracker = null; } } /** * Since scrollBy cannot be used above, the Scroller class cannot be used here to complete smooth movement. Fortunately, we have animations */ class MyAnimation extends Animation { private int viewCurrentLfet; private int viewStartLfet; private int viewTargetLfet; private int viewWidth; private View view; private int cha; public MyAnimation(View view, int viewStartLfet, int viewTargetLfet, int viewWidth) { = view; = viewStartLfet; = viewTargetLfet; = viewWidth; cha = viewTargetLfet - viewStartLfet; setDuration((cha)); } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { (interpolatedTime, t); viewCurrentLfet = (int) (viewStartLfet + cha * interpolatedTime); (viewCurrentLfet, 0, viewCurrentLfet + viewWidth, menuHeight); } } }
Thank you for reading, I hope it can help you. Thank you for your support for this site!