0

I'm using google maps, and adding markers and clusters. What i want to happen is, when the user clicks either an single item or a cluster the information contained inside the mapItem object should be shown in a viewpager at the buttom of the screen. I can get the infomation down there but only on the second click of the map icon. Is there a way to make this update happen at the first click?

I have been looking at Textview is null on first click but updates on second click, but that solution didn't help me since the info is static from the fragment itself.

here is the code for my main activity:

 package com.example.cpj.maptesting;

import android.Manifest;
import android.app.AlertDialog;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.provider.Settings;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.places.Place;
import com.google.android.gms.location.places.ui.PlaceAutocompleteFragment;
import com.google.android.gms.location.places.ui.PlaceSelectionListener;
import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
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.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.maps.android.clustering.Cluster;
import com.google.maps.android.clustering.ClusterManager;
import java.util.ArrayList;

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback, LocationListener{

    private GoogleMap mMap;
    private static ClusterManager<MapItem> itemClusterManager;
    private static ArrayList<MapItem> listOfMarkers = new ArrayList<>();
    private ArrayList<MapItem> listForFragments = new ArrayList<>();
    private LocationManager locationManager;
    PlaceAutocompleteFragment placeAutocompleteFragment;
    Marker marker;
    private Context context;
    Location location;
    ViewPager mViewpager;
    InfoPageAdapter mPagerAdapter;
    int counter = 0;
    SparseArray<Fragment> fragments = new SparseArray<>();


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);
        gpsPermission();
                // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        placeAutocompleteFragment = (PlaceAutocompleteFragment) getFragmentManager().findFragmentById(R.id.place_autocomplete);
        context = getApplicationContext();
        setUpPagers();

        if(!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)){
            alertBuilder();
        }
        placeAutocompleteFragment.setOnPlaceSelectedListener(new PlaceSelectionListener() {
            @Override
            public void onPlaceSelected(Place place) {
                if(marker != null){
                    marker.remove();
                }
                    moveCamera(place.getLatLng());
                    marker = mMap.addMarker(new MarkerOptions()
                            .position(place.getLatLng())
                            .title("" + place.getAddress())
                            .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN)));
            }

            @Override
            public void onError(Status status) {
                Log.d("Error status: " , status.toString());

            }
        });
    }

    /**
     * Manipulates the map once available.
     * This callback is triggered when the map is ready to be used.
     * This is where we can add markers or lines, add listeners or move the camera. In this case,
     * we just add a marker near Sydney, Australia.
     * If Google Play services is not installed on the device, the user will be prompted to install
     * it inside the SupportMapFragment. This method will only be triggered once the user has
     * installed Google Play services and returned to the app.
     */
    @Override
    public void onMapReady(final GoogleMap googleMap) {
        mMap = googleMap;
        setUpMap();

        if(ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED){
            mMap.setMyLocationEnabled(true);
            location = locationManager.getLastKnownLocation(locationManager.getBestProvider(new Criteria(), false));

            if(location != null){
                moveCamera(new LatLng(location.getLatitude(), location.getLongitude()));
            }
        }
        mMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() {
            @Override
            public void onCameraMove() {
                new dynamicallyAddMarkersTask().execute(mMap.getProjection().getVisibleRegion().latLngBounds);
            }
        });
        mMap.setOnMyLocationButtonClickListener(new GoogleMap.OnMyLocationButtonClickListener() {
            @Override
            public boolean onMyLocationButtonClick() {
                if(!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)){
                    alertBuilder();
                }
                return false;
            }
        });
            setUpClusters();
    }

    @Override
    public void onBackPressed() {
        if (mViewpager.getCurrentItem() == 0) {
            // If the user is currently looking at the first step, allow the system to handle the
            // Back button. This calls finish() on this activity and pops the back stack.
            super.onBackPressed();
        } else {
            // Otherwise, select the previous step.
            mViewpager.setCurrentItem(mViewpager.getCurrentItem() - 1);
        }
    }

    private void setUpClusters(){
        itemClusterManager = new ClusterManager<>(this, getMap());
        itemClusterManager.setAnimation(false);
        itemClusterManager.clearItems();
        listOfMarkers.clear();

        getMap().setOnCameraIdleListener(itemClusterManager);
        getMap().setOnMarkerClickListener(itemClusterManager);

        itemClusterManager.setOnClusterClickListener(new ClusterManager.OnClusterClickListener<MapItem>() {
            @Override
            public boolean onClusterClick(Cluster<MapItem> cluster) {
                setupClusterClicked(cluster);
                listForFragments.addAll(cluster.getItems());
                mViewpager.setCurrentItem(0);
                infoPageFragment ipf = (infoPageFragment) mPagerAdapter.getFragments(0);
                if(ipf != null){
                    ipf.setAdresse("Adresse: " + cluster.getItems().iterator().next().getTitle());
                    ipf.setNummer(1 + "/" + listForFragments.size());
                }

                return false;
            }
        });

        itemClusterManager.setOnClusterItemClickListener(new ClusterManager.OnClusterItemClickListener<MapItem>() {
            @Override
            public boolean onClusterItemClick(MapItem mapItem) {
                setupClusterItem();
                mViewpager.setCurrentItem(0);
                int position = mViewpager.getCurrentItem();

                infoPageFragment ipf = (infoPageFragment) mPagerAdapter.getFragments(position);
                if(ipf != null){
                    ipf.setAdresse("Adresse: " + mapItem.getTitle());
                    ipf.setNummer("");
                }

                return false;
            }
        });

        mMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
            @Override
            public void onMapClick(LatLng latLng) {
                mViewpager.setVisibility(View.GONE);
            }
        });
        itemClusterManager.setRenderer(new CustomIcon(this, mMap, itemClusterManager));

        addMapItem(new MapItem(56.162939, 10.203727, "Nabo", "Holde"));
        addMapItem(new MapItem(56.162939, 10.203921, "Harald", "Plads"));
        addMapItem(new MapItem(56.162939, 10.203921, "Jensen", "Plads1"));
        addMapItem(new MapItem(56.162939, 10.203921, "Mogens", "Plads2"));
        addMapItem(new MapItem(56.162939, 10.203921, "Jørgensen", "Plads3"));
        addMapItem(new MapItem(56.162939, 10.203921, "Hansen", "Plads4"));

        mViewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}

            @Override
            public void onPageSelected(int position) {
                infoPageFragment ipf;

                for(MapItem mapItem : listForFragments){
                    ipf = (infoPageFragment) mPagerAdapter.getFragments(position);

                    mapItem = listForFragments.get(position);
                    int increment = position+1;

                    if(ipf != null){
                        String text = increment + "/" + listForFragments.size();
                        ipf.setAdresse("Adresse: " + mapItem.getTitle());
                        ipf.setNummer(text);
                    }
                }
            }
            @Override
            public void onPageScrollStateChanged(int state) {}
        });
        mPagerAdapter.notifyDataSetChanged();
    }

    @Override
    public void onLocationChanged(Location location) {
        LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
        moveCamera(latLng);
        locationManager.removeUpdates(this);
    }

    @Override
    public void onStatusChanged(String s, int i, Bundle bundle) {}

    @Override
    public void onProviderEnabled(String s) {}

    @Override
    public void onProviderDisabled(String s) {}

    private GoogleMap getMap() {
        return mMap;
    }

    private void setUpMap(){
        mMap.getUiSettings().setTiltGesturesEnabled(false);
        mMap.getUiSettings().setRotateGesturesEnabled(false);
        mMap.getUiSettings().setMapToolbarEnabled(false);
    }

    private void gpsPermission(){
        ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
    }

    private void alertBuilder(){
        final AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);

        alertDialog.setTitle("GPS setting");
        alertDialog.setMessage("This app needs gps, to find the closet parking spots! \nGo to settings?");

        alertDialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                context.startActivity(intent);
            }
        });
        alertDialog.setNegativeButton("Not now", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {}
        });

        AlertDialog alert = alertDialog.create();
        alert.show();
    }

    private void moveCamera(LatLng latLng){
        CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(latLng, 16);
        mMap.animateCamera(cameraUpdate);
    }

    private void addMapItem(MapItem item){
        listOfMarkers.add(item);
        itemClusterManager.addItem(item);
    }

    private void setupClusterItem(){
        counter = 1;
        mViewpager.setVisibility(View.VISIBLE);
        mPagerAdapter.notifyDataSetChanged();
    }

    private void setupClusterClicked(Cluster<MapItem> cluster){
        listForFragments.clear();
        mViewpager.setVisibility(View.VISIBLE);
        counter = cluster.getSize();
        mPagerAdapter.notifyDataSetChanged();
    }

    private void setUpPagers(){
        mViewpager = findViewById(R.id.viewPager);
        mPagerAdapter = new InfoPageAdapter(getSupportFragmentManager());
        mViewpager.setAdapter(mPagerAdapter);
    }

    private static class dynamicallyAddMarkersTask extends AsyncTask<LatLngBounds, Void, Void> {

        @Override
        protected Void doInBackground(LatLngBounds... bounds) {
            itemClusterManager.clearItems();

            for(MapItem mapItem: listOfMarkers){
                if(bounds[0].contains(mapItem.getPosition())){
                    itemClusterManager.addItem(mapItem);
                }
            }
            return null;
        }

        @Override
        protected void onPostExecute(Void result){
            itemClusterManager.cluster();
        }
    }

    private class InfoPageAdapter extends FragmentStatePagerAdapter{

        public InfoPageAdapter(android.support.v4.app.FragmentManager fm){
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {

            return new infoPageFragment();
        }

        @Override
        public int getItemPosition(Object obj){
            return POSITION_NONE;
        }

        @Override
        public Object instantiateItem(ViewGroup viewGroup, int position){
            Fragment fragment = (Fragment) super.instantiateItem(viewGroup, position);
            fragments.put(position, fragment);

            return fragment;
        }

        @Override
        public void destroyItem(ViewGroup viewGroup, int position, Object object){
            fragments.remove(position);
            super.destroyItem(viewGroup, position, object);
        }

        public Fragment getFragments(int position){
            return fragments.get(position);
        }

        @Override
        public int getCount() {
            return counter;
        }
    }
}

