1
votes

I am trying to draw a polyline on a map

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

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

public class MainActivity extends AppCompatActivity implements OnMapReadyCallback, SeekBar.OnSeekBarChangeListener { //Initialise variables

GoogleMap gMap;
SeekBar seekWidth, seekBlue, seekGreen, seekRed;
Button btClear, btDraw;

Polyline polyline = null;
List<LatLng> latLngList = new ArrayList<>();
List<Marker> markerList = new ArrayList<>();

PolylineOptions polylineOptions = null;

int red = 0, green= 0, blue=0;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);


    //Assign variable
    seekWidth = findViewById(R.id.seek_width);
    seekRed = findViewById(R.id.seek_red);
    seekGreen = findViewById(R.id.seek_green);
    seekBlue = findViewById(R.id.seek_blue);
    btDraw=findViewById(R.id.bt_draw);
    btClear=findViewById(R.id.bt_clear);

    /*Initialise SupportMapFragment
    SupportMapFragment supportMapFragment = (SupportMapFragment)getSupportFragmentManager()
            .findFragmentById(R.id.google_map);
    supportMapFragment.getMapAsync(this);

    btDraw.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            //Draw Polyline on Map
            if (polyline!=null) polyline.remove();
            //Create PolylineOptions
            PolylineOptions polylineOptions = new PolylineOptions()
                    .addAll(latLngList).clickable(true);
            polyline = gMap.addPolyline(polylineOptions);

            setWidth();

        }
    });*/

    //Initialise SupportMapFragment
    SupportMapFragment supportMapFragment = (SupportMapFragment)getSupportFragmentManager()
            .findFragmentById(R.id.google_map);
    supportMapFragment.getMapAsync(this);

    btDraw.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            //Draw Polyline on Map
            if (polyline!=null) polyline.remove();
            if(polylineOptions!=null){
                //Create PolylineOptions
                polyline = gMap.addPolyline(polylineOptions);

                setWidth();
            }
        }
    });

    btClear.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //Clear All
            if (polyline!=null) polyline.remove();
            for(Marker marker: markerList) marker.remove();
            latLngList.clear();
            markerList.clear();
            seekWidth.setProgress(3);
            seekBlue.setProgress(0);
            seekGreen.setProgress(0);
            seekRed.setProgress(0);

        }
    });

    seekRed.setOnSeekBarChangeListener(this);
    seekGreen.setOnSeekBarChangeListener(this);
    seekBlue.setOnSeekBarChangeListener(this);


}

private void setWidth() {
    seekWidth.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            //Get Seekbar Progress
            int width = seekWidth.getProgress();
            if (polyline != null) {
                //Set Polyline Width
                polyline.setWidth(width);
            }
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {

        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {

        }
    });
}

/*@Override
public void onMapReady(GoogleMap googleMap) {
    /*gMap= googleMap;
    gMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
        @Override
        public void onMapClick(LatLng latLng) {
            //Create MarkerOptions
            MarkerOptions markerOptions = new MarkerOptions().position(latLng);
            //Create Marker
            Marker marker = gMap.addMarker(markerOptions);
            //Add Latlng and Marker
            latLngList.add(latLng);
            markerList.add(marker);
        }
    });

}*/

    public void onMapReady(GoogleMap googleMap) {
        gMap= googleMap;
        gMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
            @Override
            public void onMapClick(LatLng latLng) {
                //Create MarkerOptions
                MarkerOptions markerOptions = new MarkerOptions().position(latLng);
                //Create Marker
                Marker marker = gMap.addMarker(markerOptions);
                //Add Latlng and Marker
                latLngList.add(latLng);
                markerList.add(marker);

                polylineOptions = new PolylineOptions().addAll(latLngList).clickable(true);
            }
        });
    }

@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
    switch (seekBar.getId()){
        case R.id.seek_red:
            red = i;
            break;
        case R.id.seek_green:
            green = i;
            break;
        case R.id.seek_blue:
            blue= i;
            break;

    }
    if(polyline !=null){
    //Set Polyline Color
        polyline.setColor(Color.rgb(red,green,blue));
    }


}

@Override
public void onStartTrackingTouch(SeekBar seekBar) {

}

@Override
public void onStopTrackingTouch(SeekBar seekBar) {

}

}

THE ERROR IS AS FOLLOWS:

