Android : how to reload custom markers once the im

2019-01-25 19:57发布

问题:

I'm trying to load dynamically users avatars as custom markers. I based my code on the google maps utils demo, but somehow it doesn't work, it loads only one image and all the others are empty:

Here is my code:

public class MapsActivity extends FragmentActivity implements ClusterManager.OnClusterItemClickListener<ModelUser>, ClusterManager.OnClusterItemInfoWindowClickListener<ModelUser> {

    private ClusterManager<ModelUser> mClusterManager;
    private GoogleMap mMap; // Might be null if Google Play services APK is not available.
    private QueryAPI query = new QueryAPI();
    private List<ModelUser> users = new ArrayList<ModelUser>();
    ImageLoader imageLoader = AppController.getInstance().getImageLoader();

    //UI
    NetworkImageView avatarImageView;
    TextView name;
    TextView infos;
    TextView distance;
    RelativeLayout detailView;

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

        setUpMapIfNeeded();

        detailView = (RelativeLayout) findViewById(R.id.detailView);
        avatarImageView = (NetworkImageView) findViewById(R.id.imageView);
        name = (TextView) findViewById(R.id.name);
        infos = (TextView) findViewById(R.id.info);
        distance = (TextView) findViewById(R.id.distance);


        query.nearUsers(new QueryAPI.ApiResponse<List<ModelUser>>() {
            @Override
            public void onCompletion(List<ModelUser> result) {

                users = result;

                setUpClusterer();
            }
        });

    }

    @Override
    protected void onResume() {
        super.onResume();
        setUpMapIfNeeded();
    }


    private void setUpMap() {
        mMap.setMyLocationEnabled(true);
    }

    private void setUpClusterer() {

        // Position the map.
        getMap().moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(46.155115, 2.473060), 5));

        // Initialize the manager with the context and the map.
        // (Activity extends context, so we can pass 'this' in the constructor.)
        mClusterManager = new ClusterManager<ModelUser>(this, getMap());

        // Point the map's listeners at the listeners implemented by the cluster
        // manager.
        getMap().setOnCameraChangeListener(mClusterManager);
        getMap().setOnMarkerClickListener(mClusterManager);
        mClusterManager.setRenderer(new PersonRenderer());
        mClusterManager.setOnClusterItemClickListener(this);
        mClusterManager.setOnClusterItemInfoWindowClickListener(this);

        // Add cluster items (markers) to the cluster manager.
        addItems();
    }

    private void addItems() {

        for (int i = 0; i < users.size(); i++) {

            ModelUser user = users.get(i);

            mClusterManager.addItem(user);
        }

    }

    protected GoogleMap getMap() {
        setUpMapIfNeeded();
        return mMap;
    }

    @Override
    public boolean onClusterItemClick(ModelUser item) {

        Log.d("User clicked:", item.getName());
        avatarImageView.setImageUrl(item.getAvatar_url(), imageLoader);
        name.setText(item.getName());
        infos.setText(item.getAge());
        double d = Double.parseDouble(item.getDistance());
        distance.setText( String.format("%.1f", d) + "Km");
        detailView.setVisibility(View.VISIBLE);
        return false;
    }

    @Override
    public void onClusterItemInfoWindowClick(ModelUser item) {

        Log.d("User Window clicked:", item.getName());
    }

    /**
     * Draws profile photos inside markers (using IconGenerator).
     * When there are multiple people in the cluster, draw multiple photos (using MultiDrawable).
     */
    private class PersonRenderer extends DefaultClusterRenderer<ModelUser> {
        private final IconGenerator mIconGenerator = new IconGenerator(getApplicationContext());
        private final UICircularImage mImageView;
        //private final int mDimension;

        public PersonRenderer() {
            super(getApplicationContext(), getMap(), mClusterManager);

            View profile = getLayoutInflater().inflate(R.layout.map_marker_item, null);
            mIconGenerator.setContentView(profile);
            mImageView = (UICircularImage) profile.findViewById(R.id.avatarImageView);

        }

        @Override
        protected void onBeforeClusterItemRendered(ModelUser user, MarkerOptions markerOptions) {
            // Draw a single person.
            // Set the info window to show their name.
           // mImageView.setImageUrl(user.getAvatar_url(), imageLoader);
            Picasso.with(getApplicationContext())
                .load(user.getAvatar_url())
                .noFade()
                .into(mImageView, new com.squareup.picasso.Callback() {
                    @Override
                    public void onSuccess() {

                       //reload the marker HERE

                    }

                    @Override
                    public void onError() {

                    }
                });
            Bitmap icon = mIconGenerator.makeIcon();
            markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon)).title(user.getName());
        }

    }

