0

I have added google maps SDK to my project and have added a marker at the current position. If the user moves the camera the marker also changes its position by setting marker.setPosition(lat, long); in onCameraMove() callback, But I am getting a flicker when the marker changes its position. I want to make it smooth as in Ola app.

Attaching the code for reference

public class StoreAddressActivity extends BaseActivity<FragmentStoreAddressBinding, WebsiteCreationViewModel> implements GoogleMap.OnMyLocationButtonClickListener,
        GoogleMap.OnMyLocationClickListener, OnMapReadyCallback, ActivityCompat.OnRequestPermissionsResultCallback,
        GoogleMap.OnMarkerClickListener, OnCameraMoveListener, GoogleMap.OnCameraIdleListener, Navigator.StoreAddressNavigator {

    private static final String TAG = StoreAddressActivity.class.getSimpleName();

    @Inject
    ViewModelProviderFactory factory;

    private WebsiteCreationViewModel websiteCreationViewModel;
    private FragmentStoreAddressBinding fragmentStoreAddressBinding;
    private FusedLocationProviderClient mFusedLocationClient;
    private Location mLastLocation;
    private UiSettings mUiSettings;
    private Marker currentLocationMarker;
    private static final int DEFAULT_ZOOM = 15;
    private static final int LOCATION_PERMISSION_REQUEST_CODE = 1;
    private boolean locationPermissionGranted = false;
    private GoogleMap map;
    private StringBuilder mResult;
    private int previousLength;
    private boolean backSpace;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        fragmentStoreAddressBinding = getViewDataBinding();
        fragmentStoreAddressBinding.setNavigator(this);
        init();
    }

    private void init() {
        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);

        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);

        String apiKey = getString(R.string.google_maps_key);

        if (!Places.isInitialized()) {
            Places.initialize(getApplicationContext(), apiKey);
        }

        PlacesClient placesClient = Places.createClient(this);

        fragmentStoreAddressBinding.mapLocationEdittext.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                previousLength = s.length();

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
            }

            @Override
            public void afterTextChanged(Editable s) {
                backSpace = previousLength > s.length();

                if (!backSpace) {
                    // Create a new token for the autocomplete session. Pass this to FindAutocompletePredictionsRequest,
                    // and once again when the user makes a selection (for example when calling fetchPlace()).
                    AutocompleteSessionToken token = AutocompleteSessionToken.newInstance();
                    // Create a RectangularBounds object.
                    RectangularBounds bounds = RectangularBounds.newInstance(
                            new LatLng(-33.880490, 151.184363), //dummy lat/lng
                            new LatLng(-33.858754, 151.229596));
                    // Use the builder to create a FindAutocompletePredictionsRequest.
                    FindAutocompletePredictionsRequest request = FindAutocompletePredictionsRequest.builder()
                            // Call either setLocationBias() OR setLocationRestriction().
                            //.setLocationBias(bounds)
                            //.setLocationRestriction(bounds)
                            .setCountry("IN")
                            .setTypeFilter(TypeFilter.ADDRESS)
                            .setSessionToken(token)
                            .setQuery(s.toString())
                            .build();


                    placesClient.findAutocompletePredictions(request).addOnSuccessListener(response -> {
                        mResult = new StringBuilder();
                        for (AutocompletePrediction prediction : response.getAutocompletePredictions()) {
                            mResult.append(" ").append(prediction.getFullText(null) + "\n");
                            Log.d(TAG, prediction.getPlaceId());
                            Log.d(TAG, prediction.getPrimaryText(null).toString());
                        }
                        Log.d(TAG, mResult.toString());
//                    mSearchResult.setText(String.valueOf(mResult));
                    }).addOnFailureListener((exception) -> {
                        if (exception instanceof ApiException) {
                            ApiException apiException = (ApiException) exception;
                            Log.e(TAG, "Place not found: " + apiException.getStatusCode());
                        }
                    });
                }
            }
        });
    }

    @Override
    public WebsiteCreationViewModel getViewModel() {
        websiteCreationViewModel = new ViewModelProvider(this, factory).get(WebsiteCreationViewModel.class);
        return websiteCreationViewModel;
    }

    @Override
    public int getBindingVariable() {
        return BR.viewModel;
    }

    @Override
    public int getLayoutId() {
        return R.layout.fragment_store_address;
    }

    @Override
    public boolean onMyLocationButtonClick() {
        return false;
    }

    @Override
    public void onMyLocationClick(@NonNull Location location) {
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        map = googleMap;
        mUiSettings = map.getUiSettings();
        mUiSettings.setZoomControlsEnabled(true);
        mUiSettings.setMyLocationButtonEnabled(false);

        map.setMapType(GoogleMap.MAP_TYPE_NORMAL);
        map.setOnMarkerClickListener(this);
        map.setOnCameraMoveListener(this);
        map.setOnCameraIdleListener(this);

        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (ContextCompat.checkSelfPermission(this,
                    Manifest.permission.ACCESS_FINE_LOCATION)
                    == PackageManager.PERMISSION_GRANTED) {
                locationPermissionGranted = true;
                map.setMyLocationEnabled(true);
            } else {
                //Request Location Permission
                checkLocationPermission();
            }
        } else {
            locationPermissionGranted = true;
            map.setMyLocationEnabled(true);
        }

        getDeviceLocation();
    }


    private void checkLocationPermission() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {

            // Should we show an explanation?
            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                    Manifest.permission.ACCESS_FINE_LOCATION)) {

                // Show an explanation to the user *asynchronously* -- don't block
                // this thread waiting for the user's response! After the user
                // sees the explanation, try again to request the permission.
                new AlertDialog.Builder(this)
                        .setTitle("Location Permission Needed")
                        .setMessage("This app needs the Location permission, please accept to use location functionality")
                        .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialogInterface, int i) {
                                //Prompt the user once explanation has been shown
                                ActivityCompat.requestPermissions(StoreAddressActivity.this,
                                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                                        LOCATION_PERMISSION_REQUEST_CODE);
                            }
                        })
                        .create()
                        .show();


            } else {
                // No explanation needed, we can request the permission.
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                        LOCATION_PERMISSION_REQUEST_CODE);
            }
        }
    }

    /**
     * Enables the My Location layer if the fine location permission has been granted.
     */
    private void enableMyLocation() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            locationPermissionGranted = true;
            if (map != null) {
                map.setMyLocationEnabled(true);
            }
        } else {
            // Permission to access the location is missing. Show rationale and request permission
            requestPermissionsSafely(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode != LOCATION_PERMISSION_REQUEST_CODE) {
            return;
        }

        if (PermissionUtils.isPermissionGranted(permissions, grantResults, Manifest.permission.ACCESS_FINE_LOCATION)) {
            // Enable the my location layer if the permission has been granted.
            locationPermissionGranted = true;
            enableMyLocation();
        } else {
            locationPermissionGranted = false;
        }
    }

    @Override
    public void onPause() {
        super.onPause();
    }

    /**
     * Demonstrates converting a {@link Drawable} to a {@link BitmapDescriptor},
     * for use as a marker icon.
     */
    private BitmapDescriptor vectorToBitmap(@DrawableRes int id) {
        Drawable vectorDrawable = ResourcesCompat.getDrawable(getResources(), id, null);
        Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(),
                vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        vectorDrawable.draw(canvas);
        return BitmapDescriptorFactory.fromBitmap(bitmap);
    }


    private void getDeviceLocation() {
        /*
         * Get the best and most recent location of the device, which may be null in rare
         * cases when a location is not available.
         */
        try {
            if (locationPermissionGranted) {
                Task<Location> locationResult = mFusedLocationClient.getLastLocation();
                locationResult.addOnCompleteListener(this, new OnCompleteListener<Location>() {
                    @Override
                    public void onComplete(@NonNull Task<Location> task) {
                        if (task.isSuccessful()) {
                            // Set the map's camera position to the current location of the device.
                            mLastLocation = task.getResult();
                            if (mLastLocation != null) {
                                LatLng latLng = new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude());

                                LocationAddress locationAddress = new LocationAddress();
                                locationAddress.getAddressFromLocation(mLastLocation.getLatitude(), mLastLocation.getLongitude(),
                                        getApplicationContext(), new GeocoderHandler());

//                                currentLocationMarker = map.addMarker(new MarkerOptions()
//                                        .position(latLng)
//                                        .icon(vectorToBitmap(R.drawable.ic_location_marker))
//                                        .title("MOVE PIN TO ADJUST")
//                                        .draggable(false));

                                MarkerOptions markerOpt = new MarkerOptions();
                                markerOpt.position(latLng)
                                        .title("MOVE PIN TO ADJUST").icon(vectorToBitmap(R.drawable.ic_location_marker));

                                //Set Custom InfoWindow Adapter
                                CustomInfoWindowAdapter adapter = new CustomInfoWindowAdapter(StoreAddressActivity.this);
                                map.setInfoWindowAdapter(adapter);
                                currentLocationMarker = map.addMarker(markerOpt);
                                currentLocationMarker.showInfoWindow();
                                map.moveCamera(CameraUpdateFactory.newLatLngZoom(
                                        latLng, DEFAULT_ZOOM));
                            }
                        }
                    }
                });
            }
        } catch (SecurityException e) {
        }
    }

    @Override
    public boolean onMarkerClick(Marker marker) {
        return false;
    }

    @Override
    public void onCameraMove() {
        LatLng target = map.getCameraPosition().target;
        //currentLocationMarker.setPosition(target);

        animateMarker(currentLocationMarker, target, false);
//        CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(target, DEFAULT_ZOOM);
//        map.animateCamera(cameraUpdate, 4000, null);
    }

    @Override
    public void onCameraIdle() {
        Log.d(TAG, "Camera is idle");
        LatLng target = map.getCameraPosition().target;
        if (currentLocationMarker != null)
            currentLocationMarker.setPosition(target);

        LocationAddress locationAddress = new LocationAddress();
        locationAddress.getAddressFromLocation(target.latitude, target.longitude,
                getApplicationContext(), new GeocoderHandler());

        fragmentStoreAddressBinding.currentLocationTextview.setText(getString(R.string.go_to_current_location));
    }

    @Override
    public void currentLocationClick() {
        if (mLastLocation != null) {
            fragmentStoreAddressBinding.currentLocationTextview.setText(getString(R.string.this_is_current_location));
            LatLng latLng = new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude());
            CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(latLng, DEFAULT_ZOOM);
            map.moveCamera(cameraUpdate);
            map.animateCamera(cameraUpdate, 4000, null);
            LocationAddress locationAddress = new LocationAddress();
            locationAddress.getAddressFromLocation(mLastLocation.getLatitude(), mLastLocation.getLongitude(),
                    getApplicationContext(), new GeocoderHandler());
        }
    }

    private class GeocoderHandler extends Handler {
        @Override
        public void handleMessage(Message message) {
            String locationAddress;
            switch (message.what) {
                case 1:
                    Bundle bundle = message.getData();
                    locationAddress = bundle.getString("address");
                    break;
                default:
                    locationAddress = null;
            }
            fragmentStoreAddressBinding.mapLocationEdittext.setText(locationAddress);
        }
    }

    public void animateMarker(final Marker marker, final LatLng toPosition,
                              final boolean hideMarker) {
        if(currentLocationMarker == null)
            return;

        final Handler handler = new Handler();
        final long start = SystemClock.uptimeMillis();
        Projection proj = map.getProjection();
        Point startPoint = proj.toScreenLocation(marker.getPosition());
        final LatLng startLatLng = proj.fromScreenLocation(startPoint);
        final long duration = 250;

        final LinearInterpolator interpolator = new LinearInterpolator();

        handler.post(new Runnable() {
            @Override
            public void run() {
                long elapsed = SystemClock.uptimeMillis() - start;
                float t = interpolator.getInterpolation((float) elapsed
                        / duration);
//                double lng = t * toPosition.longitude + (1 - t)
//                        * startLatLng.longitude;
//                double lat = t * toPosition.latitude + (1 - t)
//                        * startLatLng.latitude;
                marker.setPosition(new LatLng(toPosition.latitude, toPosition.longitude));

//                if (t < 1.0) {
//                    // Post again 16ms later.
//                    handler.postDelayed(this, 16);
//                } else {
//                    if (hideMarker) {
//                        marker.setVisible(false);
//                    } else {
//                        marker.setVisible(true);
//                    }
//                }
            }
        });
    }
  • this is not a marker in ola app they set a image view on the center of map in xml than get the map center lat lng. so when ever the scroll the mpa marker still at the center and with smooth scrool . – Amit pandey Nov 20 '20 at 08:08
  • @Amitpandey I don't feel that it is an imageview fixed at the center coz when you open the Ola app then I see it moving and fixing to the centre of the map – Pratishtha Sharma Nov 20 '20 at 09:34
  • in that case you can use animation smoth moving the marker from one postion to another postion so when ever you location is moved smooth with out flick – Amit pandey Nov 20 '20 at 10:26
  • @Amitpandey I have used the animation, it is there in the code but that is not working – Pratishtha Sharma Nov 20 '20 at 12:35
  • have you try this one https://stackoverflow.com/questions/13872803/how-to-animate-marker-in-android-map-api-v2 – Amit pandey Nov 20 '20 at 13:22
  • @Amitpandey I tried that but it is flickering so much. – Pratishtha Sharma Nov 22 '20 at 09:45

0 Answers0