Search code examples
javaandroidnullpointerexception

Polylines is crashing on emulator


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)


Solution

  • 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.