Scrollview回弹效果自定义控件

滚动回弹效果分析:
首先,创建一个类,继承scrollview,重写ontouch事件,实现伸缩回弹效果。
[scroollview节点下只能有一个子节点,这个子节点就是我们要移动的view布局]
 
第一步:获取要操作的子view布局
第二步:重写onTouch事件监听
 
 
分析具体事件:
观察分析得出结论:
让布局移动每一次拉动的Y轴一半的距离,然后松手滚动[携带动画]回到原来的位置。
下拉或者上拉的时候,记录按下时的Y轴位置
action_down:
y
 
移动过程中的处理:
计算上一次与本次的Y轴(拉动距离)[而不是按下时候的Y值,和现在移动到的Y值,是每上一次和本次的Y值比较
判断是否需要移动布局的情况:Y轴的一个距离偏移
 
//2种情况,随着布局的拖动, inner.getMeasuredHeight()的值是变化的
//inner.getMeasuredHeight()与getHeight()的区别:
当屏幕可以包裹内容的时候,他们的值相等
当view的高度超出屏幕时,getMeasuredHeight()是实际View的大小,与屏幕无关,getHeight的大小此时则是屏幕的大小。
此时,getMeasuredHeight() = getHeight+超出部分。
 
抬起的处理:布局回滚到正常位置
移动动画回滚到正常位置(*:动画执行期间,不允许拖拉操作)   
距离:-的滚动距离
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
public class MyScrollview extends ScrollView {
 
     //要操作的布局
     private View innerView;
     private float y;
     private Rect normal = new Rect();
     private boolean animationFinish = true ;
 
     public MyScrollview(Context context) {
         super (context, null );
     }
 
     public MyScrollview(Context context, AttributeSet attrs) {
         super (context, attrs);
     }
 
     public MyScrollview(Context context, AttributeSet attrs, int defStyleAttr) {
         super (context, attrs, defStyleAttr);
     }
 
     @Override
     protected void onFinishInflate() {
         int childCount = getChildCount();
         if (childCount > 0 ) {
             innerView = getChildAt( 0 );
         }
     }
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         if (innerView == null ) {
             return super .onTouchEvent(ev);
         } else {
             commonTouchEvent(ev);
         }
         return super .onTouchEvent(ev);
     }
 
     /**
      * 自定义touch事件处理
      *
      * @param ev
      */
     private void commonTouchEvent(MotionEvent ev) {
         if (animationFinish) {
             int action = ev.getAction();
             switch (action) {
                 case MotionEvent.ACTION_DOWN:
                     y = ev.getY();
                     break ;
                 case MotionEvent.ACTION_MOVE:
                     float preY = y == 0 ? ev.getY() : y;
                     float nowY = ev.getY();
                     int detailY = ( int ) (preY - nowY);
                     y = nowY;
                     //操作view进行拖动detailY的一半
                     if (isNeedMove()) {
                         //布局改变位置之前,记录一下正常状态的位置
                         if (normal.isEmpty()) {
                             normal.set(innerView.getLeft(), innerView.getTop(), innerView.getRight(), innerView.getBottom());
                         }
                         innerView.layout(innerView.getLeft(), innerView.getTop() - detailY / 2 , innerView.getRight(), innerView.getBottom() - detailY / 2 );
                     }
                     break ;
                 case MotionEvent.ACTION_UP:
                     y = 0 ;
                     //布局回滚到原来的位置
                     if (isNeedAnimation()) {
                         animation();
                     }
                     break ;
             }
         }
     }
 
     private void animation() {
         TranslateAnimation ta = new TranslateAnimation( 0 , 0 , 0 , normal.top - innerView.getTop());
         ta.setDuration( 200 );
         ta.setAnimationListener( new Animation.AnimationListener() {
             @Override
             public void onAnimationStart(Animation animation) {
                 animationFinish = false ;
             }
 
             @Override
             public void onAnimationEnd(Animation animation) {
                 innerView.clearAnimation();
                 innerView.layout(normal.left, normal.top, normal.right, normal.bottom);
                 normal.setEmpty();
                 animationFinish = true ;
             }
 
             @Override
             public void onAnimationRepeat(Animation animation) {
 
             }
         });
         innerView.startAnimation(ta);
     }
 
     /**
      * 判断是否需要回滚
      *
      * @return
      */
     private boolean isNeedAnimation() {
         return !normal.isEmpty();
     }
 
     /**
      * 判断是否需要移动
      *
      * @return
      */
     private boolean isNeedMove() {
         int offset = innerView.getMeasuredHeight() - getHeight();
         int scrollY = getScrollY();
         Log.e( "zoubo" , "getMeasuredHeight:" + innerView.getMeasuredHeight() + "----getHeight:" + getHeight());
         Log.e( "zoubo" , "offset:" + offset + "----scrollY:" + scrollY);
         if (scrollY == 0 || scrollY == offset) {
             return true ;
         }
         return false ;
     }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值