安卓地图:如何在地图上动画显示折线?

4

当我在地图上绘制从点A到点B的折线时,我需要以动画方式绘制折线。就像从A到B一样,折线不断绘制。

我已经使用了以下链接作为参考:

https://github.com/amalChandran/google-maps-route-animation

使用这个解决方案,我可以动画化折线,但折线本身不正确。它没有沿着道路走。原始APK也有同样的错误。
请问有人能帮我找到适当的解决方案吗?

你好。你有解决这个问题的方法吗?因为我也遇到了同样的问题。 - Rissmon Suresh
4个回答

6
您可以尝试使用以下参考链接:https://github.com/mohak1712/UberUX?utm_source=android-arsenal.com&utm_medium=referral&utm_campaign=6129
ValueAnimator - 用于动画叠加和折线。
ValueAnimator tAnimator = ValueAnimator.ofFloat(0, 1);
       tAnimator.setRepeatCount(ValueAnimator.INFINITE);
       tAnimator.setRepeatMode(ValueAnimator.RESTART);
       tAnimator.setInterpolator(new LinearInterpolator());
       tAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
           @Override
           public void onAnimationUpdate(ValueAnimator valueAnimator) {
              // animate here
           }
       });

PolyLines - 用于在地图上绘制线条

 PolylineOptions greyOptions = new PolylineOptions();
        greyOptions.width(10);
        greyOptions.color(Color.GRAY);
        greyOptions.startCap(new SquareCap());
        greyOptions.endCap(new SquareCap());
        greyOptions.jointType(ROUND);
        greyPolyLine = mMap.addPolyline(greyOptions);

@Sayan Manna,你能帮忙吗?我不明白如何将ValueAnimator链接到你正在使用的PolylineOptions。你声明了一个简单的tAnimator,但是你如何在其他地方引用它呢?我想让我的折线像这样动画化:https://streamable.com/h569z - j2emanue
@j2emanue,以下链接可能会对您有所帮助:https://dev59.com/lKHia4cB1Zd3GeqPQ0bD - Sayan Manna
优秀的例子,虽然不涉及动画,但我真的很感谢这个链接。你为我产生了新的想法。 - j2emanue
@GowthamanM,你能在折线图中使用值动画器吗?怎么做? - j2emanue

2

在Google控制台中启用Direction API。在strings.xml中替换google_api_key_app

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Color;
import android.os.AsyncTask;
import android.util.Log;
import android.view.animation.LinearInterpolator;

import androidx.appcompat.app.AppCompatActivity;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;
import com.google.android.gms.maps.model.SquareCap;

import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;

import static com.google.android.gms.maps.model.JointType.ROUND;

public class GetPathFromLocation extends AsyncTask<String, Void, PolylineOptions> {

    private Context context;
    private String TAG = "GetPathFromLocation";
    private LatLng source, destination;
    private ArrayList<LatLng> wayPoint;
    private GoogleMap mMap;
    private boolean animatePath, repeatDrawingPath;
    private DirectionPointListener resultCallback;

    //https://www.mytrendin.com/draw-route-two-locations-google-maps-android/
    //https://www.androidtutorialpoint.com/intermediate/google-maps-draw-path-two-points-using-google-directions-google-map-android-api-v2/

    public GetPathFromLocation(Context context, LatLng source, LatLng destination, ArrayList<LatLng> wayPoint, GoogleMap mMap, boolean animatePath, boolean repeatDrawingPath, DirectionPointListener resultCallback) {
        this.context = context;
        this.source = source;
        this.destination = destination;
        this.wayPoint = wayPoint;
        this.mMap = mMap;
        this.animatePath = animatePath;
        this.repeatDrawingPath = repeatDrawingPath;
        this.resultCallback = resultCallback;
    }

    synchronized public String getUrl(LatLng source, LatLng dest, ArrayList<LatLng> wayPoint) {

        String url = "https://maps.googleapis.com/maps/api/directions/json?sensor=false&mode=driving&origin="
                + source.latitude + "," + source.longitude + "&destination=" + dest.latitude + "," + dest.longitude;
        for (int centerPoint = 0; centerPoint < wayPoint.size(); centerPoint++) {
            if (centerPoint == 0) {
                url = url + "&waypoints=optimize:true|" + wayPoint.get(centerPoint).latitude + "," + wayPoint.get(centerPoint).longitude;
            } else {
                url = url + "|" + wayPoint.get(centerPoint).latitude + "," + wayPoint.get(centerPoint).longitude;
            }
        }
        url = url + "&key=" + context.getString(R.string.google_api_key_app);

        Helper.showLog("Direction_URL: " + url);

        return url;
    }

