我有父视图v扩展线性布局。孩子一个是 LinearLayout ,孩子二是 ScrollView 。
我想让MyParent View截取垂直 MotionEvent.ACTION_MOVE (仅向下方向,子scrollview.getScrollY()==0
)并在父级处理它,其他 MotionEvent 是由儿童处理
这是MyView.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
<LinearLayout
android:id="@+id/child_linear>
android:height="50dp"
android:width="50dp">
...something...
</LinearLayout>
<ScrollView
android:id="@+id/child_scrollview>
<LinearLayout/>
</LinearLayout>
</ScrollView>
</merge>
这是我的代码
public class MyCustomView extends LinearLayout{
public MyCustomView(Context context) {
super(context);
init();
}
private void init(){
setOrientation(LinearLayout.VERTICAL);
LayoutInflater inflater =
(LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.MyView, this, true);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
final int action = event.getAction();
Log.d(log,"onInterceptTouchEvent : "+action);
if (action == MotionEvent.ACTION_CANCEL || action ==
MotionEvent.ACTION_UP) {
mIsBeingDragged = false;
return false;
}
if (action != MotionEvent.ACTION_DOWN && mIsBeingDragged) {
return true;
}
switch (action) {
case MotionEvent.ACTION_MOVE: {
if (isReadyForPull()) {
final float y = event.getY(), x =
event.getX();
final float diff, oppositeDiff, absDiff;
diff = y - mLastMotionY;
oppositeDiff = x - mLastMotionX;
absDiff = Math.abs(diff);
ViewConfiguration config =
ViewConfiguration.get(getContext());
if (absDiff > config.getScaledTouchSlop() &&
absDiff > Math.abs(oppositeDiff) && diff >= 1f) {
mLastMotionY = y;
mLastMotionX = x;
mIsBeingDragged = true;
Log.d(log,"Flag setting");
}
}
break;
}
case MotionEvent.ACTION_DOWN: {
if (isReadyForPull()) {
mLastMotionY = mInitialMotionY = event.getY();
mLastMotionX = event.getX();
mIsBeingDragged = false;
}
break;
}
}
return mIsBeingDragged;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
final int action=event.getAction();
Log.d(log,"onTouchEvent : "+action);
if (event.getAction() == MotionEvent.ACTION_DOWN && event.getEdgeFlags() != 0) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE: {
Log.d(log,"ACTION MOVE RECEIVE");
if (mIsBeingDragged) {
mLastMotionY = event.getY();
mLastMotionX = event.getX();
pullEvent();
return true;
}
break;
}
case MotionEvent.ACTION_DOWN: {
if (isReadyForPull()) {
mLastMotionY = mInitialMotionY = event.getY();
return true;
}
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
if (mIsBeingDragged) {
mIsBeingDragged = false;
if (mState == State.RELEASE_TO_REFRESH ) {
setState(State.REFRESHING);
return true;
}
if (isRefreshing()) {
smoothScrollTo(-mHeaderHeight , null);
return true;
}
setState(State.RESET);
return true;
}
break;
}
}
return false;
}
isReadyForPull()
功能仅检查此
private boolean isReadyForPull(){
return mScrollView.getScrollY()==0;
}
我的代码效果很好。
如果我向下移动子视图滚动视图,onInterceptTouchEvent
首先获取 MotionEvent.ACTION_DOWN 并初始化值,然后按顺序 MotionEvent.ACTION_MOVE 设置 mIsBeingDragged 标记为 true 并返回 true 。所以我可以在 ParentView 的onTouchEvent
。
否则,如果我向上移动子滚动视图,则onInterceptTouchEvent
返回false并且我的子滚动视图获取 MotionEvent 并向下滚动。
这是预期的工作。好。
但是,当我触摸我的孩子线性布局时,它不起作用!
如果我触摸并拖动我的孩子linearlayout,我的CustomView的onInterceptTouchEvent
获取 MotionEvent.ACTION_DOWN ,但无法获得第二个 MotionEvent.ACTION_MOVE 。
为什么这不仅适用于儿童linearlayout?
注意: 我测试过几次并且知道(默认)可触摸视图或视图组(如按钮,滚动视图等)可以使用我的代码,而(默认)不可触摸的视图或小部件(如imageview,framelayout)不能与我的代码一起使用。
答案 0 :(得分:6)
该解决方案与this答案有关,因为它是标准MotionEvent
处理的结果。
在我的代码中,当我触摸孩子LinearLayout
时,父CustomViewGroup
不拦截MotionEvent
,因为它返回false,但我的孩子LinearLayout
没有t也会消耗MotionEvent
,因此MotionEvent
会返回到父onTouchEvent
,而不是onInterceptTouchEvent
。
另一方面,当我触摸我的孩子ScrollView
时,无论是启用还是禁用滚动,它都会消耗MotionEvent
。
==&gt; 我认为,因为Android在原始版本消费或完成之前不再生成MotionEvent
,因此父CustomViewGroup
不会通过ACTION_MOVE
获取MotionEvent
onInterceptTouchEvent
,当孩子不使用onTouchEvent
时,将其设置为MotionEvent
。
我找到了两个解决方案
强行让LinearLayout
消费MotionEvent
。只有当孩子LinearLayout
没有可触摸的View
,ViewGroup
或Widget
时,此解决方案才可用。像这样:
LinearLayout mLinearLayout = (LinearLayout)findViewById(R.id.child_linear);
mLinearLayout.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
处理MotionEvent
CustomViewGroup
中孩子返回的onTouchEvent
。像这样:(如果onTouchEvent
中的情况,只需添加其他内容。)
case MotionEvent.ACTION_MOVE: {
if (mIsBeingDragged) {
mLastMotionY = event.getY();
mLastMotionX = event.getX();
pullEvent();
return true;
}
else if (isReadyForPull()) {
final float y = event.getY(), x = event.getX();
final float diff, oppositeDiff, absDiff;
diff = y - mLastMotionY;
oppositeDiff = x - mLastMotionX;
absDiff = Math.abs(diff);
ViewConfiguration config = ViewConfiguration.get(getContext());
if (absDiff > config.getScaledTouchSlop() && absDiff >
Math.abs(oppositeDiff) && diff >= 1f) {
mLastMotionY = y;
mLastMotionX = x;
mIsBeingDragged = true;
}
}
break;
}
虽然解决方案1是某些情况下的快速解决方案,但解决方案2是最灵活和最可靠的。
答案 1 :(得分:0)
有很好的答案
onInterceptTouchEvent only gets ACTION_DOWN
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
MyLog.d(MyLog.DEBUG, "dispatchTouchEvent(): "+event.getAction());
if (isEnabled()) {
MyLog.d(MyLog.DEBUG, "dispatchTouchEvent()2: "+event.getAction());
processEvent(event);//here you get all events include move & up
super.dispatchTouchEvent(event);
return true; //to keep receive event that follow down event
}
return super.dispatchTouchEvent(event);
}