[关闭]
@myron-lee 2015-03-17T06:41:40.000000Z 字数 4357 阅读 1691

双层布局 DoubleLayerLayout

Blog


前言

之前用过赶集生活 App,主界面向下滑动可以露出天气信息。效果不错。不仅充分的利用了空间,也给用户以发现的乐趣。所以,我也做了一个这样的布局。

思路

DoubleLayerLayout 继承自 RelativeLayout。正常情况下,foreground view 挡住 background view。滑动时,让 foreground 跟随手指移动,露出 background view。同时,关注手指移动的加速度,实现“猛地”一滑,foreground view 顺势滑开的效果。

参考链接

链接1

知识点

  1. View 中几个比较重要的 hook 的调用顺序:
    onFinishInflate();
    onAttachedToWindow();
    onWindowVisiblityChanged();
    onMeasure();
    onSizeChanged();
    onLayout();
    onDraw();
  2. 打算处理手势之前,触发一个cancelEvent
  1. MotionEvent cancelEvent = MotionEvent.obtain(ev);
  2. cancelEvent.setAction(MotionEvent.ACTION_CANCEL | (ev.getActionIndex()<< MotionEvent.ACTION_POINTER_INDEX_SHIFT));
  3. onTouchEvent(cancelEvent);

源码

  1. package com.example.com.example.widget;
  2. import android.content.Context;
  3. import android.util.AttributeSet;
  4. import android.view.MotionEvent;
  5. import android.view.VelocityTracker;
  6. import android.view.View;
  7. import android.view.animation.AccelerateDecelerateInterpolator;
  8. import android.view.animation.Animation;
  9. import android.view.animation.Transformation;
  10. import android.widget.RelativeLayout;
  11. import android.widget.Scroller;
  12. public class DoubleLayerLayout extends RelativeLayout {
  13. private static final int SNAP_VELOCITY_THRESHOLD = 600;
  14. private static final int DURATION = 200;
  15. private View bgView;
  16. private View fgView;
  17. private Scroller scroller;
  18. private VelocityTracker velocityTracker;
  19. private float lastY;
  20. private int originalTop;
  21. /**
  22. * if totalDy > dyThreshold, show {@link #bgView}
  23. */
  24. private float dyThreshold;
  25. public DoubleLayerLayout(Context context) {
  26. super(context);
  27. init();
  28. }
  29. public DoubleLayerLayout(Context context, AttributeSet attrs) {
  30. super(context, attrs);
  31. init();
  32. }
  33. public DoubleLayerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
  34. super(context, attrs, defStyleAttr);
  35. init();
  36. }
  37. private void init(){
  38. scroller = new Scroller(getContext());
  39. }
  40. @Override
  41. protected void onFinishInflate() {
  42. super.onFinishInflate();
  43. bgView = getChildAt(0);
  44. fgView = getChildAt(1);
  45. }
  46. @Override
  47. protected void onLayout(boolean changed, int l, int t, int r, int b) {
  48. super.onLayout(changed, l, t, r, b);
  49. originalTop = getTop();
  50. dyThreshold = getHeight()*0.25f;
  51. }
  52. @Override
  53. public boolean onTouchEvent(MotionEvent event) {
  54. // return super.onTouchEvent(event);
  55. if (super.onTouchEvent(event)) {
  56. return true;
  57. }
  58. addVelocityTracker(event);
  59. switch (event.getAction()) {
  60. case MotionEvent.ACTION_DOWN:
  61. handleDownEvent(event);
  62. break;
  63. case MotionEvent.ACTION_MOVE:
  64. handleMoveEvent(event);
  65. break;
  66. case MotionEvent.ACTION_UP:
  67. case MotionEvent.ACTION_CANCEL:
  68. handleUpEvent(event);
  69. break;
  70. }
  71. return true;
  72. }
  73. private void handleDownEvent(MotionEvent ev) {
  74. lastY = ev.getY();
  75. }
  76. private void handleMoveEvent(MotionEvent ev) {
  77. // update the position of fgView
  78. float currY = ev.getY();
  79. float dy = currY - lastY;
  80. lastY = currY;
  81. fgView.layout(fgView.getLeft(), ((int) (fgView.getTop() + dy)), fgView.getRight(), ((int) (fgView.getTop() + getMeasuredHeight() + dy)));
  82. // fgView.scrollBy(0, (int) -dy);
  83. }
  84. private void handleUpEvent(MotionEvent ev) {
  85. float totalDy = fgView.getTop() - originalTop;
  86. float snapVelocity = getYVelocity();
  87. if ((totalDy < dyThreshold && snapVelocity < SNAP_VELOCITY_THRESHOLD) || snapVelocity < -SNAP_VELOCITY_THRESHOLD){
  88. // reset
  89. // fgView.layout(fgView.getLeft(), originalTop, fgView.getRight(), originalTop+getMeasuredHeight());
  90. animateLayout(fgView, originalTop-fgView.getTop());
  91. } else {
  92. // show bgView
  93. // fgView.layout(fgView.getLeft(), ((int) (originalTop + getMeasuredHeight() * 0.5f)), getRight(), ((int) (originalTop + getMeasuredHeight() * 1.5f)));
  94. animateLayout(fgView, ((int) (originalTop + getMeasuredHeight() * 0.5f - fgView.getTop())));
  95. }
  96. recycleVelocityTracker();
  97. }
  98. private void animateLayout(View view, int dy){
  99. Animation layoutAnimation = new LayoutAnimation(view, dy);
  100. layoutAnimation.setDuration(DURATION);
  101. layoutAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
  102. startAnimation(layoutAnimation);
  103. }
  104. private void addVelocityTracker(MotionEvent event) {
  105. if (velocityTracker == null) {
  106. velocityTracker = VelocityTracker.obtain();
  107. }
  108. velocityTracker.addMovement(event);
  109. }
  110. private void recycleVelocityTracker() {
  111. if (velocityTracker != null) {
  112. velocityTracker.recycle();
  113. velocityTracker = null;
  114. }
  115. }
  116. private int getYVelocity() {
  117. velocityTracker.computeCurrentVelocity(1000);
  118. int velocity = (int) velocityTracker.getYVelocity();
  119. return velocity;
  120. }
  121. class LayoutAnimation extends Animation {
  122. private View view;
  123. private int dy;
  124. private int t;
  125. private int h;
  126. private int l;
  127. private int r;
  128. public LayoutAnimation(View view, int dy) {
  129. super();
  130. this.view = view;
  131. this.dy = dy;
  132. t = view.getTop();
  133. l = view.getLeft();
  134. r = view.getRight();
  135. h = view.getMeasuredHeight();
  136. }
  137. @Override
  138. protected void applyTransformation(float interpolatedTime,
  139. Transformation transformation) {
  140. int dy = (int) (interpolatedTime * this.dy);
  141. view.layout(l, t + dy, r, t + h + dy);
  142. }
  143. }
  144. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注