    public int getRandomColor() {
        Random rnd = new Random();
        return Color.argb(255, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256));
    }

    @Override
    protected PolylineOptions doInBackground(String... url) {

        String data;

        try {
            InputStream inputStream = null;
            HttpURLConnection connection = null;
            try {
                URL directionUrl = new URL(getUrl(source, destination, wayPoint));
                connection = (HttpURLConnection) directionUrl.openConnection();
                connection.connect();
                inputStream = connection.getInputStream();

                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                StringBuffer stringBuffer = new StringBuffer();

                String line = "";
                while ((line = bufferedReader.readLine()) != null) {
                    stringBuffer.append(line);
                }

                data = stringBuffer.toString();
                bufferedReader.close();

            } catch (Exception e) {
                Log.e(TAG, "Exception : " + e.toString());
                return null;
            } finally {
                inputStream.close();
                connection.disconnect();
            }
            Log.e(TAG, "Background Task data : " + data);

            //Second AsyncTask

            JSONObject jsonObject;
            List<List<HashMap<String, String>>> routes = null;

            try {
                jsonObject = new JSONObject(data);
                // Starts parsing data
                DirectionHelper helper = new DirectionHelper();
                routes = helper.parse(jsonObject);
                Log.e(TAG, "Executing Routes : "/*, routes.toString()*/);

                //Third AsyncTask

                ArrayList<LatLng> points;
                PolylineOptions lineOptions = null;

                // Traversing through all the routes
                for (int i = 0; i < routes.size(); i++) {
                    points = new ArrayList<>();
                    lineOptions = new PolylineOptions();

                    // Fetching i-th route
                    List<HashMap<String, String>> path = routes.get(i);

                    // Fetching all the points in i-th route
                    for (int j = 0; j < path.size(); j++) {
                        HashMap<String, String> point = path.get(j);

                        double lat = Double.parseDouble(point.get("lat"));
                        double lng = Double.parseDouble(point.get("lng"));
                        LatLng position = new LatLng(lat, lng);

                        points.add(position);
                    }

                    // Adding all the points in the route to LineOptions
                    lineOptions.addAll(points);
                    lineOptions.width(8);
                    lineOptions.color(Color.BLACK);
                    //lineOptions.color(getRandomColor());

                    if (animatePath) {
                        final ArrayList<LatLng> finalPoints = points;
                        ((AppCompatActivity) context).runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                PolylineOptions polylineOptions;
                                final Polyline greyPolyLine, blackPolyline;
                                final ValueAnimator polylineAnimator;

                                LatLngBounds.Builder builder = new LatLngBounds.Builder();
                                for (LatLng latLng : finalPoints) {
                                    builder.include(latLng);
                                }
                                polylineOptions = new PolylineOptions();
                                polylineOptions.color(Color.GRAY);
                                polylineOptions.width(8);
                                polylineOptions.startCap(new SquareCap());
                                polylineOptions.endCap(new SquareCap());
                                polylineOptions.jointType(ROUND);
                                polylineOptions.addAll(finalPoints);
                                greyPolyLine = mMap.addPolyline(polylineOptions);

                                polylineOptions = new PolylineOptions();
                                polylineOptions.width(8);
                                polylineOptions.color(Color.BLACK);
                                polylineOptions.startCap(new SquareCap());
                                polylineOptions.endCap(new SquareCap());
                                polylineOptions.zIndex(5f);
                                polylineOptions.jointType(ROUND);

                                blackPolyline = mMap.addPolyline(polylineOptions);
                                polylineAnimator = ValueAnimator.ofInt(0, 100);
                                polylineAnimator.setDuration(2000);
                                polylineAnimator.setInterpolator(new LinearInterpolator());
                                polylineAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                                    @Override
                                    public void onAnimationUpdate(ValueAnimator valueAnimator) {
                                        List<LatLng> points = greyPolyLine.getPoints();
                                        int percentValue = (int) valueAnimator.getAnimatedValue();
                                        int size = points.size();
                                        int newPoints = (int) (size * (percentValue / 100.0f));
                                        List<LatLng> p = points.subList(0, newPoints);
                                        blackPolyline.setPoints(p);
                                    }
                                });

                                polylineAnimator.addListener(new Animator.AnimatorListener() {
                                    @Override
                                    public void onAnimationStart(Animator animation) {

                                    }

                                    @Override
                                    public void onAnimationEnd(Animator animation) {
                                        if (repeatDrawingPath) {
                                            List<LatLng> greyLatLng = greyPolyLine.getPoints();
                                            if (greyLatLng != null) {
                                                greyLatLng.clear();

                                            }
                                            polylineAnimator.start();
                                        }
                                    }

                                    @Override
                                    public void onAnimationCancel(Animator animation) {
                                        polylineAnimator.cancel();
                                    }

                                    @Override
                                    public void onAnimationRepeat(Animator animation) {
                                    }
                                });
                                polylineAnimator.start();
                            }
                        });
                    }

                    Log.e(TAG, "PolylineOptions Decoded");
                }

                // Drawing polyline in the Google Map for the i-th route
                if (lineOptions != null) {
                    return lineOptions;
                } else {
                    return null;
                }

            } catch (Exception e) {
                Log.e(TAG, "Exception in Executing Routes : " + e.toString());
                return null;
            }

        } catch (Exception e) {
            Log.e(TAG, "Background Task Exception : " + e.toString());
            return null;
        }
    }

    @Override
    protected void onPostExecute(PolylineOptions polylineOptions) {
        super.onPostExecute(polylineOptions);
        if (resultCallback != null && polylineOptions != null)
            resultCallback.onPath(polylineOptions);
    }
}

