Azurespot
Azurespot

Reputation: 3152

ArrayAdapter<> does not clear in onPause(), android

I have been researching this a while, but nothing. My ArrayAdapter<> for my GridView is in a Fragment tab. I have concluded that because it is in a fragment, that when I browse tabs then go back to my photos tab, instead of re-uploading images from scratch, it instead uploads images and add the same ones to the adapter. This results in 2 sets of identical images, but what I need is only a single set to upload upon revisiting the tab.

I tried putting adapter.clear() followed by adapter.notifyDataSetChanged() into my onPause() fragment lifecycle method. So when the fragment is not visible anymore, all adapter items clear, and when the fragment is visible again, it loads a fresh copy of images from my SD card. But this is not working, it still adds the new set of images in addition to the previously loaded set. Anyone know how to get the adapter cleared so I don't get the same images added every time I revisit the tab? Or if there is another way to do it, that I'm not aware of ... Thanks in advance.

UPDATE:

Thanks to everyone for the contributions, I finally found a work around that helped. I logged all of my adapter variables at different stages, and found that no matter where I clear it, it will always post a duplicate set in the loadSDCard() method, so it must have been coming from there. So I just made a condition on my adapter to prevent it from loading again. I noticed that my adapter was already loaded after coming back from the other tabs, by using getCount() on it. This came from my PhotoTab.java and is the missing piece:

if (adapter.getCount() == 0) {
            adapter.addAll(loadSDCard());

            // add the default icons remaining, to GridView, if less than 24 files on SD card
            for (int i = 0; i < (24 - photoList.size()); i++) {

                adapter.add(new PhotoGridItem(BitmapFactory.decodeResource(getResources(),
                        R.drawable.ic_photo_placeholder)));
            }

        }

UPDATE 2:

See my answer below, I found out the real reason why the duplicates were happening in the first place.

enter image description here

PhotoTab.java

package org.azurespot.cutecollection;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.GridView;

import org.azurespot.R;

import java.io.File;
import java.util.ArrayList;

/**
 * Created by mizu on 2/8/15.
 */
public class PhotoTab extends Fragment {

    private GridView gridView;
    File[] files;
    ArrayList<PhotoGridItem> photoList = new ArrayList<>();
    ArrayAdapter<PhotoGridItem> adapter;
    Bitmap bitmap;

    public PhotoTab() {
        super();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        // Inflate the layout for this fragment
        View v = inflater.inflate(R.layout.photo_tab, container, false);

        // with fragments, make sure you include the rootView when finding id
        gridView = (GridView) v.findViewById(R.id.photo_grid);
        adapter = new GridViewPhotoAdapter(getActivity(), R.layout.photo_grid_item);
        // Set the Adapter to GridView
        gridView.setAdapter(adapter);
        adapter.addAll(loadSDCard());

        // add the default icons remaining, to GridView, if less than 24 files on SD card
        for (int i = 0; i < (24 - photoList.size()); i++) {

            adapter.add(new PhotoGridItem(BitmapFactory.decodeResource(getResources(),
                    R.drawable.ic_photo_placeholder)));
        }

        return v;
    }


    private ArrayList<PhotoGridItem> loadSDCard() {

        try {
            // gets directory CutePhotos from sd card
            File cutePhotosDir = new File(Environment.getExternalStoragePublicDirectory
                    (Environment.DIRECTORY_PICTURES), "CutePhotos");
            // lists all files in CutePhotos, loads in Files[] array
            files = cutePhotosDir.listFiles();

            for (File singleFile : files) {
                String filePath = singleFile.getAbsolutePath();

                // this method makes size small for the view (to save memory)
                bitmap = decodeSampledBitmap(filePath, 270, 270);

                photoList.add(new PhotoGridItem(bitmap));
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return photoList;
    }

    public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

            final int halfHeight = height / 2;
            final int halfWidth = width / 2;

            // Calculate the largest inSampleSize value that is a power of 2 and keeps both
            // height and width larger than the requested height and width.
            while ((halfHeight / inSampleSize) > reqHeight
                    && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }
        }

        return inSampleSize;
    }

    public static Bitmap decodeSampledBitmap(String path, int reqWidth, int reqHeight) {

        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);

        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;

        return BitmapFactory.decodeFile(path, options);
    }

    @Override
    public void onPause(){
        super.onPause();
        adapter.clear();
        adapter.notifyDataSetChanged();

    }

}

GridViewPhotoAdapter

    package org.azurespot.cutecollection;

/**
* Created by mizu on 2/5/15.
*/
// package org.azurespot.cutecollection;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;

import org.azurespot.R;

/**
 * Created by mizu on 2/5/15.
 */
public class GridViewPhotoAdapter extends ArrayAdapter<PhotoGridItem> {

