Одновременная обработка сенсорных событий в нескольких представлениях в Android

Учитывая пример на изображении ниже,

введите описание изображения здесь

Я ищу решение для передачи событий касания с холста View на Viewpager. Это необходимо для одновременного применения одинакового масштаба к обоим представлениям.

Вот техническое описание того, что происходит,

(Давайте рассмотрим представление как A, B и C)

А. Родительский вид

Б. Посмотреть

С. Просмотр пейджера

Перед вызовом dispatchTouchEvent() для дочерних элементов (B и C) A.dispatchTouchEvent() сначала вызовет A.onInterceptTouchEvent(), чтобы узнать, заинтересована ли группа просмотра в перехвате события.

Таким образом, если A.onTouchEvent() возвращает false, происходит возврат к B.onTouchEvent(). Если он возвращает false, он возвращается к C.onTouchEvent(), и диспетчеризация продолжается как обычно.

И после возврата true

  1. ACTION_CANCEL будет отправлено всем дочерним элементам.

  2. Все последующие события жестов (до ACTION_UP/ACTION_CANCEL) будут использоваться прослушивателями событий (OnTouchListener.onTouch()), если они определены, иначе обработчик события A.onTouchEvent() на уровне A.

На данный момент я могу передать события от родительского к дочерним представлениям, но не могу позволить 2 дочерним представлениям B. & C обрабатывают события вместе (применяя одинаковое масштабирование к обоим представлениям).

Есть ли способ передать события касания из родительского представления в дочернее, чтобы они могли обрабатывать события одновременно?

Вот моя настройка макета,

<RelativeLayout
    android:id="@+id/layoutParent"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFFFFFFF">

            <com.androidapp.NonSwipeableViewPager
                android:id="@+id/pager"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="center"
                android:background="@color/transparent" />

            <com.androidapp.DrawingView
                android:id="@+id/drawing"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@color/transparent" />

</RelativeLayout>

person Falling Into Infinity    schedule 06.12.2016    source источник
comment
Как холст расположен в элементе управления viewpager? Он появляется как всплывающее окно или как-то иначе?   -  person ridoy    schedule 11.12.2016
comment
Холст и просмотрщик находятся внутри двух отдельных представлений. Проверьте мой код макета для более подробного ознакомления. @ридой   -  person Falling Into Infinity    schedule 13.12.2016
comment
Итак, ваш DrawingView перехватывает NonSwipeableViewPager (согласно вашему коду макета), не так ли?   -  person ridoy    schedule 13.12.2016
comment
Да! @ридой   -  person Falling Into Infinity    schedule 14.12.2016


Ответы (2)


Если я не ошибаюсь, вы хотите обрабатывать одно и то же событие касания как сверху, так и снизу. В соответствии с сенсорной архитектурой Android вы не можете обрабатывать сенсорные события из нескольких представлений одновременно. Либо вы должны обработать его внутри метода onTouchEvent(), либо перейти к следующему представлению. Вам также нужно дождаться, пока вид сверху завершит обработку MotionEvent() и позволить дочернему виду обработать.

Вот возможное решение с использованием подхода RxAndroid:

Внутри вашего представления холста MotionEvent(),

@Override
    public boolean onTouchEvent(MotionEvent event) {

    // process all touch events (UP/DOWN/MOVE)

    //send events as observable
    RxBus.getInstance().sendMotionEvent(event);

    return true;
}

Это позволит наблюдать за событиями движения и уведомлять всех наблюдателей.

Теперь внутри onResume() вашего пейджера подпишитесь на события движения, чтобы при каждом изменении на холсте события немедленно передавались.

Subscription RxBus _rxBus = RxBus.getInstance();
        subscription = _rxBus.getMotionEvent()
                .subscribe(new Action1<MotionEvent>() {
                    @Override
                    public void call(MotionEvent event) {
                       // call the onTouchEvent of your widget e.g. ImageView and pass the received event
                       // this will apply the same touch event that was applied in the canvas 

                        // .onTouchEvent(event) is important to override the onTouchEvent() of your widget that will use the touch event
                        imageView.onTouchEvent(event);
                    }
            });

