创建叠加 ImageView 动画 Google 地图
Create Overlay ImageView Animation Google Map
我正在尝试使我的叠加图像执行以下操作:
- onClick / onDrag of map , 在地图中间显示常量图片
这是一个引脚
- onTouchUp ,将标记图像更改为加载标记和一次数据
加载完成将加载图像更改为带文本的新图像。
这里有一个与我的问题非常相似的解决方案:
到目前为止我做了什么?
- 在我的 google 地图中间放置一个 imageview,然后得到
使用
mMap.getCameraPosition().target
定位该 imageView
使用给出大约中间位置坐标
setOnCameraChanged
,我知道这已被弃用,我们有一个更好的
解决方案?
- 其余部分我无法弄清楚,虽然我已经阅读了几个 SO 问题,声称他们已经实现了将 framelayout 包装在 google 地图的片段上,并在其上应用 onTouch,但找不到那个也是真实的答案。
我的 xml 发生这一切的片段看起来像这样:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragments.MovableMapFragment">
<fragment
android:id="@+id/map_frag_fmm"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/tablayout_global_tabs"
android:layout_alignParentTop="true" />
<! -- long xml ahead -->
有人知道如何实现这种行为吗?
布局
标记应位于地图上的特定坐标处。因此,当您更改地图时,它们会移动。
由于您的图钉总是在地图的中间,因此不适合使用Marker
创建它。相反,您可以将其放置在地图容器中心的地图片段之外。所以你的 XML 应该如下所示:
<RelativeLayout>
<fragment
android:id="@+id/map_fragment" />
<RelativeLayout
android:id="@+id/pin_view"
android:centerInParent="true" />
</RelativeLayout>
您可以根据需要向 pin_view
添加子项以实现动画。
引脚状态
根据您提供的动图,图钉可以有三种状态。
- 空
- 正在加载
- 已加载
听众
您可以通过设置这些侦听器来触发动画:
- setOnCameraMoveStartedListener
- 之前的值需要作废。将 pin 的状态更改为
Empty
- 取消任何正在进行的请求
- setOnCameraIdleListener
- 使用
map.getCameraPosition()
获取坐标
- 将引脚状态更改为 'Loading'
- 触发网络调用以获取数据。在回调中
- 将引脚状态更改为'Loaded'并显示值
请注意,如果用户在 api 完成之前移动,则不会执行回调,因为我们会在用户移动后立即取消请求。所以,这个场景也会被处理
把你的布局放在FrameLayout中,然后在地图上画图。
在 Google 地图中准备就绪(我的代码使用 RxJava):
Observable.<Void>create(subscriber -> {
mMap.setOnCameraIdleListener(() -> subscriber.onNext(null));
})
.debounce(1, TimeUnit.SECONDS)
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(aVoid -> {
LatLng newPos = mMap.getProjection().fromScreenLocation(new Point(mBinding.googleMap.getWidth() / 2, mBinding.googleMap.getHeight() / 2));
makeServerRequest(newPos);
});
最好的解决方案是使用 RxJava/RxAndroid 并用它执行后台任务,但我无法 post 在没有看到您的代码的情况下使用 rx 的解决方案并且已经有来自 @user27799 的示例@Manas Chaudhari 也提供了一个很好的逻辑解释(在下面)。
//bunch of imports
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
public class MapsActivity extends AppCompatActivity implements OnMapReadyCallback
{
private GoogleMap mGoogleMap;
private FrameLayout frameLayout, circleFrameLayout;
private ProgressBar progress;
private TextView textView;
private int circleRadius;
private boolean isMoving = false;
private SupportMapFragment mapFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
initViews();
}
private void initViews() {
frameLayout = (FrameLayout) findViewById(R.id.map_container);
circleFrameLayout = (FrameLayout) frameLayout.findViewById(R.id.pin_view_circle);
textView = (TextView) circleFrameLayout.findViewById(R.id.textView);
progress = (ProgressBar) circleFrameLayout.findViewById(R.id.profile_loader);
mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
private void moveMapCamera() {
if (mGoogleMap == null) {
return;
}
CameraUpdate center = CameraUpdateFactory
.newLatLng(new LatLng(40.76793169992044, -73.98180484771729));
CameraUpdate zoom = CameraUpdateFactory.zoomTo(15);
mGoogleMap.moveCamera(center);
mGoogleMap.animateCamera(zoom);
}
@Override
public void onMapReady(GoogleMap googleMap) {
mGoogleMap = googleMap;
mGoogleMap.setOnCameraMoveStartedListener(new GoogleMap.OnCameraMoveStartedListener() {
/* User starts moving map - change your pin's image here
*/
@Override
public void onCameraMoveStarted(int i) {
isMoving = true;
textView.setVisibility(View.GONE);
progress.setVisibility(View.GONE);
Drawable mDrawable;
if (Build.VERSION.SDK_INT >= 21)
mDrawable = getApplicationContext().getResources().getDrawable(R.drawable.circle_background_moving, null);
else
mDrawable = getApplicationContext().getResources().getDrawable(R.drawable.circle_background_moving);
circleFrameLayout.setBackground(mDrawable);
resizeLayout(false);
}
});
/*User stoped moving map -> retrieve location and do your background task */
mGoogleMap.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() {
@Override
public void onCameraIdle() {
isMoving = false;
textView.setVisibility(View.INVISIBLE);
progress.setVisibility(View.VISIBLE);
resizeLayout(true);
/* this is just an example that simulates data loading
you shoud substitute it with your logic
*/
new Handler().postDelayed(new Runnable() {
public void run() {
Drawable mDrawable;
if (Build.VERSION.SDK_INT >= 21)
mDrawable = getApplicationContext().getResources().getDrawable(R.drawable.circle_background, null);
else
mDrawable = getApplicationContext().getResources().getDrawable(R.drawable.circle_background);
if (!isMoving) {
circleFrameLayout.setBackground(mDrawable);
textView.setVisibility(View.VISIBLE);
progress.setVisibility(View.GONE);
}
}
}, 1500);
}
});
MapsInitializer.initialize(this);
moveMapCamera();
}
//resing circle pin
private void resizeLayout(boolean backToNormalSize){
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) circleFrameLayout.getLayoutParams();
ViewTreeObserver vto = circleFrameLayout.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
circleFrameLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
circleRadius = circleFrameLayout.getMeasuredWidth();
}
});
if (backToNormalSize) {
params.width = WRAP_CONTENT;
params.height = WRAP_CONTENT;
params.topMargin = 0;
} else {
params.topMargin = (int) (circleRadius * 0.3);
params.height = circleRadius - circleRadius / 3;
params.width = circleRadius - circleRadius / 3;
}
circleFrameLayout.setLayoutParams(params);
}
}
大头针位于屏幕中央的布局文件:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/map_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.mapstest.MapsActivity" />
<FrameLayout
android:id="@+id/pin_view_line"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/line_top_margin"
android:background="@drawable/line_background"/>
<FrameLayout
android:id="@+id/pin_view_circle"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/circle_background">
<TextView
android:id="@+id/textView"
android:layout_margin="@dimen/inner_circle_margin"
android:layout_width="@dimen/inner_circle_radius"
android:layout_height="@dimen/inner_circle_radius"
android:layout_gravity="top|center_horizontal"
android:gravity="center"
android:text="12 min"
android:textSize="@dimen/text_size"
android:textColor="@android:color/white"/>
<ProgressBar
android:id="@+id/profile_loader"
android:layout_margin="@dimen/inner_circle_margin"
android:layout_width="@dimen/inner_circle_radius"
android:layout_height="@dimen/inner_circle_radius"
android:indeterminate="true"
android:layout_gravity="top|center_horizontal"
android:visibility="gone"
android:contentDescription="@null"/>
</FrameLayout>
不动时圈背景:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@android:color/holo_green_dark"/>
<stroke
android:width="@dimen/stroke_width"
android:color="@android:color/black"/>
<size
android:width="@dimen/circle_radius"
android:height="@dimen/circle_radius"/>
</shape>
地图移动时的圆形背景:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@android:color/black"/>
<size
android:width="@dimen/circle_radius"
android:height="@dimen/circle_radius"/>
</shape>
线条背景文件:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/black"/>
<size
android:width="@dimen/line_width"
android:height="@dimen/line_height"/>
</shape>
维度:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="circle_radius">48dp</dimen>
<dimen name="line_width">4dp</dimen>
<dimen name="line_height">40dp</dimen>
<dimen name="stroke_width">4dp</dimen>
<dimen name="text_size">10sp</dimen>
<dimen name="inner_circle_radius">40dp</dimen>
<dimen name="inner_circle_margin">4dp</dimen>
<dimen name="line_top_margin">20dp</dimen>
</resources>
希望对您有所帮助,但如果您有任何问题,请随时提出,或者如果您发现需要改进的地方,请编辑我的解决方案。
干杯。
我正在尝试使我的叠加图像执行以下操作:
- onClick / onDrag of map , 在地图中间显示常量图片 这是一个引脚
- onTouchUp ,将标记图像更改为加载标记和一次数据 加载完成将加载图像更改为带文本的新图像。
这里有一个与我的问题非常相似的解决方案:
到目前为止我做了什么?
- 在我的 google 地图中间放置一个 imageview,然后得到
使用
mMap.getCameraPosition().target
定位该 imageView 使用给出大约中间位置坐标setOnCameraChanged
,我知道这已被弃用,我们有一个更好的 解决方案? - 其余部分我无法弄清楚,虽然我已经阅读了几个 SO 问题,声称他们已经实现了将 framelayout 包装在 google 地图的片段上,并在其上应用 onTouch,但找不到那个也是真实的答案。
我的 xml 发生这一切的片段看起来像这样:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragments.MovableMapFragment">
<fragment
android:id="@+id/map_frag_fmm"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/tablayout_global_tabs"
android:layout_alignParentTop="true" />
<! -- long xml ahead -->
有人知道如何实现这种行为吗?
布局
标记应位于地图上的特定坐标处。因此,当您更改地图时,它们会移动。
由于您的图钉总是在地图的中间,因此不适合使用Marker
创建它。相反,您可以将其放置在地图容器中心的地图片段之外。所以你的 XML 应该如下所示:
<RelativeLayout>
<fragment
android:id="@+id/map_fragment" />
<RelativeLayout
android:id="@+id/pin_view"
android:centerInParent="true" />
</RelativeLayout>
您可以根据需要向 pin_view
添加子项以实现动画。
引脚状态
根据您提供的动图,图钉可以有三种状态。
- 空
- 正在加载
- 已加载
听众
您可以通过设置这些侦听器来触发动画:
- setOnCameraMoveStartedListener
- 之前的值需要作废。将 pin 的状态更改为
Empty
- 取消任何正在进行的请求
- 之前的值需要作废。将 pin 的状态更改为
- setOnCameraIdleListener
- 使用
map.getCameraPosition()
获取坐标
- 将引脚状态更改为 'Loading'
- 触发网络调用以获取数据。在回调中
- 将引脚状态更改为'Loaded'并显示值 请注意,如果用户在 api 完成之前移动,则不会执行回调,因为我们会在用户移动后立即取消请求。所以,这个场景也会被处理
- 使用
把你的布局放在FrameLayout中,然后在地图上画图。 在 Google 地图中准备就绪(我的代码使用 RxJava):
Observable.<Void>create(subscriber -> {
mMap.setOnCameraIdleListener(() -> subscriber.onNext(null));
})
.debounce(1, TimeUnit.SECONDS)
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(aVoid -> {
LatLng newPos = mMap.getProjection().fromScreenLocation(new Point(mBinding.googleMap.getWidth() / 2, mBinding.googleMap.getHeight() / 2));
makeServerRequest(newPos);
});
最好的解决方案是使用 RxJava/RxAndroid 并用它执行后台任务,但我无法 post 在没有看到您的代码的情况下使用 rx 的解决方案并且已经有来自 @user27799 的示例@Manas Chaudhari 也提供了一个很好的逻辑解释(在下面)。
//bunch of imports
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
public class MapsActivity extends AppCompatActivity implements OnMapReadyCallback
{
private GoogleMap mGoogleMap;
private FrameLayout frameLayout, circleFrameLayout;
private ProgressBar progress;
private TextView textView;
private int circleRadius;
private boolean isMoving = false;
private SupportMapFragment mapFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
initViews();
}
private void initViews() {
frameLayout = (FrameLayout) findViewById(R.id.map_container);
circleFrameLayout = (FrameLayout) frameLayout.findViewById(R.id.pin_view_circle);
textView = (TextView) circleFrameLayout.findViewById(R.id.textView);
progress = (ProgressBar) circleFrameLayout.findViewById(R.id.profile_loader);
mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
private void moveMapCamera() {
if (mGoogleMap == null) {
return;
}
CameraUpdate center = CameraUpdateFactory
.newLatLng(new LatLng(40.76793169992044, -73.98180484771729));
CameraUpdate zoom = CameraUpdateFactory.zoomTo(15);
mGoogleMap.moveCamera(center);
mGoogleMap.animateCamera(zoom);
}
@Override
public void onMapReady(GoogleMap googleMap) {
mGoogleMap = googleMap;
mGoogleMap.setOnCameraMoveStartedListener(new GoogleMap.OnCameraMoveStartedListener() {
/* User starts moving map - change your pin's image here
*/
@Override
public void onCameraMoveStarted(int i) {
isMoving = true;
textView.setVisibility(View.GONE);
progress.setVisibility(View.GONE);
Drawable mDrawable;
if (Build.VERSION.SDK_INT >= 21)
mDrawable = getApplicationContext().getResources().getDrawable(R.drawable.circle_background_moving, null);
else
mDrawable = getApplicationContext().getResources().getDrawable(R.drawable.circle_background_moving);
circleFrameLayout.setBackground(mDrawable);
resizeLayout(false);
}
});
/*User stoped moving map -> retrieve location and do your background task */
mGoogleMap.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() {
@Override
public void onCameraIdle() {
isMoving = false;
textView.setVisibility(View.INVISIBLE);
progress.setVisibility(View.VISIBLE);
resizeLayout(true);
/* this is just an example that simulates data loading
you shoud substitute it with your logic
*/
new Handler().postDelayed(new Runnable() {
public void run() {
Drawable mDrawable;
if (Build.VERSION.SDK_INT >= 21)
mDrawable = getApplicationContext().getResources().getDrawable(R.drawable.circle_background, null);
else
mDrawable = getApplicationContext().getResources().getDrawable(R.drawable.circle_background);
if (!isMoving) {
circleFrameLayout.setBackground(mDrawable);
textView.setVisibility(View.VISIBLE);
progress.setVisibility(View.GONE);
}
}
}, 1500);
}
});
MapsInitializer.initialize(this);
moveMapCamera();
}
//resing circle pin
private void resizeLayout(boolean backToNormalSize){
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) circleFrameLayout.getLayoutParams();
ViewTreeObserver vto = circleFrameLayout.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
circleFrameLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
circleRadius = circleFrameLayout.getMeasuredWidth();
}
});
if (backToNormalSize) {
params.width = WRAP_CONTENT;
params.height = WRAP_CONTENT;
params.topMargin = 0;
} else {
params.topMargin = (int) (circleRadius * 0.3);
params.height = circleRadius - circleRadius / 3;
params.width = circleRadius - circleRadius / 3;
}
circleFrameLayout.setLayoutParams(params);
}
}
大头针位于屏幕中央的布局文件:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/map_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.mapstest.MapsActivity" />
<FrameLayout
android:id="@+id/pin_view_line"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/line_top_margin"
android:background="@drawable/line_background"/>
<FrameLayout
android:id="@+id/pin_view_circle"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/circle_background">
<TextView
android:id="@+id/textView"
android:layout_margin="@dimen/inner_circle_margin"
android:layout_width="@dimen/inner_circle_radius"
android:layout_height="@dimen/inner_circle_radius"
android:layout_gravity="top|center_horizontal"
android:gravity="center"
android:text="12 min"
android:textSize="@dimen/text_size"
android:textColor="@android:color/white"/>
<ProgressBar
android:id="@+id/profile_loader"
android:layout_margin="@dimen/inner_circle_margin"
android:layout_width="@dimen/inner_circle_radius"
android:layout_height="@dimen/inner_circle_radius"
android:indeterminate="true"
android:layout_gravity="top|center_horizontal"
android:visibility="gone"
android:contentDescription="@null"/>
</FrameLayout>
不动时圈背景:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@android:color/holo_green_dark"/>
<stroke
android:width="@dimen/stroke_width"
android:color="@android:color/black"/>
<size
android:width="@dimen/circle_radius"
android:height="@dimen/circle_radius"/>
</shape>
地图移动时的圆形背景:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@android:color/black"/>
<size
android:width="@dimen/circle_radius"
android:height="@dimen/circle_radius"/>
</shape>
线条背景文件:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/black"/>
<size
android:width="@dimen/line_width"
android:height="@dimen/line_height"/>
</shape>
维度:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="circle_radius">48dp</dimen>
<dimen name="line_width">4dp</dimen>
<dimen name="line_height">40dp</dimen>
<dimen name="stroke_width">4dp</dimen>
<dimen name="text_size">10sp</dimen>
<dimen name="inner_circle_radius">40dp</dimen>
<dimen name="inner_circle_margin">4dp</dimen>
<dimen name="line_top_margin">20dp</dimen>
</resources>
希望对您有所帮助,但如果您有任何问题,请随时提出,或者如果您发现需要改进的地方,请编辑我的解决方案。
干杯。