This is the fragment

package com.example.cpj.maptesting;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class infoPageFragment extends Fragment {
    TextView adresse, nummer;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);
        View rootView = inflater.inflate(R.layout.info_page, container, false);
        adresse = rootView.findViewById(R.id.adresse);
        nummer = rootView.findViewById(R.id.nummer);
        return rootView;
    }

    public void setAdresse(String value){
        adresse.setText(value);
    }

    public void setNummer(String value){
        nummer.setText(value);
    }
}

activity_maps.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:weightSum="10"
    tools:context=".MapsActivity">

    <fragment
        android:id="@+id/place_autocomplete"
        android:name="com.google.android.gms.location.places.ui.PlaceAutocompleteFragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <fragment
        android:id="@+id/map"
        android:name="com.google.android.gms.maps.SupportMapFragment"
        android:layout_width="match_parent"
        android:layout_height="633dp" />

    <LinearLayout
        android:id="@+id/pagerLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:visibility="visible"
        android:weightSum="1"
        >

        <android.support.v4.view.ViewPager
            android:id="@+id/viewPager"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:visibility="gone"/>
    </LinearLayout>

</LinearLayout>

and the info_page.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/info_page"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView style="?android:textAppearanceMedium"
        android:padding="16dp"
        android:id="@+id/adresse"
        android:lineSpacingMultiplier="1.2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Adresse: "/>

    <TextView style="?android:textAppearanceMedium"
        android:id="@+id/nummer"
        android:padding="16dp"
        android:lineSpacingMultiplier="1.2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="right"
        />