Any idea why?

EDIT: I realized that sometimes it does load all the images, I think the problem comes from the async task. So my question is now: if I implement a callback to know when Picasso has finished to load the image, how can I reload this specific marker?

回答1:

We'll load raw bitmap from Picasso and then pass it to marker representing current user

Picasso.with(getApplicationContext())
        .load(user.getAvatar_url())
        .into(new com.squareup.picasso.Target() {
            @Override
            public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                // let's find marker for this user
                Marker markerToChange = null;
                for (Marker marker : mClusterManager.getMarkerCollection().getMarkers()) {
                    if (marker.getPosition().equals(user.getPosition())) {
                        markerToChange = marker;
                        break;
                    }
                }
                // if found - change icon
                if (markerToChange != null) {
                    markerToChange.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap));
                }
            }
            @Override
            public void onBitmapFailed(Drawable errorDrawable) {
            }
            @Override
            public void onPrepareLoad(Drawable placeHolderDrawable) {
            }
        });

I have some troubles with Picasso too. So, i recommend to use Glide

compile 'com.github.bumptech.glide:glide:3.5.2'



Glide.with(getApplicationContext()).
            load(user.getAvatar_url())
            .asBitmap()
            .fitCenter()
            .into(new SimpleTarget<Bitmap>() {
                @Override
                public void onResourceReady(Bitmap bitmap, GlideAnimation<? super Bitmap> glideAnimation) {
                    // let's find marker for this user
                    Marker markerToChange = null;
                    for (Marker marker : mClusterManager.getMarkerCollection().getMarkers()) {
                        if (marker.getPosition().equals(user.getPosition())) {
                            markerToChange = marker;
                            break;
                        }
                    }
                    // if found - change icon
                    if (markerToChange != null) {
                        markerToChange.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap));
                    }
                }
            });


回答2:

Regarding about @Stas Parshin answer, there might be some problems with Glide if you put the request within onBeforeClusterItemRendered().

I placed it on onClusterItemRendered() and it worked beautifully. It is because Glide is using asynchronous process and might not able to load it into ImageView in time.

Hope it helps.



回答3:

hello man the previous answer is working with with libs and here is another answer with native code

private class DownloadImage extends AsyncTask<String, Void, Bitmap> {

    ImageView image;
    LatLng latLng;

    public DownloadImage (double lat, double lng) {
        this.latLng = new LatLng(lat, lng);
    }

    protected Bitmap doInBackground(String... urls) {

        String url = urls[0];
        Bitmap bmpImg = null;

        try {
            InputStream in = new java.net.URL(url).openStream();
            bmpImg = BitmapFactory.decodeStream(in);

        } catch (Exception e) {

            Log.e("image", e.toString());
        }

        return bmpImg;
    }

    protected void onPostExecute(Bitmap bmpImg) {

        View viewLocationMarker = ((LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.marker_custom_view, null);
        ImageView imageView = (ImageView) viewLocationMarker.findViewById(R.id.imgPic);

        try {
            imageView.setImageBitmap(bmpImg);
        } catch (Exception e) {
            imageView.setImageResource(R.drawable.place_holder);
        }

        Marker locationMarker = mMap.addMarker(new MarkerOptions()
                .position(latLng).title("Sattar").anchor(0.33f, 1)
                .icon(BitmapDescriptorFactory.fromBitmap(createDrawableFromView(MapsActivity.this, viewLocationMarker))));
    }
}

public static Bitmap createDrawableFromView(Context context, View view) {
    DisplayMetrics displayMetrics = new DisplayMetrics();
    ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
    view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
    view.measure(displayMetrics.widthPixels, displayMetrics.heightPixels);
    view.layout(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels);
    view.buildDrawingCache();
    Bitmap bitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);

    Canvas canvas = new Canvas(bitmap);
    view.draw(canvas);

    return bitmap;
}