Подтвердить что ты не робот

API Карт Google v2 SupportMapFragment внутри ScrollView - пользователи не могут прокручивать карту по вертикали

Я пытаюсь поместить карту Google в прокрутку, чтобы пользователь мог прокручивать вниз другое содержимое, чтобы увидеть карту. Проблема в том, что этот вид прокрутки поглощает все вертикальные касательные события, поэтому опыт работы с интерфейсом UI становится очень странным.

Я знаю, что в V1 карты google вы можете переопределить onTouch или setOnTouchListener для вызова requestDisallowInterceptTouchEvent в MotionEvent.ACTION_DOWN. Я попытался реализовать подобный трюк с V2 безрезультатно.

До сих пор я пробовал:

  • Переопределите SupportMapFragment и внутри onCreateView, установите прослушиватель касания для представления
  • вызов .getView() экземпляра SupportMapFragment, затем setOnTouchListener
  • Обтекание относительной компоновки или макета кадра, маскирование фрагмента с помощью прозрачного представления или изображения

Ни одна из них не устраняет проблему прокрутки. Я что-то упустил? Если у кого-нибудь есть рабочий пример карты внутри прокрутки, можете ли вы, пожалуйста, поделиться примером кода?

4b9b3361

Ответ 1

Применить прозрачное изображение над фрагментом mapview.

<RelativeLayout
    android:id="@+id/map_layout"
    android:layout_width="match_parent"
    android:layout_height="300dp">

    <fragment
        android:id="@+id/mapview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="-100dp"
        android:layout_marginBottom="-100dp"
        android:name="com.google.android.gms.maps.MapFragment"/>

    <ImageView
        android:id="@+id/transparent_image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@color/transparent" />

</RelativeLayout>   

Затем установите requestDisallowInterceptTouchEvent(true) для основного ScrollView. Когда пользователь коснется прозрачного изображения и перемещается, отключите касание прозрачного изображения для MotionEvent.ACTION_DOWN и MotionEvent.ACTION_MOVE, чтобы фрагмент карты мог принимать события касания.

ScrollView mainScrollView = (ScrollView) findViewById(R.id.main_scrollview);
ImageView transparentImageView = (ImageView) findViewById(R.id.transparent_image);

transparentImageView.setOnTouchListener(new View.OnTouchListener() {

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        int action = event.getAction();
        switch (action) {
           case MotionEvent.ACTION_DOWN:
                // Disallow ScrollView to intercept touch events.
                mainScrollView.requestDisallowInterceptTouchEvent(true);
                // Disable touch on transparent view
                return false;

           case MotionEvent.ACTION_UP:
                // Allow ScrollView to intercept touch events.
                mainScrollView.requestDisallowInterceptTouchEvent(false);
                return true;

           case MotionEvent.ACTION_MOVE:
                mainScrollView.requestDisallowInterceptTouchEvent(true);
                return false;

           default: 
                return true;
        }   
    }
});

Это сработало для меня. Надеюсь, это поможет вам.

Ответ 2

Я столкнулся с подобной проблемой и придумал более общее рабочее решение, основанное на ответах In-Ho Yi и Данаил Димитров выше.

public class CustomScrollView extends ScrollView {

    List<View> mInterceptScrollViews = new ArrayList<View>();

    public CustomScrollView(Context context) {
        super(context);
    }

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

    public CustomScrollView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public void addInterceptScrollView(View view) {
        mInterceptScrollViews.add(view);
    }

    public void removeInterceptScrollView(View view) {
        mInterceptScrollViews.remove(view);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {

        // check if we have any views that should use their own scrolling
        if (mInterceptScrollViews.size() > 0) {
            int x = (int) event.getX();
            int y = (int) event.getY();
            Rect bounds = new Rect();

            for (View view : mInterceptScrollViews) {
                view.getHitRect(bounds);
                if (bounds.contains(x, y)) {
                    //were touching a view that should intercept scrolling
                    return false;
                }
            }
        }

        return super.onInterceptTouchEvent(event);
    }
}

Ответ 3

Спасибо за предложения,

После многих попыток и ошибок, вытащив мои волосы и присягнув на монитор и мой плохой Android-тестовый телефон, я понял, что если я настрою ScrollView, переопределите onInterceptTouchEvent, в котором мы вернем false, когда событие находится на карте независимо от того, что происходит, тогда прокрутка на карте происходит так, как ожидалось.

class MyScrollView(c:Context, a:AttributeSet) extends ScrollView(c,a) {
  val parent = c.asInstanceOf[MyActivity]
  override def onInterceptTouchEvent(ev:MotionEvent):Boolean = {
    var bound:Rect = new Rect()
    parent.mMap.getHitRect(bound)
    if(bound.contains(ev.getX.toInt,ev.getY.toInt))
      false
    else
      super.onInterceptTouchEvent(ev)
  }
}

Этот код находится в Scala, но вы получаете эту идею.

Примечание. Я закончил использование исходного вида карты (как показано в android-sdks\extras\google\google_play_services\samples\maps\src\com\example\mapdemoRawMapViewDemoActivity.java). Угадайте, что вы можете сделать почти то же самое с фрагментами, в первую очередь мне никогда не нравились фрагменты.

Я думаю, Google должен мне извиняться.

Ответ 4

У меня была такая же проблема, поэтому вот как я использовал решение как код Java на всякий случай, если кому-то это понадобится. Вам просто нужно установить поле mapView при его использовании.

import com.google.android.gms.maps.MapView;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ScrollView;

public class ScrollViewWithMap extends ScrollView
{
    public MapView mapView;

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

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev)
    {
        if (mapView == null)
            return super.onInterceptTouchEvent(ev);

        if (inRegion(ev.getRawX(), ev.getRawY(), mapView))
            return false;

        return super.onInterceptTouchEvent(ev);
    }