2019-11-29 13:39:40.391 20694-20694/com.example.poly E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.poly, PID: 20694 java.lang.NullPointerException: Attempt to invoke virtual method 'void com.google.android.gms.maps.model.Polyline.setColor(int)' on a null object reference at com.example.poly.MainActivity.onProgressChanged(MainActivity.java:149) at android.widget.SeekBar.onProgressRefresh(SeekBar.java:93) at android.widget.ProgressBar.doRefreshProgress(ProgressBar.java:1327) at android.widget.ProgressBar.refreshProgress(ProgressBar.java:1382) at android.widget.ProgressBar.setProgressInternal(ProgressBar.java:1447) at android.widget.AbsSeekBar.trackTouchEvent(AbsSeekBar.java:850) at android.widget.AbsSeekBar.onTouchEvent(AbsSeekBar.java:760) at android.view.View.dispatchTouchEvent(View.java:9943) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2669) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2358) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2669) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2358) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2669) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2358) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2669) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2358) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2669) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2358) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2669) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2358) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2669) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2358) at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:411) at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1810) at android.app.Activity.dispatchTouchEvent(Activity.java:3061) at androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:69) at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:373) at android.view.View.dispatchPointerEvent(View.java:10163) at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4434) at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4302) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3849) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3902) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3868) at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3995) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3876) at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4052) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3849) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3902) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3868) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3876) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3849) at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6210) at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6184) at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6145) at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6313) at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185) at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native Method) at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:176) at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:6284) at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:6336)

1
Your question title is very bad and the issue you're having has to do with google map Polyline and I can only guess that your map is not yet initialized and you're trying to make use of it, pls correct your title.Wale
Thank you for the suggestion. I made changes to the title. Could you tell me how to go about correcting it? I am a beginner with Android Studio.user12456799
@Daiel Ravichandran I just posted an answer, check it out, it should resolve the crash, if it does, kindly mark as answer.Wale
Your polyline is always null... What examples are you following? Where do they define it?OneCricketeer
@Whales Thank you for helping out. I made the changes as suggested by you. The app works on the emulator now but crashes when I install in it on my phone. The same error on logcat is still showing up.user12456799

1 Answers

1
votes

This might actually serve as a temporary fix or permanent depending on the root cause of why polyline is still null, but below should fix your crash.

Note: I used commenting to explain every aspect of the code below.

.

First thing is to change below snippet:

if(polyline!=null) /*Note that without the curly brace, the if statement will only be applied to the immediate code below, so in your case, polyline is still also null here as the if statement only applies to the comment which will be ignored by the compiler. */ 
  //Set Polyline Width
  polyline.setWidth(width);

To below:

if(polyline!=null){ /*you should try and always use your opening and closing brace for readability. */
    //Set Polyline Width
    polyline.setWidth(width);
}

Secondly which is the main cause of your crash:

//Set Polyline Color
polyline.setColor(Color.rgb(red,green,blue)); /* you for got to place a check here which is why you get the crash because as at here, *polyline* is still null for some reasons. */

So change the immediate above snippet to the below:

if(polyline !=null){
//Set Polyline Color
polyline.setColor(Color.rgb(red,green,blue));
}

In addition to this as noted by @cricket_007:

Just like you have Polyline polyline = null; above, add also this: PolylineOptions polylineOptions = null;

Follow by modifying your SupportMapFragment like below:

    //Initialise SupportMapFragment
            SupportMapFragment supportMapFragment = (SupportMapFragment)getSupportFragmentManager()
                    .findFragmentById(R.id.google_map);
            supportMapFragment.getMapAsync(this);

            btDraw.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    //Draw Polyline on Map
                    if (polyline!=null) polyline.remove();
                       if(polylineOptions!=null){
                          //Create PolylineOptions
                    polyline = gMap.addPolyline(polylineOptions);

                    setWidth();
                   }   
                }
            });

Then here:

    @Override
    public void onMapReady(GoogleMap googleMap) {
        gMap= googleMap;
        gMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
            @Override
            public void onMapClick(LatLng latLng) {
                //Create MarkerOptions
                MarkerOptions markerOptions = new MarkerOptions().position(latLng);
                //Create Marker
                Marker marker = gMap.addMarker(markerOptions);
                //Add Latlng and Marker
                latLngList.add(latLng);
                markerList.add(marker);

                polylineOptions = new PolylineOptions().addAll(latLngList).clickable(true);
            }
        });
    }

Since you're new to android, if you're finding it difficult to locate where those code above are in your code, simple copy for example polyline.setColor(Color.rgb(red,green,blue)); and navigate to Edit in your AndroidStudio and navigate to find, paste it there to help you find the exact location of the code. Good luck coding.

...

NEW ANSWER:

Below is the full java class including all the imports needed, simply copy and replace as it's same package name and everything else are the same.

