ユーザーが他のコンテンツをスクロールして地図を表示できるように、Googleマップをスクロールビュー内に配置しようとしています。問題は、このスクロールビューがすべての垂直タッチイベントを使い果たしているため、マップのUIエクスペリエンスが非常に奇妙になることです。
GoogleマップのV1では、onTouchまたはsetOnTouchListenerをオーバーライドして、MotionEvent.ACTION_DOWNでrequestDisallowInterceptTouchEventを呼び出すことができます。私はV2で同様のトリックを実装しようとしましたが、役に立ちませんでした。
これまで私は試しました:
これらのどれもスクロールの問題を解決しませんでした。ここに何かが足りませんか?スクロールビュー内に実際の地図の例がある場合は、コード例を教えてください。
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>
_
次に、メインのScrollViewにrequestDisallowInterceptTouchEvent(true)
を設定します。ユーザーが透明画像に触れて移動すると、_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;
}
}
});
_
これは私のために働いた。お役に立てば幸いです。
同様の問題に遭遇し、上記の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 + scrollY)) {
//were touching a view that should intercept scrolling
return false;
}
}
}
return super.onInterceptTouchEvent(event);
}
}
ご提案ありがとうございます、
多くの試行錯誤の後、私の髪を脱いでモニターと私の貧しいAndroidテスト電話で宣誓しました。ScrollViewをカスタマイズすると、onInterceptTouchEventをオーバーライドして、イベントが何であれマップビュー上にある場合、マップのスクロールは期待どおりに行われます。
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には謝罪があると思います。
私は同じ問題を抱えていたので、誰かが必要な場合に備えて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
}
}
私の場合、受け入れられた答えはうまくいきませんでした。ゲストの答えはどちらもしませんでした(しかしほとんど)。これが他の人に当てはまる場合は、この編集済みバージョンのゲストの回答を試してください。
ヒットボックスを計算するときに誰かが使用する必要がある場合、アクションバーの高さをコメントアウトしました。
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);
}
}
XMLでカスタムのGoogleマップフラグメントを使用します。
ここに私のために働いた完全なコードがあります。質問がありましたら教えてください。
XMLファイルに、以下をマップフラグメントとして追加
<fragment
Android:id="@+id/map_with_scroll_fix"
Android:name="com.myapplication.maputil.GoogleMapWithScrollFix"
Android:layout_width="match_parent"
Android:layout_height="match_parent" />
これがマップのカスタムクラスです
package com.myapplication.maputil;
import Android.content.Context;
import Android.os.Bundle;
import Android.view.LayoutInflater;
import Android.view.MotionEvent;
import Android.view.View;
import Android.view.ViewGroup;
import Android.widget.FrameLayout;
import com.google.Android.gms.maps.SupportMapFragment;
public class GoogleMapWithScrollFix extends SupportMapFragment {
private OnTouchListener mListener;
@Override
public View onCreateView(LayoutInflater layoutInflater, ViewGroup viewGroup, Bundle savedInstance) {
View layout = super.onCreateView(layoutInflater, viewGroup, savedInstance);
TouchableWrapper touchableWrapper = new TouchableWrapper(getActivity());
touchableWrapper.setBackgroundColor(getResources().getColor(Android.R.color.transparent));
((ViewGroup) layout).addView(touchableWrapper,
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
return layout;
}
public void setListener(OnTouchListener listener) {
mListener = listener;
}
public interface OnTouchListener {
void onTouch();
}
public class TouchableWrapper extends FrameLayout {
public TouchableWrapper(Context context) {
super(context);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mListener.onTouch();
break;
case MotionEvent.ACTION_UP:
mListener.onTouch();
break;
}
return super.dispatchTouchEvent(event);
}
}
}
mapviewを初期化するには、アクティビティクラスに次を追加します。それだけです。多田:)
((GoogleMapWithScrollFix) getSupportFragmentManager()
.findFragmentById(R.id.map_with_scroll_fix)).getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(GoogleMap googleMap) {
ScrollView mScrollView = findViewById(R.id.scrollview); //parent scrollview in xml, give your scrollview id value
((GoogleMapWithScrollFix) getSupportFragmentManager()
.findFragmentById(R.id.map_with_scroll_fix)).setListener(new GoogleMapWithScrollFix.OnTouchListener() {
@Override
public void onTouch() {
//Here is the magic happens.
//we disable scrolling of outside scroll view here
mScrollView.requestDisallowInterceptTouchEvent(true);
}
});
}
});
透明な画像が再度必要ない場合のコードの改善:
// 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;
}
}
});
上記のオプションのほとんどは私には機能しませんでしたが、次の問題は私にとって問題の素晴らしい解決策であることが判明しました。
また、マップをフラグメントで使用しているため、実装が多少複雑になりますが、作業が簡単になるため、実装する必要があります。