Класс RxBus приведен ниже,

public class RxBus {

    private static RxBus instance;

    private final PublishSubject<MotionEvent> motion_event = PublishSubject.create();

    public static RxBus getInstance() {
        if (instance == null) {
            instance = new RxBus();
        }
        return instance;
    }

    public void sendMotionEvent(MotionEvent motionEvent) {
        motion_event.onNext(motion_event);
    }

    public Observable<MotionEvent> getMotionEvent() {
        return motion_event;
    }
}
person Prokash Sarkar    schedule 13.12.2016
comment
как насчет того, чтобы доставить прикосновение к более чем одному представлению? - person Luiz Alegria; 01.04.2020
comment
В функции sendMotionEvent ошибка, т.е. должно быть: motion_event.onNext(motionEvent), а не motion_event.onNext(motion_event) - person Sajid2405; 26.01.2021

Это довольно легко реализовать с помощью обратного вызова. Просто создайте интерфейс с любыми методами, которые вам нужны, а затем реализуйте их в своем Viewpager. Вызов будет вызываться в вашем классе View на основе ваших сенсорных событий, и ответы будут поступать на Viewpager. Я создал пример с ImageView в своей MainActivity и призвал изменить его размер из другого фрагмента, и он работает. Вот мой код ниже:

public interface ICallBackForResize {
   void resizeme();
   void actionUp();
   void actionDown();

 }

Основная деятельность:

public class MainActivity extends AppCompatActivity implements ICallBackForResize {

ImageView img;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    img= (ImageView) findViewById(R.id.image);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}

@Override
public void resizeme() {
    Toast.makeText(MainActivity.this,"called",Toast.LENGTH_LONG).show();
    img.getLayoutParams().height += 20;
    img.getLayoutParams().width += 20;
    img.requestLayout();
}

@Override
public void actionUp() {
    //do whatever
}

@Override
public void actionDown() {
    //do whatever

}
 }

Мой класс Fragment с простой кнопкой:

public class MyFragmentButton extends Fragment {
View view;
ICallBackForResize callBackForResize;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    callBackForResize= (ICallBackForResize) getActivity();
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    view=inflater.inflate(R.layout.mybutton,container,false);
    Button btn= (Button) view.findViewById(R.id.button);
    btn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            callBackForResize.resizeme();
        }
    });
    return view;
}
}

Я считаю, что вам не нужен файл xml. Я просто поделился, если кому-то нужно:

Activity_main:

<?xml version="1.0" encoding="utf-8"?>

<android.support.design.widget.AppBarLayout
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:theme="@style/AppTheme.AppBarOverlay">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        app:popupTheme="@style/AppTheme.PopupOverlay" />

</android.support.design.widget.AppBarLayout>

<include layout="@layout/content_main"/>


 </android.support.design.widget.CoordinatorLayout>

content_main:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:showIn="@layout/activity_main"
tools:context="nanofaroque.com.addlistener.MainActivity">

<ImageView
    android:id="@+id/image"
    android:text="Hello World!"
    android:layout_width="100dp"
    android:layout_gravity="center"
    android:src="@mipmap/ic_launcher"
    android:layout_height="100dp" />


<fragment
    android:name="nanofaroque.com.addlistener.MyFragmentButton"
    android:id="@+id/headlines_fragment"
    android:layout_width="100dp"
    android:layout_height="100dp" />

 </FrameLayout>

моя кнопка:

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
    android:id="@+id/button"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

 </LinearLayout>
person Omar Faroque Anik    schedule 08.12.2016
comment
Спасибо за краткий ответ, однако я заинтересован в одновременной обработке событий касания в обоих представлениях (прямо сейчас, если я передаю событие касания из одного представления в другое, оно просто игнорирует событие в представлении верхнего уровня). Пожалуйста, проверьте обновленный вопрос. - person Falling Into Infinity; 08.12.2016
comment
Я считаю, что в этом случае ChildView должен быть в отдельном макете. Относительный макет не будет работать в этом сценарии, потому что один просматривает позицию относительно других. Спасибо и удачи. - person Omar Faroque Anik; 08.12.2016