package com.example.poly;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.SeekBar;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity implements OnMapReadyCallback, SeekBar.OnSeekBarChangeListener {
    //Initialise variables
    GoogleMap gMap;
    SeekBar seekWidth, seekBlue, seekGreen, seekRed;
    Button btClear, btDraw;

    List<LatLng> latLngList = new ArrayList<>();
    List<Marker> markerList = new ArrayList<>();
    int red = 0, green= 0, blue=0;

    Polyline polyline = null;
    PolylineOptions polylineOptions = null;

    private static int PERMISSION_LOCATION_STATE = 1001; //hopefully no other permission code conflict with this in the future, so watch out.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //Assign variable
        seekWidth = findViewById(R.id.seek_width);
        seekRed = findViewById(R.id.seek_red);
        seekGreen = findViewById(R.id.seek_green);
        seekBlue = findViewById(R.id.seek_blue);
        btDraw=findViewById(R.id.bt_draw);
        btClear=findViewById(R.id.bt_clear);

        /* We decide to put our function inside a runner that runs on UI thread to at least try to
         * load the page before prompting users to allow location access, this will delay the prompt by 2 seconds.
         * Note: it's actually not compulsory, so you can remove it either way, it will work.
         * */
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                mapPermissionCheck();
            }
        }, 2000);

        btDraw.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //Draw Polyline on Map
                if (polyline!=null) polyline.remove();
                if(polylineOptions!=null){
                    //Create PolylineOptions
                    polyline = gMap.addPolyline(polylineOptions);
                    setWidth();
                }
            }
        });

        btClear.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //Clear All
                if (polyline!=null) polyline.remove();
                for(Marker marker: markerList) marker.remove();
                latLngList.clear();
                markerList.clear();
                seekWidth.setProgress(3);
                seekBlue.setProgress(0);
                seekGreen.setProgress(0);
                seekRed.setProgress(0);
            }
        });

        seekRed.setOnSeekBarChangeListener(this);
        seekGreen.setOnSeekBarChangeListener(this);
        seekBlue.setOnSeekBarChangeListener(this);
    }

    private void initSupportFragment() {
        SupportMapFragment supportMapFragment = (SupportMapFragment)getSupportFragmentManager().findFragmentById(R.id.google_map);
        if(supportMapFragment!=null){
            supportMapFragment.getMapAsync(this);
        }
    }

    private void mapPermissionCheck() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
                == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            //Initialise SupportMapFragment when we're sure permission is granted to access location, else Map will not be ready and mapReady() will never be called.
            initSupportFragment();
        }else{
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSION_LOCATION_STATE);
        }
    }

    private void setWidth() {
        seekWidth.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                //Get Seekbar Progress
                int width = seekWidth.getProgress();
                if (polyline != null) {
                    //Set Polyline Width
                    polyline.setWidth(width);
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
            gMap= googleMap;
            gMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
                @Override
                public void onMapClick(LatLng latLng) {
                    //Create MarkerOptions
                    MarkerOptions markerOptions = new MarkerOptions().position(latLng);
                    //Create Marker
                    Marker marker = gMap.addMarker(markerOptions);
                    //Add Latlng and Marker
                    latLngList.add(latLng);
                    markerList.add(marker);

                    polylineOptions = new PolylineOptions().addAll(latLngList).clickable(true);
                }
            });
        }

    @Override
    public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
        switch (seekBar.getId()){
            case R.id.seek_red:
                red = i;
                break;
            case R.id.seek_green:
                green = i;
                break;
            case R.id.seek_blue:
                blue= i;
                break;
        }

        if(polyline !=null){
        //Set Polyline Color
            polyline.setColor(Color.rgb(red,green,blue));
        }
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {

    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {

    }

    /*
    * Right below here is a permission callback method that get called when ever an action is taken on a permission prompt request
    * So, if a user declined or accept, we can check and decide what to do next, in this case, we will try enforce the user to
    * give us location access in case they declined at first since we can't do much without the permission.
    * */
    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == PERMISSION_LOCATION_STATE) { // remember PERMISSION_LOCATION_STATE? check above on how we parse it.
                if (grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED) {
                    new AlertDialog.Builder(this)
                        .setTitle("Location Permission Needed")
                        .setMessage("Daniel Poly App Need Your Location Permission To Proceed.")
                        .setCancelable(false)
                        .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                dialog.cancel();
                                mapPermissionCheck();
                            }
                        })
                        .show();
                }else{ //Meaning permission finally granted....hurray!!!
                    initSupportFragment();
                }
        }
    }

}

In conclusion, what you missed is the permission. You should read this for more info.