</LinearLayout>

I realise this has something to do with fragment lifecycle but I just can't seem to figure out what the last bit I'm missing is.

Thanks for the answers.

ps. Please bear with me on the sloppy code I'm still quite the rookie when it comes to android development :).

w00lster
  • 23
  • 5
  • You don't get your first click? Are you sure that in methods `onClusterClick` & `onClusterItemClick` you need to return false? – Vadim Eksler Aug 29 '18 at 07:39
  • @VadimEksler Well the first click i make opens the fragment and shows the layout on the bottom, but the info from the item is not added before after the second click. The textview is null the first time around. Whether or not I have to return false i don't know but i get get info I want, so I think that part is correct. – w00lster Aug 29 '18 at 07:53
  • Try to add log in your method `setAdresse()` with value? did you see it in the log? – Vadim Eksler Aug 29 '18 at 07:58
  • If i call it outside of the if(ipf != null) I get a null pointer, but if it gets into the if statement then it gets the value fine, which is why I added that little check, becuase the textview is null on the first click. – w00lster Aug 29 '18 at 08:02
  • Ok, so try to check with log what exactly you get first time in method `getFragments(int position)`. Put your log in this method and inside `instantiateItem()` if there exist some fragment when your make first click. – Vadim Eksler Aug 29 '18 at 10:38
  • In the getFragments(int position) the log says: 08-29 12:49:03.114 24030-24030/com.example.cpj.maptesting D/Stack: getFragments: null, While in intantiateItem() is says 08-29 12:49:03.127 24030-24030/com.example.cpj.maptesting D/Stack: instantiateItem: infoPageFragment{5fba77e id=0x7f07009d}. – w00lster Aug 29 '18 at 10:51

1 Answers1

0

So, as you can see the first time your fragments array is empty when you try to get data. It feels after you get request. You need to make sure that instantiateItem() finished his update like isViewFromObject and make yours getFragments(int position) after it. Look at the docs - https://developer.android.com/reference/androidx/viewpager/widget/PagerAdapter.html#isViewFromObject(android.view.View,%20java.lang.Object)

Vadim Eksler
  • 865
  • 9
  • 24