2

I have a RecyclerView with a list of cards in it, and each has a spot for a picture. When I scroll down initially, the cards that began off-screen have their images loaded once they appear on-screen, but the cards that began on-screen only have their images loaded once they are moved off and back on-screen.

It also appears to be slightly random. Gif for context: https://gfycat.com/gifs/detail/accurateeducatedbaiji

The very last example in the gif (where the first 3 restaurant images do not load) is the most common.

ViewHolder:

public class LocationsViewHolder extends RecyclerView.ViewHolder{

private ImageView locationImage;
private TextView locationName;
private TextView locationAddress;

public LocationsViewHolder(View itemView) {
    super(itemView);

    locationName = (TextView)itemView.findViewById(R.id.location_name);
    locationAddress = (TextView)itemView.findViewById(R.id.location_address);
    locationImage = (ImageView) itemView.findViewById(R.id.location_image);
}

public void updateUI(Place place){
    locationName.setText(place.getName());
    if(place.getPhotoID() != null){
        // Log.v("IMAGE", "photoID for " + place.getName() + ": " + place.getPhotoID());
        locationImage.setImageBitmap(place.getImage());
    } else {
        locationImage.setImageResource(R.drawable.no_image);
    }
}

}

Adapter:

public class LocationsAdapter extends RecyclerView.Adapter<LocationsViewHolder>{

ArrayList<Place> locations;

Context context;

GetNearbyPlacesData data;

public LocationsAdapter(ArrayList<Place> locations, Context context) {
    this.locations = locations;
    this.context = context;
    data = GetNearbyPlacesData.getInstance(context);
}

@Override
public LocationsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View card = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_location, parent, false);
    return new LocationsViewHolder(card);
}

@Override
public void onBindViewHolder(LocationsViewHolder holder, int position) {
    Place location = locations.get(position);
    holder.updateUI(location);
}

@Override
public int getItemCount() {
    return locations.size();
}

}

downloadPlaceImage:

    Bitmap bitmap;

public void downloadPlaceImage(String imageKey, final Place place){
    String url = buildImgURL(imageKey);

    ImageRequest imageRequest = new ImageRequest(url, new Response.Listener<Bitmap>() {
        @Override
        public void onResponse(Bitmap response) {
            bitmap = response;
            place.setImage(bitmap);
            Log.v("IMG", "downloaded image");
        }
    }, 0, 0, null, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {

        }
    });

    requestQueue.add(imageRequest);
}
}

How switching between restaurants and pharmacies is done: The onNavigationItemSelected listener:

@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    Log.v("NAV", "Navigation item selected");
    URLInfo = new Bundle();
    URLInfo.putString("radius", MAP_RADIUS);
    URLInfo.putString("location", userMarker.getPosition().latitude + "," + userMarker.getPosition().longitude);

    mMap.clear();
    switch (item.getItemId()){
        case R.id.nav_restaurant: {
            URLInfo.putString("type", "restaurant");
            placeList = data.downloadPlacesData(URLInfo);
            break;
        }
        case R.id.nav_pharmacy: {
            URLInfo.putString("type", "pharmacy");
            placeList = data.downloadPlacesData(URLInfo);
            break;
        }
        case R.id.nav_bank: {
            URLInfo.putString("type", "bank");
            placeList = data.downloadPlacesData(URLInfo);
            break;
        }
    }

    mDrawerLayout.closeDrawer(GravityCompat.START);
    return true;
}

the downloadPlacesData() function:

ArrayList<Place> placesList;

