android滑动冲突的解决方案
来源:互联网 发布:淘宝买东西店铺下架了 编辑:程序博客网 时间:2024/06/07 03:00
Android 中解决滑动的方案有2种:外部拦截法 和内部拦截法。
滑动冲突也存在2种场景: 横竖滑动冲突、同向滑动冲突。
所以我就写了4个例子来学习如何解决滑动冲突的,这四个例子分别为: 外部拦截法解决横竖冲突、外部拦截法解决同向冲突、内部拦截法解决横竖冲突、内部拦截法解决同向冲突。
先上效果图:
二、实战
1、外部拦截法,解决横竖冲突
思路是,重写父控件的onInterceptTouchEvent方法,然后根据具体的需求,来决定父控件是否拦截事件。如果拦截返回返回true,不拦截返回false。关于为什么返回true就代表拦截事件 。 如果父控件拦截了事件,则在父控件的onTouchEvent进行相应的事件处理。
我的这个例子,是一个横向滑动的ViewGroup里面包含了3个竖向滑动的ListView。下面我附上代码:
HorizontalEx.java
* Created by blueberry on 2016/6/20.
*
* 解决交错的滑动冲突
*
* 外部拦截法
*/
public
class
HorizontalEx
extends
ViewGroup {
private
static
final
String TAG =
"HorizontalEx"
;
private
boolean
isFirstTouch =
true
;
private
int
childIndex;
private
int
childCount;
private
int
lastXIntercept, lastYIntercept, lastX, lastY;
private
Scroller mScroller;
private
VelocityTracker mVelocityTracker;
public
HorizontalEx(Context context) {
super
(context);
init();
}
public
HorizontalEx(Context context, AttributeSet attrs) {
super
(context, attrs);
init();
}
public
HorizontalEx(Context context, AttributeSet attrs,
int
defStyleAttr) {
super
(context, attrs, defStyleAttr);
init();
}
private
void
init() {
mScroller =
new
Scroller(getContext());
mVelocityTracker = VelocityTracker.obtain();
}
@Override
protected
void
onMeasure(
int
widthMeasureSpec,
int
heightMeasureSpec) {
int
width = MeasureSpec.getSize(widthMeasureSpec);
int
height = MeasureSpec.getSize(heightMeasureSpec);
int
widthMode = MeasureSpec.getMode(widthMeasureSpec);
int
heightMode = MeasureSpec.getMode(heightMeasureSpec);
childCount = getChildCount();
measureChildren(widthMeasureSpec, heightMeasureSpec);
if
(childCount ==
0
) {
setMeasuredDimension(
0
,
0
);
}
else
if
(widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
width = childCount * getChildAt(
0
).getMeasuredWidth();
height = getChildAt(
0
).getMeasuredHeight();
setMeasuredDimension(width, height);
}
else
if
(widthMode == MeasureSpec.AT_MOST) {
width = childCount * getChildAt(
0
).getMeasuredWidth();
setMeasuredDimension(width, height);
}
else
{
height = getChildAt(
0
).getMeasuredHeight();
setMeasuredDimension(width, height);
}
}
@Override
protected
void
onLayout(
boolean
changed,
int
l,
int
t,
int
r,
int
b) {
int
left =
0
;
for
(
int
i =
0
; i < getChildCount(); i++) {
final
View child = getChildAt(i);
child.layout(left + l, t, r + left, b);
left += child.getMeasuredWidth();
}
}
/**
* 拦截事件
* @param ev
* @return
*/
@Override
public
boolean
onInterceptTouchEvent(MotionEvent ev) {
boolean
intercepted =
false
;
int
x = (
int
) ev.getX();
int
y = (
int
) ev.getY();
switch
(ev.getAction()) {
/*如果拦截了Down事件,则子类不会拿到这个事件序列*/
case
MotionEvent.ACTION_DOWN:
lastXIntercept = x;
lastYIntercept = y;
intercepted =
false
;
if
(!mScroller.isFinished()) {
mScroller.abortAnimation();
intercepted =
true
;
}
break
;
case
MotionEvent.ACTION_MOVE:
final
int
deltaX = x - lastXIntercept;
final
int
deltaY = y - lastYIntercept;
/*根据条件判断是否拦截该事件*/
if
(Math.abs(deltaX) > Math.abs(deltaY)) {
intercepted =
true
;
}
else
{
intercepted =
false
;
}
break
;
case
MotionEvent.ACTION_UP:
intercepted =
false
;
break
;
}
lastXIntercept = x;
lastYIntercept = y;
return
intercepted;
}
@Override
public
boolean
onTouchEvent(MotionEvent event) {
int
x = (
int
) event.getX();
int
y = (
int
) event.getY();
mVelocityTracker.addMovement(event);
ViewConfiguration configuration = ViewConfiguration.get(getContext());
switch
(event.getAction()) {
case
MotionEvent.ACTION_DOWN:
if
(!mScroller.isFinished()) {
mScroller.abortAnimation();
}
break
;
case
MotionEvent.ACTION_MOVE:
/*因为这里父控件拿不到Down事件,所以使用一个布尔值,
当事件第一次来到父控件时,对lastX,lastY赋值*/
if
(isFirstTouch) {
lastX = x;
lastY = y;
isFirstTouch =
false
;
}
final
int
deltaX = x - lastX;
scrollBy(-deltaX,
0
);
break
;
case
MotionEvent.ACTION_UP:
int
scrollX = getScrollX();
final
int
childWidth = getChildAt(
0
).getWidth();
mVelocityTracker.computeCurrentVelocity(
1000
, configuration.getScaledMaximumFlingVelocity());
float
xVelocity = mVelocityTracker.getXVelocity();
if
(Math.abs(xVelocity) > configuration.getScaledMinimumFlingVelocity()) {
childIndex = xVelocity <
0
? childIndex +
1
: childIndex -
1
;
}
else
{
childIndex = (scrollX + childWidth /
2
) / childWidth;
}
childIndex = Math.min(getChildCount() -
1
, Math.max(childIndex,
0
));
smoothScrollBy(childIndex * childWidth - scrollX,
0
);
mVelocityTracker.clear();
isFirstTouch =
true
;
break
;
}
lastX = x;
lastY = y;
return
true
;
}
void
smoothScrollBy(
int
dx,
int
dy) {
mScroller.startScroll(getScrollX(), getScrollY(), dx, dy,
500
);
invalidate();
}
@Override
public
void
computeScroll() {
if
(mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
invalidate();
}
}
@Override
protected
void
onDetachedFromWindow() {
super
.onDetachedFromWindow();
mVelocityTracker.recycle();
}
}
调用代码:
<code
class
=
"hljs lasso"
>
@Override
public
void
showOutHVData(List<string> data1, List<string> data2, List<string> data3) {
ListView listView1 =
new
ListView(getContext());
ArrayAdapter<string> adapter1 =
new
ArrayAdapter<string>(getContext(), android.R.layout.simple_list_item_1, data1);
listView1.setAdapter(adapter1);
ListView listView2 =
new
ListView(getContext());
ArrayAdapter<string> adapter2 =
new
ArrayAdapter<string>(getContext(), android.R.layout.simple_list_item_1, data2);
listView2.setAdapter(adapter2);
ListView listView3 =
new
ListView(getContext());
ArrayAdapter<string> adapter3 =
new
ArrayAdapter<string>(getContext(), android.R.layout.simple_list_item_1, data3);
listView3.setAdapter(adapter3);
ViewGroup.LayoutParams params
=
new
ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
mHorizontalEx.addView(listView1, params);
mHorizontalEx.addView(listView2, params);
mHorizontalEx.addView(listView3, params);
}
其实外部拦截的主要思想都在于对onInterceptTouchEvent的重写。
<code
class
=
"hljs java"
>
@Override
public
boolean
onInterceptTouchEvent(MotionEvent ev) {
boolean
intercepted =
false
;
int
x = (
int
) ev.getX();
int
y = (
int
) ev.getY();
switch
(ev.getAction()) {
/*如果拦截了Down事件,则子类不会拿到这个事件序列*/
case
MotionEvent.ACTION_DOWN:
lastXIntercept = x;
lastYIntercept = y;
intercepted =
false
;
if
(!mScroller.isFinished()) {
mScroller.abortAnimation();
intercepted =
true
;
}
break
;
case
MotionEvent.ACTION_MOVE:
final
int
deltaX = x - lastXIntercept;
final
int
deltaY = y - lastYIntercept;
/*根据条件判断是否拦截该事件*/
if
(Math.abs(deltaX) > Math.abs(deltaY)) {
intercepted =
true
;
}
else
{
intercepted =
false
;
}
break
;
case
MotionEvent.ACTION_UP:
intercepted =
false
;
break
;
}
lastXIntercept = x;
lastYIntercept = y;
return
intercepted;
}</code>
这几乎是一个实现外部拦截事件的模板,这里一定不要在ACTION_DOWN 中返回 true,否则会让子VIew没有机会得到事件,因为如果在ACTION_DOWN的时候返回了 true,同一个事件序列ViewGroup的disPatchTouchEvent就不会在调用onInterceptTouchEvent方法了,如果不明白可以参考我的上一遍文章。
还有就是 在ACTION_UP中返回false,因为如果父控件拦截了ACTION_UP,那么子View将得不到UP事件,那么将会影响子View的 Onclick方法等。但这对父控件是没有影响的,因为如果是父控件子ACITON_MOVE中 就拦截了事件,他们UP事件必定也会交给它处理,因为有那么一条定律叫做:父控件一但拦截了事件,那么同一个事件序列的所有事件都将交给他处理。这条结论在我的上一篇文章中已经分析过。
最后就是在 ACTION_MOVE中根据需求决定是否拦截。
2、内部拦截法,解决横竖冲突
内部拦截主要依赖于父控件的 requestDisallowInterceptTouchEvent方法,关于这个方法我的上篇文章其实已经分析过。他设置父控件的一个标志(FLAG_DISALLOW_INTERCEPT)
这个标志可以决定父控件是否拦截事件,如果设置了这个标志则不拦截,如果没设这个标志,它就会调用父控件的onInterceptTouchEvent()来询问父控件是否拦截。但这个标志对Down事件无效。
可以参考一下源码:
ViewGroup#dispatchTouchEvent
<code
class
=
"hljs coffeescript"
>
// Handle an initial down.
if
(actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
cancelAndClearTouchTargets(ev);
//清楚标志
resetTouchState();
}
// Check for interception.
final
boolean
intercepted;
if
(actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget !=
null
) {
//标志
final
boolean
disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) !=
0
;
if
(!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
// restore action in case it was changed
}
else
{
intercepted =
false
;
}
}
else
{
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted =
true
;
}
</code>
那么我们如果想使用 内部拦截法拦截事件。
第一步:
a、我们要重写父控件的onInterceptTouchEvent,在ACTION_DOWN的时候返回false,负责的话子View调用requestDisallowInterceptTouchEvent也将无能为力。
b、还有就是其他事件的话都返回true,这样就把能否拦截事件的权利交给了子View。
第二步:
在子View的dispatchTouchEvent中 来决定是否让父控件拦截事件。
a. 先要在MotionEvent.ACTION_DOWN:的时候使用mHorizontalEx2.requestDisallowInterceptTouchEvent(true);,负责的话,下一个事件到来时,就交给父控件了。
b. 然后在MotionEvent.ACTION_MOVE: 根据业务逻辑决定是否调用mHorizontalEx2.requestDisallowInterceptTouchEvent(false);来决定父控件是否拦截事件。
上代码:
HorizontalEx2.java
/**
* Created by blueberry on 2016/6/20.
* <p/>
* 内部拦截
* 和 ListViewEx配合使用
*/
public
class
HorizontalEx2
extends
ViewGroup {
private
int
lastX, lastY;
private
int
childIndex;
private
Scroller mScroller;
private
VelocityTracker mVelocityTracker;
public
HorizontalEx2(Context context) {
super
(context);
init();
}
public
HorizontalEx2(Context context, AttributeSet attrs) {
super
(context, attrs);
init();
}
public
HorizontalEx2(Context context, AttributeSet attrs,
int
defStyleAttr) {
super
(context, attrs, defStyleAttr);
init();
}
private
void
init() {
mScroller =
new
Scroller(getContext());
mVelocityTracker = VelocityTracker.obtain();
}
@Override
protected
void
onMeasure(
int
widthMeasureSpec,
int
heightMeasureSpec) {
int
width = MeasureSpec.getSize(widthMeasureSpec);
int
widthMode = MeasureSpec.getMode(widthMeasureSpec);
int
height = MeasureSpec.getSize(heightMeasureSpec);
int
heightMode = MeasureSpec.getMode(heightMeasureSpec);
int
childCount = getChildCount();
measureChildren(widthMeasureSpec, heightMeasureSpec);
if
(childCount ==
0
) {
setMeasuredDimension(
0
,
0
);
}
else
if
(widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
height = getChildAt(
0
).getMeasuredHeight();
width = childCount * getChildAt(
0
).getMeasuredWidth();
setMeasuredDimension(width, height);
}
else
if
(widthMode == MeasureSpec.AT_MOST) {
width = childCount * getChildAt(
0
).getMeasuredWidth();
setMeasuredDimension(width, height);
}
else
{
height = getChildAt(
0
).getMeasuredHeight();
setMeasuredDimension(width, height);
}
}
@Override
protected
void
onLayout(
boolean
changed,
int
l,
int
t,
int
r,
int
b) {
int
leftOffset =
0
;
for
(
int
i =
0
; i < getChildCount(); i++) {
View child = getChildAt(i);
child.layout(l + leftOffset, t, r + leftOffset, b);
leftOffset += child.getMeasuredWidth();
}
}
/**
* 不拦截Down事件,其他一律拦截
* @param ev
* @return
*/
@Override
public
boolean
onInterceptTouchEvent(MotionEvent ev) {
if
(ev.getAction() == MotionEvent.ACTION_DOWN) {
if
(!mScroller.isFinished()) {
mScroller.abortAnimation();
return
true
;
}
return
false
;
}
else
{
return
true
;
}
}
private
boolean
isFirstTouch =
true
;
@Override
public
boolean
onTouchEvent(MotionEvent event) {
int
x = (
int
) event.getX();
int
y = (
int
) event.getY();
mVelocityTracker.addMovement(event);
ViewConfiguration configuration = ViewConfiguration.get(getContext());
switch
(event.getAction()) {
case
MotionEvent.ACTION_DOWN:
if
(!mScroller.isFinished()) {
mScroller.abortAnimation();
}
break
;
case
MotionEvent.ACTION_MOVE:
if
(isFirstTouch) {
isFirstTouch =
false
;
lastY = y;
lastX = x;
}
final
int
deltaX = x - lastX;
scrollBy(-deltaX,
0
);
break
;
case
MotionEvent.ACTION_UP:
isFirstTouch =
true
;
int
scrollX = getScrollX();
mVelocityTracker.computeCurrentVelocity(
1000
, configuration.getScaledMaximumFlingVelocity());
float
mVelocityX = mVelocityTracker.getXVelocity();
if
(Math.abs(mVelocityX) > configuration.getScaledMinimumFlingVelocity()) {
childIndex = mVelocityX <
0
? childIndex +
1
: childIndex -
1
;
}
else
{
childIndex = (scrollX + getChildAt(
0
).getWidth() /
2
) / getChildAt(
0
).getWidth();
}
childIndex = Math.min(getChildCount() -
1
, Math.max(
0
, childIndex));
smoothScrollBy(childIndex*getChildAt(
0
).getWidth()-scrollX,
0
);
mVelocityTracker.clear();
break
;
}
lastX = x;
lastY = y;
return
true
;
}
private
void
smoothScrollBy(
int
dx,
int
dy) {
mScroller.startScroll(getScrollX(), getScrollY(), dx, dy,
500
);
invalidate();
}
@Override
public
void
computeScroll() {
if
(mScroller.computeScrollOffset()){
scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
postInvalidate();
}
}
@Override
protected
void
onDetachedFromWindow() {
super
.onDetachedFromWindow();
mVelocityTracker.recycle();
}
}
ListViewEx.java
<code
class
=
"hljs java"
>
/**
* Created by blueberry on 2016/6/20.
* 内部拦截事件
*/
public
class
ListViewEx
extends
ListView {
private
int
lastXIntercepted, lastYIntercepted;
private
HorizontalEx2 mHorizontalEx2;
public
ListViewEx(Context context) {
super
(context);
}
public
ListViewEx(Context context, AttributeSet attrs) {
super
(context, attrs);
}
public
ListViewEx(Context context, AttributeSet attrs,
int
defStyleAttr) {
super
(context, attrs, defStyleAttr);
}
public
HorizontalEx2 getmHorizontalEx2() {
return
mHorizontalEx2;
}
public
void
setmHorizontalEx2(HorizontalEx2 mHorizontalEx2) {
this
.mHorizontalEx2 = mHorizontalEx2;
}
/**
* 使用 outter.requestDisallowInterceptTouchEvent();
* 来决定父控件是否对事件进行拦截
* @param ev
* @return
*/
@Override
public
boolean
dispatchTouchEvent(MotionEvent ev) {
int
x = (
int
) ev.getX();
int
y = (
int
) ev.getY();
switch
(ev.getAction()) {
case
MotionEvent.ACTION_DOWN:
mHorizontalEx2.requestDisallowInterceptTouchEvent(
true
);
break
;
case
MotionEvent.ACTION_MOVE:
final
int
deltaX = x-lastYIntercepted;
final
int
deltaY = y-lastYIntercepted;
if
(Math.abs(deltaX)>Math.abs(deltaY)){
mHorizontalEx2.requestDisallowInterceptTouchEvent(
false
);
}
break
;
case
MotionEvent.ACTION_UP:
break
;
}
lastXIntercepted = x;
lastYIntercepted = y;
return
super
.dispatchTouchEvent(ev);
}
}
</code>
调用代码:
<code
class
=
"hljs lasso"
>
@Override
public
void
showInnerHVData(List<string> data1, List<string> data2, List<string> data3) {
ListViewEx listView1 =
new
ListViewEx(getContext());
ArrayAdapter<string> adapter1 =
new
ArrayAdapter<string>(getContext(), android.R.layout.simple_list_item_1, data1);
listView1.setAdapter(adapter1);
listView1.setmHorizontalEx2(mHorizontalEx2);
ListViewEx listView2 =
new
ListViewEx(getContext());
ArrayAdapter<string> adapter2 =
new
ArrayAdapter<string>(getContext(), android.R.layout.simple_list_item_1, data2);
listView2.setAdapter(adapter2);
listView2.setmHorizontalEx2(mHorizontalEx2);
ListViewEx listView3 =
new
ListViewEx(getContext());
ArrayAdapter<string> adapter3 =
new
ArrayAdapter<string>(getContext(), android.R.layout.simple_list_item_1, data3);
listView3.setAdapter(adapter3);
listView3.setmHorizontalEx2(mHorizontalEx2);
ViewGroup.LayoutParams params
=
new
ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
mHorizontalEx2.addView(listView1, params);
mHorizontalEx2.addView(listView2, params);
mHorizontalEx2.addView(listView3, params);
}
- android滑动冲突的解决方案
- android滑动冲突的解决方案
- Android-滑动冲突解决方案
- Android滑动冲突解决方案
- android滑动冲突解决方案
- Android滑动冲突解决方案
- Android中滑动冲突的解决方案
- android View滑动冲突的终极解决方案
- 滑动冲突的解决方案
- Android双向滑动冲突解决方案
- View的滑动冲突解决方案
- view滑动冲突的解决方案
- 关于滑动冲突的解决方案
- Android 开发中scrollview嵌套webview滑动冲突的解决方案
- Android scrollview嵌套webview滑动冲突的解决方案
- Android View的事件分发机制和滑动冲突解决方案
- Android Viewpager与WebView轮播滑动冲突的解决方案
- Android 开发中scrollview嵌套webview滑动冲突的解决方案
- R语言分图散点图
- 欢迎使用CSDN-markdown编辑器
- 现代软件工程-构建之法(第四,五章)
- c++类型转换dynamic_cast
- 三层Datasnap 服务端验证以及心跳包技术
- android滑动冲突的解决方案
- ffmpeg的内存的分配和释放av_malloc()、av_free()的函数
- 如何使用Anhui Online Judge添加题目
- 是的
- ByteArrayOutputStream用法
- u3d 递归输出子孙物体相对总父物体的路径
- R语言数据处理之缺失数据问题(一)
- Java基础教程13-do-while循环
- 注册页面校验