要實現ScrollView的回彈效果,需要對其進行觸摸事件處理。先來看一下簡單的效果:
根據Android的View事件分發處理機制,下面對dispatchTouchEvent進行詳細分析:
在加載布局完成之后,獲取ScrollView的第一個子元素,保存它的參數,left top right bottom參數,根據頂部下拉操作和底部上拉操作進行子View的布局參數根據滑動距離改變,ACTION_UP的時候判斷是否存在回彈,如果需要則進行動畫回彈到原來的位置,可以添加一個回彈結束監聽,比如監聽回彈處理跳轉到其他的頁面的操作等。
具體的實現如下,添加了是否禁用頂部和底部回彈的參數設置,以及回彈效果結束監聽。
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
/** * A Simple Rebound ScrollView * @author Denluoyia */ public class ReboundScrollView extends ScrollView{ private boolean mEnableTopRebound = true ; private boolean mEnableBottomRebound = true ; private OnReboundEndListener mOnReboundEndListener; private View mContentView; private Rect mRect = new Rect(); public ReboundScrollView(Context context) { super (context); } public ReboundScrollView(Context context, AttributeSet attrs) { super (context, attrs); } public ReboundScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super (context, attrs, defStyleAttr); } /** after inflating view, we can get the width and height of view */ @Override protected void onFinishInflate() { super .onFinishInflate(); mContentView = getChildAt( 0 ); } @Override protected void onLayout( boolean changed, int l, int t, int r, int b) { super .onLayout(changed, l, t, r, b); if (mContentView == null ) return ; // to remember the location of mContentView mRect.set(mContentView.getLeft(), mContentView.getTop(), mContentView.getRight(), mContentView.getBottom()); } public ReboundScrollView setOnReboundEndListener(OnReboundEndListener onReboundEndListener){ this .mOnReboundEndListener = onReboundEndListener; return this ; } public ReboundScrollView setEnableTopRebound( boolean enableTopRebound){ this .mEnableTopRebound = enableTopRebound; return this ; } public ReboundScrollView setEnableBottomRebound( boolean mEnableBottomRebound){ this .mEnableBottomRebound = mEnableBottomRebound; return this ; } private int lastY; private boolean rebound = false ; private int reboundDirection = 0 ; //<0 表示下部回彈 >0 表示上部回彈 0表示不回彈 @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (mContentView == null ){ return super .dispatchTouchEvent(ev); } switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: lastY = ( int ) ev.getY(); break ; case MotionEvent.ACTION_MOVE: if (!isScrollToTop() && !isScrollToBottom()){ lastY = ( int ) ev.getY(); break ; } //處于頂部或者底部 int deltaY = ( int ) (ev.getY() - lastY); //deltaY > 0 下拉 deltaY < 0 上拉 //disable top or bottom rebound if ((!mEnableTopRebound && deltaY > 0 ) || (!mEnableBottomRebound && deltaY < 0 )){ break ; } int offset = ( int ) (deltaY * 0.48 ); mContentView.layout(mRect.left, mRect.top + offset, mRect.right, mRect.bottom + offset); rebound = true ; break ; case MotionEvent.ACTION_UP: if (!rebound) break ; reboundDirection = mContentView.getTop() - mRect.top; TranslateAnimation animation = new TranslateAnimation( 0 , 0 , mContentView.getTop(), mRect.top); animation.setDuration( 300 ); animation.setAnimationListener( new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { if (mOnReboundEndListener != null ){ if (reboundDirection > 0 ){ mOnReboundEndListener.onReboundTopComplete(); } if (reboundDirection < 0 ){ mOnReboundEndListener.onReboundBottomComplete(); } reboundDirection = 0 ; } } @Override public void onAnimationRepeat(Animation animation) { } }); mContentView.startAnimation(animation); mContentView.layout(mRect.left, mRect.top, mRect.right, mRect.bottom); rebound = false ; break ; } return super .dispatchTouchEvent(ev); } @Override public void setFillViewport( boolean fillViewport) { super .setFillViewport( true ); //默認是填充ScrollView 或者再XML布局文件中設置fillViewport屬性 } /** * 判斷當前ScrollView是否處于頂部 */ private boolean isScrollToTop(){ return getScrollY() == 0 ; } /** * 判斷當前ScrollView是否已滑到底部 */ private boolean isScrollToBottom(){ return mContentView.getHeight() <= getHeight() + getScrollY(); } /** * listener for top and bottom rebound * do your implement in the following methods */ public interface OnReboundEndListener{ void onReboundTopComplete(); void onReboundBottomComplete(); } } |
使用:
直接在XML布局文件中把ScrollView替換成ReboundScrollView就可以了。還可以拓展把回彈頂部和底部添加其他的動畫效果(之后再拓展試下)。
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
|
<? xml version = "1.0" encoding = "utf-8" ?> < LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android" xmlns:tools = "http://schemas.android.com/tools" android:layout_width = "match_parent" android:layout_height = "match_parent" android:orientation = "vertical" tools:context = ".TestActivity" > < com.denluoyia.dtils.widget.ReboundScrollView android:id = "@+id/reboundScrollView" android:layout_width = "match_parent" android:layout_height = "match_parent" > < LinearLayout android:layout_width = "match_parent" android:layout_height = "match_parent" android:background = "#eefade" android:padding = "16dp" > < TextView android:layout_width = "match_parent" android:layout_height = "match_parent" android:textSize = "15sp" android:lineSpacingExtra = "5dp" android:text = "@string/content" /> </ LinearLayout > </ com.denluoyia.dtils.widget.ReboundScrollView > </ LinearLayout > |
如果需要禁用回彈,可以直接設置enableTopRebound和enableBottomRebound參數,同樣設置回彈結束(或開始)監聽。
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
|
public class TestActivity extends AppCompatActivity { private ReboundScrollView reboundScrollView; @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_test); reboundScrollView = findViewById(R.id.reboundScrollView); //reboundScrollView.setEnableTopRebound(false); //reboundScrollView.setEnableBottomRebound(false); reboundScrollView.setOnReboundEndListener( new ReboundScrollView.OnReboundEndListener() { @Override public void onReboundTopComplete() { Toast.makeText(TestActivity. this , "頂部回彈" , Toast.LENGTH_SHORT).show(); } @Override public void onReboundBottomComplete() { Toast.makeText(TestActivity. this , "底部回彈" , Toast.LENGTH_SHORT).show(); } }); } } |
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:http://www.cnblogs.com/denluoyia/p/8995589.html