public ArrayList<Place> downloadPlacesData(Bundle bundle){
    String url = buildPlaceURL(bundle);

    placesList = new ArrayList<>();

    //TODO make more requests for the next pages of results since each page is limited to 20 places

    JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(url, null, new Response.Listener<JSONObject>() {
        @Override
        public void onResponse(JSONObject response) {
            try {
                JSONArray results = response.getJSONArray("results");
                for(int i = 0; i < results.length(); i++){
                    JSONObject main = results.getJSONObject(i);
                    JSONObject geo = main.getJSONObject("geometry");
                    JSONObject loc = geo.getJSONObject("location");
                    String placeID = main.getString("place_id");
                    String name = main.getString("name");
                    String lat = loc.getString("lat");
                    String lng = loc.getString("lng");
                    String photoID = null;

                    try {
                        if (main.getJSONArray("photos") != null) {
                            JSONArray photos = main.getJSONArray("photos");
                            photoID = photos.getJSONObject(0).getString("photo_reference");
                        }
                    }catch (JSONException e){
                        Log.v("JSON4", e.getLocalizedMessage());
                    }

                    Place place = new Place(photoID, name, placeID, Double.parseDouble(lat), Double.parseDouble(lng), context);
                    Log.v("NAME", name);

                    placesList.add(place);

                }
                onPlacesLoadedListener.placesLoaded(placesList);

            }catch (JSONException e){
                Log.v("JSON", e.getLocalizedMessage());
            }

        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {

        }
    });

    requestQueue.add(jsonObjectRequest);

    return placesList;
}

The placesLoaded callback and creating the LocationsListFragment:

@Override
public void placesLoaded(ArrayList<Place> list) {
    Log.v("PLACES", "adding places");
    MarkerOptions markerOptions = new MarkerOptions();

    // build a resource path using the type of place
    String type = URLInfo.getString("type");
    String resourcePath = "drawable/pin_" + type;

    // create a resource path out of the String created
    int resource = getResources().getIdentifier(resourcePath, null, this.getPackageName());

    // set the marker icon according to the place type
    markerOptions.icon(BitmapDescriptorFactory.fromResource(resource));

    for(int i = 0; i < list.size(); i++){
        // get the latitude and longitude
        double lat = list.get(i).getLat();
        double lng = list.get(i).getLng();
        // set the marker position and title
        markerOptions.position(new LatLng(lat, lng));
        markerOptions.title(list.get(i).getName());
        mMap.addMarker(markerOptions);
    }
    createLocationsListFragment();

}

private void createLocationsListFragment() {
    FragmentManager fm = getSupportFragmentManager();
    LocationsListFragment fragment = LocationsListFragment.newInstance();
    fm.beginTransaction().add(R.id.container_locations, fragment).commit();
}

and finally, the actual fragment (onCreate and onCreateView):

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    getLocationsData = (GetLocationsData) getContext();
    mParam1 = getLocationsData.getList();
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View v = inflater.inflate(R.layout.fragment_locations_list, container, false);
    adapter = new LocationsAdapter(mParam1, getContext());

    RecyclerView recyclerView = (RecyclerView)v.findViewById(R.id.recycler_locations);
    recyclerView.setHasFixedSize(true);
    recyclerView.setAdapter(adapter);

    LinearLayoutManager layoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false);
    recyclerView.setLayoutManager(layoutManager);

    return v;
}
Ajeet Choudhary
  • 1,969
  • 1
  • 17
  • 41
damian r
  • 93
  • 1
  • 10
  • What code is executed when (in your example video) you switch between restaurants and pharmacies? I.e., how is your adapter's data updated? – Ben P. Jul 21 '17 at 03:48
  • when one of the items are selected it downloads nearby location data similarly to the image download (JSON and volley) edit: i will add this to the main post – damian r Jul 21 '17 at 04:15

2 Answers2

0

if you have url or bitmap of the image then use glide for it will cache your image make network call if you send url and load it for more about glide

Glide.with(mContext)
    .load(imageUrl)
    .diskCacheStrategy(DiskCacheStrategy.ALL)
    .centerCrop()
    .into(mImageView_photo)
;

Avinash
  • 264
  • 5
  • 15
0

In RecyclerView you have to ensure that you either return a unique Id for each Item or In case of no Stable Id you should return RecyclerView.NO_ID (-1). Please Refrain from returning values that are not stable. (An Stable value would be a unique value that does not change even if position of dataset changes)

In your RecyclerView adapter add the getItemId call back.

eg:

@Override
public long getItemId(int position) {
    return Long.parseLong(programList.get(position).getLmsId());
}
Zayid Mohammed
  • 2,275
  • 2
  • 10
  • 17