Reputation: 1103
I am trying to make my overlay image to do following things:
Here is a pretty similar solution to my problem:
What i have done so far ?
mMap.getCameraPosition().target
which gives approx middle position coordinates using
setOnCameraChanged
, i know that's deprecated, well have a better
solution?.my xml of fragment where all this is happening looks like this :
<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 -->
Someone know how to achieve this kind of behavior ?
Upvotes: 16
Views: 2481
Reputation: 1002
The best solution would be to use RxJava/RxAndroid and do background tasks with it, but I can't post a solution using rx without seeing your code and there's already an example from @user27799 also @Manas Chaudhari provided a great explanation of the logic (down below).
//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);
}
}
Layout file with pin in the center of the screen:
<?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>
Circle background when not moving:
<?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>
Circle background when map is moving:
<?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>
Line background file:
<?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>
Dimensions:
<?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>
I hope it helps you, but if you have any questions feel free to ask or if you see areas that you'd improve - edit my solution.
Cheers.
Upvotes: 7
Reputation: 1574
Put you layout in FrameLayout and draw picture over Map. When GoogleMap ready (My code use 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);
});
Upvotes: 5
Reputation: 276
Markers are meant to be positioned at specific coordinates on Map. Thus, they move when you change the Map.
As your pin is always in the middle of the map, its not suitable to create it using Marker
. Instead, you can place it outside the map fragment at the center of the map container. So your XML should look as follows:
<RelativeLayout>
<fragment
android:id="@+id/map_fragment" />
<RelativeLayout
android:id="@+id/pin_view"
android:centerInParent="true" />
</RelativeLayout>
You can add children to pin_view
to implement animations as per your requirement.
Based on the gif provided by you, there can be three states for the pin.
You can trigger the animations by setting these listeners:
Empty
map.getCameraPosition()
Upvotes: 1