至此,2种拦截方法已经学习完毕,下面我们来学习如何解决同向滑动冲突。
其实和上面的2个例子思路是一样的,只是用来判断是否拦截的那块逻辑不同而已。
下面的例子,是一个下拉刷新的一个控件。
3、外部拦截 解决同向滑动冲突
RefreshLayoutBase.java
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
|
<code
class
=
"hljs cs"
>
package
com.blueberry.sample.widget.refresh;
import
android.content.Context;
import
android.graphics.Color;
import
android.util.AttributeSet;
import
android.util.DisplayMetrics;
import
android.util.Log;
import
android.util.TypedValue;
import
android.view.MotionEvent;
import
android.view.View;
import
android.view.ViewConfiguration;
import
android.view.ViewGroup;
import
android.view.WindowManager;
import
android.widget.ProgressBar;
import
android.widget.Scroller;
import
android.widget.TextView;
import
com.blueberry.sample.R;
/**
* Created by blueberry on 2016/6/21.
*
*外部拦截(同向)
*
*/
public
abstract
class
RefreshLayoutBase<t
extends
=
""
view=
""
>
extends
ViewGroup {
private
static
final
String TAG =
"RefreshLayoutBase"
;
public
static
final
int
STATUS_LOADING =
1
;
public
static
final
int
STATUS_RELEASE_TO_REFRESH =
2
;
public
static
final
int
STATUS_PULL_TO_REFRESH =
3
;
public
static
final
int
STATUS_IDLE =
4
;
public
static
final
int
STATUS_LOAD_MORE =
5
;
private
static
int
SCROLL_DURATION =
500
;
protected
ViewGroup mHeadView;
protected
ViewGroup mFootView;
private
T contentView;
private
ProgressBar headProgressBar;
private
TextView headTv;
private
ProgressBar footProgressBar;
private
TextView footTv;
private
boolean
isFistTouch =
true
;
protected
int
currentStatus = STATUS_IDLE;
private
int
mScreenWidth;
private
int
mScreenHeight;
private
int
mLastXIntercepted;
private
int
mLastYIntercepted;
private
int
mLastX;
private
int
mLastY;
protected
int
mInitScrollY =
0
;
private
int
mTouchSlop;
protected
Scroller mScoller;
private
OnRefreshListener mOnRefreshListener;
public
RefreshLayoutBase(Context context) {
this
(context,
null
);
}
public
RefreshLayoutBase(Context context, AttributeSet attrs) {
this
(context, attrs,
0
);
}
public
RefreshLayoutBase(Context context, AttributeSet attrs,
int
defStyleAttr) {
super
(context, attrs, defStyleAttr);
getScreenSize();
initView();
mScoller =
new
Scroller(context);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
setPadding(
0
,
0
,
0
,
0
);
}
public
void
setContentView(T view) {
addView(view,
1
);
}
public
OnRefreshListener getOnRefreshListener() {
return
mOnRefreshListener;
}
public
void
setOnRefreshListener(OnRefreshListener mOnRefreshListener) {
this
.mOnRefreshListener = mOnRefreshListener;
}
private
void
initView() {
setupHeadView();
setupFootView();
}
private
void
getScreenSize() {
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics =
new
DisplayMetrics();
wm.getDefaultDisplay().getMetrics(metrics);
mScreenWidth = metrics.widthPixels;
mScreenHeight = metrics.heightPixels;
}
private
int
dp2px(
int
dp) {
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics =
new
DisplayMetrics();
wm.getDefaultDisplay().getMetrics(metrics);
return
(
int
) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, metrics);
}
/**
* 设置头布局
*/
private
void
setupHeadView() {
mHeadView = (ViewGroup) View.inflate(getContext(), R.layout.fresh_head_view,
null
);
mHeadView.setBackgroundColor(Color.RED);
headProgressBar = (ProgressBar) mHeadView.findViewById(R.id.head_progressbar);
headTv = (TextView) mHeadView.findViewById(R.id.head_tv);
/*设置 实际高度为 1/4 ,但内容区域只有 100dp*/
ViewGroup.LayoutParams layoutParams =
new
ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, mScreenHeight /
4
);
mHeadView.setLayoutParams(layoutParams);
mHeadView.setPadding(
0
, mScreenHeight /
4
- dp2px(
100
),
0
,
0
);
addView(mHeadView);
}
/**
* 设置尾布局
*/
private
void
setupFootView() {
mFootView = (ViewGroup) View.inflate(getContext(), R.layout.fresh_foot_view,
null
);
mFootView.setBackgroundColor(Color.BLUE);
footProgressBar = (ProgressBar) mFootView.findViewById(R.id.fresh_foot_progressbar);
footTv = (TextView) mFootView.findViewById(R.id.fresh_foot_tv);
addView(mFootView);
}
@Override
protected
void
onMeasure(
int
widthMeasureSpec,
int
heightMeasureSpec) {
int
widthSize = MeasureSpec.getSize(widthMeasureSpec);
int
widthMode = MeasureSpec.getMode(widthMeasureSpec);
int
height = MeasureSpec.getSize(heightMeasureSpec);
int
heightMode = MeasureSpec.getMode(heightMeasureSpec);
int
finalHeight =
0
;
for
(
int
i =
0
; i < getChildCount(); i++) {
View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
finalHeight += child.getMeasuredHeight();
}
if
(widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
widthSize = getChildAt(
0
).getMeasuredWidth();
setMeasuredDimension(widthSize, finalHeight);
}
else
if
(widthMode == MeasureSpec.AT_MOST) {
widthSize = getChildAt(
0
).getMeasuredWidth();
setMeasuredDimension(widthSize, height);
}
else
{
setMeasuredDimension(widthSize, finalHeight);
}
}
@Override
protected
void
onLayout(
boolean
changed,
int
l,
int
t,
int
r,
int
b) {
int
topOffset =
0
;
for
(
int
i =
0
; i < getChildCount(); i++) {
View child = getChildAt(i);
child.layout(getPaddingLeft(), getPaddingTop() + topOffset, r, getPaddingTop() + child.getMeasuredHeight() + topOffset);
topOffset += child.getMeasuredHeight();
}
mInitScrollY = mHeadView.getMeasuredHeight() + getPaddingTop();
scrollTo(
0
, mInitScrollY);
}
@Override
public
boolean
onInterceptTouchEvent(MotionEvent ev) {
boolean
intercepted =
false
;
int
x = (
int
) ev.getX();
int
y = (
int
) ev.getY();
switch
(ev.getAction()) {
case
MotionEvent.ACTION_DOWN:
mLastXIntercepted = x;
mLastYIntercepted = y;
break
;
case
MotionEvent.ACTION_MOVE:
final
int
deltaY = x - mLastYIntercepted;
if
(isTop() && deltaY >
0
&& Math.abs(deltaY) > mTouchSlop) {
/*下拉*/
intercepted =
true
;
}
break
;
case
MotionEvent.ACTION_UP:
break
;
}
mLastXIntercepted = x;
mLastYIntercepted = y;
return
intercepted;
}
private
void
doRefresh() {
Log.i(TAG,
"doRefresh: "
);
if
(currentStatus == STATUS_RELEASE_TO_REFRESH) {
mScoller.startScroll(
0
, getScrollY(),
0
, mInitScrollY - getScrollY(), SCROLL_DURATION);
currentStatus = STATUS_IDLE;
}
else
if
(currentStatus == STATUS_PULL_TO_REFRESH) {
mScoller.startScroll(
0
,getScrollY(),
0
,
0
-getScrollY(),SCROLL_DURATION);
if
(
null
!= mOnRefreshListener) {
currentStatus = STATUS_LOADING;
mOnRefreshListener.refresh();
}
}
invalidate();
}
@Override
public
boolean
onTouchEvent(MotionEvent event) {
int
x = (
int
) event.getX();
int
y = (
int
) event.getY();
switch
(event.getAction()) {
case
MotionEvent.ACTION_DOWN:
if
(!mScoller.isFinished()) {
mScoller.abortAnimation();
}
mLastX = x;
mLastY = y;
break
;
case
MotionEvent.ACTION_MOVE:
if
(isFistTouch) {
isFistTouch =
false
;
mLastX = x;
mLastY = y;
}
final
int
deltaY = y - mLastY;
if
(currentStatus != STATUS_LOADING) {
changeScrollY(deltaY);
}
break
;
case
MotionEvent.ACTION_UP:
isFistTouch =
true
;
doRefresh();
break
;
}
mLastX = x;
mLastY = y;
return
true
;
}
private
void
changeScrollY(
int
deltaY) {
Log.i(TAG,
"changeScrollY: "
);
int
curY = getScrollY();
if
(deltaY >
0
) {
/*下拉*/
if
(curY - deltaY > getPaddingTop()) {
scrollBy(
0
, -deltaY);
}
}
else
{
/*上拉*/
if
(curY - deltaY <= mInitScrollY) {
scrollBy(
0
, -deltaY);
}
}
curY = getScrollY();
int
slop = mInitScrollY /
2
;
if
(curY >
0
&& curY <=slop) {
currentStatus = STATUS_PULL_TO_REFRESH;
}
else
if
(curY >
0
&& curY >= slop) {
currentStatus = STATUS_RELEASE_TO_REFRESH;
}
}
@Override
public
void
computeScroll() {
if
(mScoller.computeScrollOffset()) {
scrollTo(mScoller.getCurrX(), mScoller.getCurrY());
postInvalidate();
}
}
/**
* 加载完成调用这个方法
*/
public
void
refreshComplete() {
mScoller.startScroll(
0
, getScrollY(),
0
, mInitScrollY - getScrollY(), SCROLL_DURATION);
currentStatus = STATUS_IDLE;
invalidate();
}
/**
* 显示 Footer
*/
public
void
showFooter() {
if
(currentStatus==STATUS_LOAD_MORE)
return
;
currentStatus = STATUS_LOAD_MORE ;
mScoller.startScroll(
0
, getScrollY(),
0
, mFootView.getMeasuredHeight()
, SCROLL_DURATION);
invalidate();
}
/**
* loadMore完成之后调用
*/
public
void
footerComplete() {
mScoller.startScroll(
0
, getScrollY(),
0
, mInitScrollY - getScrollY(), SCROLL_DURATION);
invalidate();
currentStatus = STATUS_IDLE;
}
public
interface
OnRefreshListener {
void
refresh();
}
abstract
boolean
isTop();
abstract
boolean
isBottom();
}
</t></code>
|
它是一个抽象类,需要编写子类继承isTop()和 isBottom()方法、
下面给出它的一个实现类:
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
|
<code
class
=
"hljs cs"
>
package
com.blueberry.sample.widget.refresh;
import
android.content.Context;
import
android.util.AttributeSet;
import
android.widget.AbsListView;
import
android.widget.ListView;
/**
* Created by blueberry on 2016/6/21.
*
* RefreshLayoutBase 的一个实现类
*/
public
class
RefreshListView
extends
RefreshLayoutBase<listview> {
private
static
final
String TAG =
"RefreshListView"
;
private
ListView listView;
private
OnLoadListener loadListener;
public
RefreshListView(Context context) {
super
(context);
}
public
RefreshListView(Context context, AttributeSet attrs) {
super
(context, attrs);
}
public
RefreshListView(Context context, AttributeSet attrs,
int
defStyleAttr) {
super
(context, attrs, defStyleAttr);
}
public
ListView getListView() {
return
listView;
}
public
void
setListView(
final
ListView listView) {
this
.listView = listView;
setContentView(listView);
this
.listView.setOnScrollListener(
new
AbsListView.OnScrollListener() {
@Override
public
void
onScrollStateChanged(AbsListView view,
int
scrollState) {
}
@Override
public
void
onScroll(AbsListView view,
int
firstVisibleItem,
int
visibleItemCount,
int
totalItemCount) {
/*这里存在一个bug: 当listView滑动到底部的时候,如果下拉也会出现footer
* 这是因为,暂时还没有想到如何判断是下拉还是上拉。
* 如果要解决此问题,我觉得应该重写listView 的onTouchEvent来判断手势方向
* 次模块主要解决竖向滑动冲突,故现将此问题放下。
* */
if
(currentStatus == STATUS_IDLE
&& getScrollY() <= mInitScrollY && isBottom()
) {
showFooter();
if
(
null
!= loadListener) {
loadListener.onLoadMore();
}
}
}
});
}
public
OnLoadListener getLoadListener() {
return
loadListener;
}
public
void
setLoadListener(OnLoadListener loadListener) {
this
.loadListener = loadListener;
}
@Override
boolean
isTop() {
return
listView.getFirstVisiblePosition() ==
0
&& getScrollY() <= mHeadView.getMeasuredHeight();
}
@Override
boolean
isBottom() {
return
listView.getLastVisiblePosition() == listView.getAdapter().getCount() -
1
;
}
public
interface
OnLoadListener {
void
onLoadMore();
}
}
</listview></code>
|
4、内部拦截法解决同向滑动
同样是一个下拉刷新组件,因为实现原理都一样,所以这个写的比较随意些。主要还是如果解决滑动冲突。
RefreshLayoutBase2.java
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
|
<code
class
=
"hljs java"
>
package
com.blueberry.sample.widget.refresh;
import
android.content.Context;
import
android.graphics.Color;
import
android.util.AttributeSet;
import
android.util.Log;
import
android.view.MotionEvent;
import
android.view.View;
import
android.view.ViewGroup;
import
android.widget.ArrayAdapter;
import
android.widget.ListView;
import
android.widget.Scroller;
import
com.blueberry.sample.R;
import
java.util.ArrayList;
import
java.util.List;
/**
* Created by blueberry on 2016/6/22.
* 结合内部类 ListVieEx
* 内部拦截法,同向
*/
public
class
RefreshLayoutBase2
extends
ViewGroup {
private
static
final
String TAG =
"RefreshLayoutBase2"
;
private
static
List<string> datas;
static
{
datas =
new
ArrayList<>();
for
(
int
i =
0
; i <
40
; i++) {
datas.add(
"数据—"
+ i);
}
}
private
ViewGroup headView;
private
ListViewEx lv;
private
int
lastY;
public
int
mInitScrollY;
private
Scroller mScroller;
public
RefreshLayoutBase2(Context context) {
this
(context,
null
);
}
public
RefreshLayoutBase2(Context context, AttributeSet attrs) {
this
(context, attrs,
0
);
}
public
RefreshLayoutBase2(Context context, AttributeSet attrs,
int
defStyleAttr) {
super
(context, attrs, defStyleAttr);
mScroller =
new
Scroller(context);
setupHeadView(context);
setupContentView(context);
}
@Override
protected
void
onMeasure(
int
widthMeasureSpec,
int
heightMeasureSpec) {
int
widthSize = MeasureSpec.getSize(widthMeasureSpec);
int
widthMode = MeasureSpec.getMode(widthMeasureSpec);
int
height = MeasureSpec.getSize(heightMeasureSpec);
int
heightMode = MeasureSpec.getMode(heightMeasureSpec);
int
finalHeight =
0
;
for
(
int
i =
0
; i < getChildCount(); i++) {
View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
finalHeight += child.getMeasuredHeight();
}
if
(widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
widthSize = getChildAt(
0
).getMeasuredWidth();
setMeasuredDimension(widthSize, finalHeight);
}
else
if
(widthMode == MeasureSpec.AT_MOST) {
widthSize = getChildAt(
0
).getMeasuredWidth();
setMeasuredDimension(widthSize, height);
}
else
{
setMeasuredDimension(widthSize, finalHeight);
}
}
@Override
protected
void
onLayout(
boolean
changed,
int
l,
int
t,
int
r,
int
b) {
int
topOffset =
0
;
for
(
int
i =
0
; i < getChildCount(); i++) {
View child = getChildAt(i);
child.layout(getPaddingLeft(), getPaddingTop() + topOffset, r, getPaddingTop() + child.getMeasuredHeight() + topOffset);
topOffset += child.getMeasuredHeight();
}
mInitScrollY = headView.getMeasuredHeight() + getPaddingTop();
scrollTo(
0
, mInitScrollY);
}
/**
* 不拦截Down 其他一律拦截
* @param ev
* @return
*/
@Override
public
boolean
onInterceptTouchEvent(MotionEvent ev) {
if
(ev.getAction() == MotionEvent.ACTION_DOWN)
return
false
;
return
true
;
}
@Override
public
boolean
onTouchEvent(MotionEvent event) {
int
y = (
int
) event.getY();
switch
(event.getAction()) {
case
MotionEvent.ACTION_DOWN:
break
;
case
MotionEvent.ACTION_MOVE:
final
int
deltaY = y-lastY;
Log.i(TAG,
"onTouchEvent: deltaY: "
+deltaY);
if
(deltaY >=
0
&& lv.isTop() && getScrollY() - deltaY >=getPaddingTop()) {
scrollBy(
0
, -deltaY);
}
break
;
case
MotionEvent.ACTION_UP:
this
.postDelayed(
new
Runnable() {
@Override
public
void
run() {
mScroller.startScroll(
0
,getScrollY(),
0
,mInitScrollY-getScrollY());
invalidate();
}
},
2000
);
break
;
}
lastY = y ;
return
true
;
}
private
void
setupHeadView(Context context) {
headView = (ViewGroup) View.inflate(context, R.layout.fresh_head_view,
null
);
headView.setBackgroundColor(Color.RED);
ViewGroup.LayoutParams params =
new
ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
300
);
addView(headView, params);
}
public
void
setupContentView(Context context) {
lv =
new
ListViewEx(context,
this
);
lv.setBackgroundColor(Color.BLUE);
ArrayAdapter<string> adapter =
new
ArrayAdapter<string>(getContext(), android.R.layout.simple_list_item_1, datas);
lv.setAdapter(adapter);
addView(lv,
new
ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
@Override
public
void
computeScroll() {
if
(mScroller.computeScrollOffset()){
scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
postInvalidate();
}
}
public
static
class
ListViewEx
extends
ListView {
private
RefreshLayoutBase2 outter;
public
ListViewEx(Context context, RefreshLayoutBase2 outter) {
super
(context);
this
.outter = outter;
}
public
ListViewEx(Context context, AttributeSet attrs) {
super
(context, attrs);
}
public
ListViewEx(Context context, AttributeSet attrs,
int
defStyleAttr) {
super
(context, attrs, defStyleAttr);
}
/**
* 使用 outter.requestDisallowInterceptTouchEvent();
* 来决定父控件是否对事件进行拦截
* @param ev
* @return
*/
@Override
public
boolean
dispatchTouchEvent(MotionEvent ev) {
switch
(ev.getAction()) {
case
MotionEvent.ACTION_DOWN:
outter.requestDisallowInterceptTouchEvent(
true
);
break
;
case
MotionEvent.ACTION_MOVE:
if
( isTop() && outter.getScrollY() <= outter.mInitScrollY) {
outter.requestDisallowInterceptTouchEvent(
false
);
}
break
;
}
return
super
.dispatchTouchEvent(ev);
}
public
boolean
isTop() {
return
getFirstVisiblePosition() ==
0
;
}
}
}
|