    public Context context;
    private int resourceId;
    Bitmap bm;

    public GridViewPhotoAdapter(Context context, int layoutResourceId) {
        super(context, layoutResourceId);
        this.context = context;
        this.resourceId = layoutResourceId;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        View row = convertView;
        final ViewHolder holder;

        if (row == null) {

            LayoutInflater inflater = ((Activity) context).getLayoutInflater();
            row = inflater.inflate(resourceId, parent, false);

            holder = new ViewHolder();
            holder.imageView = (ImageView) row.findViewById(R.id.photo_grid_view);
            // stores holder with view
            row.setTag(holder);

        } else {

            holder = (ViewHolder)row.getTag();
        }

        PhotoGridItem photoGridItem = getItem(position);

        if (photoGridItem != null) {
            bm = photoGridItem.getImage();
            holder.imageView.setImageBitmap(bm);

            // positioning the image in the GridView slot
            holder.imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            holder.imageView.setLayoutParams(new LinearLayout.LayoutParams(270, 270));
        }

        return row;

    }

    public class ViewHolder{
        ImageView imageView;
    }

}

PhotoGridItem

package org.azurespot.cutecollection;

import android.graphics.Bitmap;

/**
 * Created by mizu on 3/19/15.
 */
public class PhotoGridItem {

    private Bitmap image;

    public PhotoGridItem(Bitmap image) {
        super();
        this.image = image;
    }

    public Bitmap getImage() {
        return image;
    }

    public void setImage(Bitmap image) {
        this.image = image;
    }

}

Upvotes: 1

Views: 591

Answers (5)

Azurespot
Azurespot

Reputation: 3152

While I found a work around for the duplicate problem, the real reason I was getting duplicates is because I made my loadSDCard() method return an ArrayList<>. I realized I did not need to do that. So when I changed my method to void instead, I could refresh the tab and the SD Card would load, but it would start from scratch each time, and load the photo items over the old items, without duplicates. I don't really know why returning an ArrayList<> variable (I was returning photoList) in that method made it do that, but hopefully someone else will be helped if you get duplicates in the future.

Upvotes: 0

Xcihnegn
Xcihnegn

Reputation: 11597

Maybe you could try this modified your codes in onCreateView(...):

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    // Inflate the layout for this fragment
    View v = inflater.inflate(R.layout.photo_tab, container, false);

    // with fragments, make sure you include the rootView when finding id
    gridView = (GridView) v.findViewById(R.id.photo_grid);

    if(adapter == null)
        adapter = new GridViewPhotoAdapter(getActivity(), R.layout.photo_grid_item);

    // Set the Adapter to GridView
    gridView.setAdapter(adapter);

    if(adapter.getCount()>0)
        adapter.clear();

    adapter.addAll(loadSDCard());

    // add the default icons remaining, to GridView, if less than 24 files on SD card
    for (int i = 0; i < (24 - photoList.size()); i++) {

        adapter.add(new PhotoGridItem(BitmapFactory.decodeResource(getResources(),
                R.drawable.ic_photo_placeholder)));
    }

    adapter.notifyDataSetChanged(); //that was missing!!!  

    return v;
}

Hope this help!

Upvotes: 1

The Original Android
The Original Android

Reputation: 6215

The onCreateView of PhotoTab fragment is triggered several times, like when user switches apps (Android may need to unload your app to gain memory) and when rotate the screen. So from your code, I think they could all be duplicates within time. I think your design is such that the reloading of images is done in the onCreateView method. One disadvantage may be performance, but let's not worry about that for now.

My suggestion is to modify loadSDCard() to check if a file is already processed or displayed. In your code sample:

private ArrayList<PhotoGridItem> loadSDCard() {
    ...
    for (File singleFile : files) {
...
       // Check if this is a new bitmap file
       if (photoList.contains(bitmap) == false) {
          photoList.add(new PhotoGridItem(bitmap));
       }

    }
}

Upvotes: 0

Karim Varela
Karim Varela

Reputation: 7652

Fragment.onPause() isn't tied to the visibility of your Fragment. Fragment.onPause() is tied to it's encapsulating Activity.onPause(). i.e. Fragment.onPause() will only be called after Activity.onPause() is called.

If you want to update your Fragment when it becomes visible, I recommend you use Fragment.setUserVisibleHint() OR you use a ViewPager.onPageChangeListener() to let you know when your Fragment has been selected and update its contents accordingly.

Upvotes: 1

Treeline
Treeline

Reputation: 485

In your fragment, in onCreateView, try checking whether savedInstanceState is null like this:

 if (savedInstanceState == null) {
     //Add all your images to the adapter
 }

That way you should be sure that images are only added to the adapter when the view of the fragment is loaded the first time.

Upvotes: 0

Related Questions