2013-07-24 2 views
3

У меня есть пользовательский вид, где у меня есть функциональность с событиями касания (проведите пальцем и т. Д.). Теперь может случиться, что это пользовательское представление используется в пределах ScrollableLayout. Проблема заключается в том, что когда пользователь просматривает мое пользовательское представление, родительский (ScrollableLayout) также будет обрабатывать жестов салфетки, и поэтому он прокручивается, но он не должен.Как предотвратить TouchEvent от прокрутки

Мне нужно что-то вроде event.preventDefaults() с JavaScript.

Я переопределяю View#onTouchEvent и всегда возвращаю true. Я думал, что когда я вернусь с onTouchEvent, это означает, что событие уничтожено, и никакое другое представление не получит onTouchEvent, но это неправильно.

Может кто-нибудь мне помочь?

мой взгляд, очень легко проверить:

public class PreventingTouchEventView extends View { 

    public PreventingTouchEventView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    @Override 
    protected void onDraw(Canvas canvas) { 
     canvas.drawColor(Color.RED); 
    } 

    @Override 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
     setMeasuredDimension(200, 200); 
    } 

    @Override 
    public boolean onTouchEvent(MotionEvent event) { 
     return true; 
    } 
} 

Я нажал андроид проект примера для GitHub: https://github.com/jjoe64/android-preventing-touch-test

Если нажать и прокручивать внутри красного холста, ScrollableLayout должен не свиток.

ответ

12

Чтобы предотвратить штрихи с этого макета у вас есть несколько вариантов:

1) набор OnTouchListener, который всегда возвращает истину.

2) переопределение dispatchTouchEvent (MotionEvent событие) всегда возвращает истину

UPD: Окей, как правило, вы должны установить OnTouchListener, который всегда возвращает истину, как я уже сказал, чтобы другие виды не onTouch, но с ScrollView это совсем другая история.

Прежде всего я расскажу, как работает dispatchTouchEvent. Он начинает отправку события из корневого представления в его дочерние представления, поэтому это означает, что dispatchTouchEvent родителя называется ПЕРЕДdispatchTouchEvent его детей.

Тогда в ViewGroup.dispatchTouchEvent() есть кусок кода

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; 
} 

, который решает, если это прикосновение событие должно быть перехвачено, что означает, если intercepted флага верно, то эта точка зрения будет перехватывать все последующие события. И в ScrollViewonInterceptTouchEvent реализованы для перехвата действий, если они выполняют вертикальную прокрутку.

Так что моя первая идея состояла в том, чтобы установить FLAG_DISALLOW_INTERCEPT к родителю вашего пользовательского представления, чтобы запретить эту возможность перехвата событий, но эта идея не удается из следующих строк кода в ViewGroup:

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(); 
} 

Так что означает, что после MotionEvent.ACTION_DOWN все состояния очищаются (и так же FLAG_DISALLOW_INTERCEPT также очищается).

Так что окончательное решение довольно простое

public class PreventingTouchEventView extends View { 

    public PreventingTouchEventView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    @Override 
    protected void onDraw(Canvas canvas) { 
     canvas.drawColor(Color.RED); 
    } 

    @Override 
    public boolean dispatchTouchEvent(MotionEvent event) { 
     if (getParent() != null && event.getAction() == MotionEvent.ACTION_DOWN) { 
      getParent().requestDisallowInterceptTouchEvent(true); 
     } 
     return super.dispatchTouchEvent(event); 
    } 
} 

это подняло бы FLAG_DISALLOW_INTERCEPT сразу после первого MotionEvent.ACTION_DOWN, который будет Запрещать дальнейший перехват будущих действий родителей.

Но обратите внимание, что если ваш родительский взгляд делает некоторые сумасшедшие вещи на MotionEvent.ACTION_DOWN и перехватывать это событие, то вы просто не можете ничего об этом, делать, потому что ваш родителя dispatchTouchEvent вызываются до вашего dispatchTouchEvent.

+0

такой же здесь. Эти изменения относятся к родительскому представлению. Я могу изменить свой собственный пользовательский вид ... – appsthatmatter

+0

Даже если бы я мог внести изменения в родительский элемент, это полностью отключит прокрутку. Он не должен прокручиваться, когда я коснусь своего пользовательского представления. – appsthatmatter

+0

Почему вы говорите, что эти изменения относятся к родительскому виду? Вы устанавливаете 'OnTouchListener' только для своего пользовательского представления, и, таким образом, если вы касаетесь внешнего вида - другое представление должно обрабатывать это событие, в вашем случае это' ScrollableLayour' (родительский элемент вашего пользовательского представления). – Desert

1

вы должны переопределить метод onInterceptTouchEvent родителя

+0

Так что ** невозможно ** отменить все касание событие. У меня есть возможность делать изменения в своем пользовательском представлении. Родительское представление зависит от разработчиков, которые используют мой пользовательский вид. Какой позор – appsthatmatter

Смежные вопросы