API Карт Google v2 SupportMapFragment внутри ScrollView - пользователи не могут прокручивать карту по вертикали
Я пытаюсь поместить карту Google в прокрутку, чтобы пользователь мог прокручивать вниз другое содержимое, чтобы увидеть карту. Проблема в том, что этот вид прокрутки поглощает все вертикальные касательные события, поэтому опыт работы с интерфейсом UI становится очень странным.
Я знаю, что в V1 карты google вы можете переопределить onTouch или setOnTouchListener для вызова requestDisallowInterceptTouchEvent в MotionEvent.ACTION_DOWN. Я попытался реализовать подобный трюк с V2 безрезультатно.
До сих пор я пробовал:
- Переопределите SupportMapFragment и внутри onCreateView, установите прослушиватель касания для представления
- вызов .getView() экземпляра SupportMapFragment, затем setOnTouchListener
- Обтекание относительной компоновки или макета кадра, маскирование фрагмента с помощью прозрачного представления или изображения
Ни одна из них не устраняет проблему прокрутки. Я что-то упустил? Если у кого-нибудь есть рабочий пример карты внутри прокрутки, можете ли вы, пожалуйста, поделиться примером кода?
Ответы
Ответ 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
Большинство параметров, перечисленных выше, не работали для меня, но следующее оказалось отличным решением проблемы для меня:
Решение по сырному барону
Мне также пришлось реализовать немного по-разному для моей реализации, поскольку я использую карту в фрагменте, который сделал вещи немного сложнее, но легко работать.