DirectionHelper.java

import com.google.android.gms.maps.model.LatLng;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class DirectionHelper {

    public List<List<HashMap<String, String>>> parse(JSONObject jObject) {

        List<List<HashMap<String, String>>> routes = new ArrayList<>();
        JSONArray jRoutes;
        JSONArray jLegs;
        JSONArray jSteps;

        try {

            jRoutes = jObject.getJSONArray("routes");

            /** Traversing all routes */
            for (int i = 0; i < jRoutes.length(); i++) {
                jLegs = ((JSONObject) jRoutes.get(i)).getJSONArray("legs");
                List path = new ArrayList<>();

                /** Traversing all legs */
                for (int j = 0; j < jLegs.length(); j++) {
                    jSteps = ((JSONObject) jLegs.get(j)).getJSONArray("steps");

                    /** Traversing all steps */
                    for (int k = 0; k < jSteps.length(); k++) {
                        String polyline = "";
                        polyline = (String) ((JSONObject) ((JSONObject) jSteps.get(k)).get("polyline")).get("points");
                        List<LatLng> list = decodePoly(polyline);

                        /** Traversing all points */
                        for (int l = 0; l < list.size(); l++) {
                            HashMap<String, String> hm = new HashMap<>();
                            hm.put("lat", Double.toString((list.get(l)).latitude));
                            hm.put("lng", Double.toString((list.get(l)).longitude));
                            path.add(hm);
                        }
                    }
                    routes.add(path);
                }
            }

        } catch (JSONException e) {
            e.printStackTrace();
        } catch (Exception e) {
        }


        return routes;
    }

    //Method to decode polyline points
    private List<LatLng> decodePoly(String encoded) {

        List<LatLng> poly = new ArrayList<>();
        int index = 0, len = encoded.length();
        int lat = 0, lng = 0;

        while (index < len) {
            int b, shift = 0, result = 0;
            do {
                b = encoded.charAt(index++) - 63;
                result |= (b & 0x1f) << shift;
                shift += 5;
            } while (b >= 0x20);
            int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lat += dlat;

            shift = 0;
            result = 0;
            do {
                b = encoded.charAt(index++) - 63;
                result |= (b & 0x1f) << shift;
                shift += 5;
            } while (b >= 0x20);
            int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lng += dlng;

            LatLng p = new LatLng((((double) lat / 1E5)),
                    (((double) lng / 1E5)));
            poly.add(p);
        }

        return poly;
    }
}

DirectionPointListener.java

import com.google.android.gms.maps.model.PolylineOptions;

public interface DirectionPointListener {
    public void onPath(PolylineOptions polyLine);
}

在Activity或Fragment中使用

ArrayList<LatLng> wayPoint = new ArrayList<>();
GoogleMap mMap;
LatLng source = new LatLng(xx.xxxx, yy.yyyy);
LatLng destination = new LatLng(xx.xxxx, yy.yyyy);

new GetPathFromLocation(context, source, destination, wayPoint, mMap, true, false, new DirectionPointListener() {
        @Override
        public void onPath(PolylineOptions polyLine) {
            mMap.addPolyline(polyLine);
        }
    }).execute();

strings.xml

<string name="google_api_key_app">Place_Your_API_Key</string>    