    private boolean inRegion(float x, float y, View v)
    {
        int[] mCoordBuffer = new int[]
        { 0, 0 };

        v.getLocationOnScreen(mCoordBuffer);

        return mCoordBuffer[0] + v.getWidth() > x && // right edge
                mCoordBuffer[1] + v.getHeight() > y && // bottom edge
                mCoordBuffer[0] < x && // left edge
                mCoordBuffer[1] < y; // top edge
    }
}

Ответ 5

Принятый ответ не работал в моем случае. Ответ гостя не сделал (но почти). Если это так, если кто-то попробует эту отредактированную версию ответа гостя.

Я прокомментировал высоту панели действий, если кто-то должен ее использовать при расчете hitbox.

public class InterceptableScrollView extends ScrollView {

    List<View> mInterceptScrollViews = new ArrayList<View>();

    public InterceptableScrollView(Context context) {
        super(context);
    }

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

    public InterceptableScrollView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public void addInterceptScrollView(View view) {
        mInterceptScrollViews.add(view);
    }

    public void removeInterceptScrollView(View view) {
        mInterceptScrollViews.remove(view);
    }

    private int getRelativeTop(View myView) {
        if (myView.getParent() == this)
            return myView.getTop();
        else
            return myView.getTop() + getRelativeTop((View) myView.getParent());
    }
    private int getRelativeLeft(View myView) {
        if (myView.getParent() == this)
            return myView.getLeft();
        else
            return myView.getLeft() + getRelativeLeft((View) myView.getParent());
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {

        // check if we have any views that should use their own scrolling
        if (mInterceptScrollViews.size() > 0) {
            int x = (int) event.getX();
            int y = (int) event.getY();


            /*
            int actionBarHeight = 0;

            TypedValue tv = new TypedValue();
            if (getContext().getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true))
            {
                actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,getResources().getDisplayMetrics());
            }
            */

            int viewLocationY = 0;
            int viewLocationX = 0;
            int relativeTop = 0;
            int relativeLeft = 0;

            for (View view : mInterceptScrollViews) {

                relativeTop = getRelativeTop((View) view.getParent());
                relativeLeft = getRelativeLeft((View) view.getParent());
                viewLocationY = relativeTop - getScrollY();
                viewLocationX = relativeLeft - getScrollX();

                if (view.getHeight() + viewLocationY > y && y > viewLocationY && view.getWidth() + viewLocationX > x && x > viewLocationX)
                {
                    return false;
                }
            }
        }

        return super.onInterceptTouchEvent(event);
    }
}

Ответ 6

Улучшение кода, если вам не требуется прозрачное изображение еще раз:

// gmap hack for touch and scrollview
        final ScrollView mainScrollView = (ScrollView) rootView.findViewById(R.id.scrollView);
        (rootView.findViewById(R.id.fixTouchMap)).setOnTouchListener(new View.OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                int action = event.getAction();
                switch (action) {
                    case MotionEvent.ACTION_DOWN:
                        // Disallow ScrollView to intercept touch events.
                        mainScrollView.requestDisallowInterceptTouchEvent(true);
                        // Disable touch on transparent view
                        return false;

                    case MotionEvent.ACTION_UP:
                        // Allow ScrollView to intercept touch events.
                        mainScrollView.requestDisallowInterceptTouchEvent(false);
                        return true;

                    case MotionEvent.ACTION_MOVE:
                        mainScrollView.requestDisallowInterceptTouchEvent(true);
                        return false;

                    default:
                        return true;
                }
            }
        });

Ответ 7

Большинство параметров, перечисленных выше, не работали для меня, но следующее оказалось отличным решением проблемы для меня:

Решение по сырному барону

Мне также пришлось реализовать немного по-разному для моей реализации, поскольку я использую карту в фрагменте, который сделал вещи немного сложнее, но легко работать.