1
private void drawRoute()
    {
        mGoogleApiProvider.getDirections(mOrigenLatLng, mDestinoLatLng)
                .enqueue(new Callback<String>()
                         {
                             @Override
                             public void onResponse(Call<String> call, Response<String> response)
                             {
                                 try
                                 {
                                     JSONObject jsonObject = new JSONObject(response.body());
                                     JSONArray jsonArray = jsonObject.getJSONArray("routes");
                                     JSONObject route = jsonArray.getJSONObject(0);
                                     JSONObject polylines = route.getJSONObject("overview_polyline");
                                     String points = polylines.getString("points");
                                     mPolylineList = DecodificarPuntos.decodificarPoly(points);

                                     mPolylineOptions = new PolylineOptions();
                                     mPolylineOptions.width(10f);
                                     mPolylineOptions.color(Color.parseColor("#191a23"));
                                     mPolylineOptions.startCap(new SquareCap());
                                     mPolylineOptions.endCap(new SquareCap());
                                     mPolylineOptions.jointType(ROUND);

                                     mPolylineOptions.addAll(mPolylineList);

                                     blackPolyLine = mMap.addPolyline(mPolylineOptions);


                                     PolylineOptions greyOptions = new PolylineOptions();
                                     greyOptions.width(10f);
                                     greyOptions.color(Color.GRAY);
                                     greyOptions.startCap(new SquareCap());
                                     greyOptions.endCap(new SquareCap());
                                     greyOptions.jointType(ROUND);

                                     greyPolyLine = mMap.addPolyline(greyOptions);

                                     animatePolyLine();

                                 }
                                 catch(Exception e)
                                 {
                                     Log.e("Error: ", Objects.requireNonNull(e.getMessage()));
                                 }

                             }
                             @Override
                             public void onFailure(Call<String> call, Throwable t)
                             {

                             }
                         }
                );
    }

    private void animatePolyLine()
    {
        ValueAnimator animator = ValueAnimator.ofInt(0, 100);
        animator.setDuration(1000);
        animator.setInterpolator(new LinearInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animator) {

                List<LatLng> latLngList = blackPolyLine.getPoints();
                int initialPointSize = latLngList.size();
                int animatedValue = (int) animator.getAnimatedValue();
                int newPoints = (animatedValue * mPolylineList.size()) / 100;

                if (initialPointSize < newPoints ) {
                    latLngList.addAll(mPolylineList.subList(initialPointSize, newPoints));
                    blackPolyLine.setPoints(latLngList);
                }


            }
        });

        animator.addListener(polyLineAnimationListener);
        animator.start();
    }

    Animator.AnimatorListener polyLineAnimationListener = new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animator) {

            addMarker(mPolylineList.get(mPolylineList.size()-1));
        }

        @Override
        public void onAnimationEnd(Animator animator)
        {
            List<LatLng> blackLatLng = blackPolyLine.getPoints();
            List<LatLng> greyLatLng = greyPolyLine.getPoints();

            greyLatLng.clear();
            greyLatLng.addAll(blackLatLng);
            blackLatLng.clear();

            blackPolyLine.setPoints(blackLatLng);
            greyPolyLine.setPoints(greyLatLng);

            blackPolyLine.setZIndex(2);

            animator.start();
        }

        @Override
        public void onAnimationCancel(Animator animator)
        {
        }

        @Override
        public void onAnimationRepeat(Animator animator)
        {
        }
    };

    private void addMarker(LatLng destination) {

        MarkerOptions options = new MarkerOptions()
                .position(destination)
                .draggable(false)
                .flat(false)
                .icon(BitmapDescriptorFactory.fromBitmap(marcadorPersonalizadoDestino("Destino", mExtraDestino,
                        mExtraTiempo, mExtraTipoDeTiempo, mExtraDistancia, mExtraTipoDeDistancia)
                ));

        mMap.addMarker(options);

    }
    

它正在工作,我还没有添加addMarker(mPolylineList.get(mPolylineList.size()-1));这一行,问题是当我改变我的起点和终点时,仍然存在旧的折线。如何清除旧的折线路径? - Ashwin H
是的,我在添加了以下代码后得到了答案:如果(animator!= null){ animator.cancel(); animator.removeAllListeners(); } - Ashwin H

1
您也可以不使用折线绘制路线。使用谷歌地图投影API,您可以在覆盖层上绘制它。请查看repo以获取示例。

enter image description here


使用覆盖层可以绘制带有动画的路径吗? - BIS Tech
这个程序运行得很好,但是有一个问题,当你旋转地图时,它会导致弧线和投影移动到原本绘制的点之外。整个屏幕看起来很混乱。 - Attique Ur Rehman
当然,请给我几分钟。 - Attique Ur Rehman
@AttiqueUrRehman 这个问题已经在同一仓库的另一个分支中得到了解决。我会尝试在本周末发布一个包含此修复的新版本。 - amalBit
我们如何隐藏弧线的投影?可以添加这个功能吗? @amalBit,预计什么时候会在发布中可用? - Attique Ur Rehman
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接