0

I have an image carousel I've built for my app using a ViewFlipper but the quality of the images stored on the device are very poor. I've using the following:

String fileLoc = mediaData.get("fileLoc");
ImageView imageItem = new ImageView(getApplicationContext());
InputStream imageStream = null;
try {
     File file = new File(fileLoc);
     imageStream = new FileInputStream(file);
     imageItem.setImageBitmap(Configurator.decodeSampledBitmapFromResource(getResources(), fileLoc, 100, 100));
} catch...

Memory is a concern as there could be any number of images displayed.

I did find this:

Resource to bitmap conversion results in poor quality

But I can't see how to translate it to my situation.

Per @Ultimo_m , Here is detail on what needs to happen:

  1. The user is presented with a screen that has three sections based on previous options selected: pdf documents, images, and videos.
  2. The user selects an image from the set shown
  3. User is taken to a screen that shows the selected item
  4. Swiping from right to left shows the next image in that set
  5. Swiping from left to right shows the previous image in the set

Right now I'm using a ViewFlipper:

  1. I pass an array of each image's location via a SharedPreference
  2. I also pass the key location of the current image
  3. I loop through the array to build the ViewFlipper
  4. I use the current key to set the current view in the flipper

Using the selected library from @Ultimo_m how do I tell the library the current image to show as well what the next and previous image will be?

----- EDIT -----

I have Univeral Image loader working on the current image when tapped from the previous screen. I can't find in the docs how to handle the swipe event and tell the library what to display next. There is a screenshot on the library's github of a swipe event in progress but how do I leverage this?

----- EDIT -----

App is crashing. Throwing a NullPointer at imageLoadView.setAdapter(new ImagePagerAdapter(imageArraySet));. I've made the xml files as you specified. I did make adjustments to your code to get my array from the SharedPref I've catlogged my array that I'm passing (imageArraySet) and the array is correct. I've pasted the full error log after the code:

public class Viewer extends baseActivity {

    clientDB clientDB = new ClientDB(this);

    public static final String PREFS_NAME = "myPrefs";
    SharedPreferences storedInfo;

    String chosenImg;

    ViewPager imageLoadView;

    DisplayImageOptions options;

    String[] imageArraySet;

    //LoadImageUtil mLoadImageUtil;
    private static final String STATE_POSITION = "STATE_POSITION";

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

        Bundle bundle = getIntent().getExtras();
        assert bundle != null;

        storedInfo = getSharedPreferences(PREFS_NAME,0);
        String mediaID = storedInfo.getString("imgId", null); //ids of all passed images
        String chosenImg = storedInfo.getString("chosenImg", null); //id of current image

        String[] imageArray = mediaID.split(",");
        imageArraySet = clientDB.getMediaDataSet(imageArray);
        int pagerPosition = 0;

        imageLoadView = (ViewPager) findViewById(R.id.imageLoadView);
        imageLoadView.setAdapter(new ImagePagerAdapter(imageArraySet));
        imageLoadView.setCurrentItem(pagerPosition);


    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        outState.putInt(STATE_POSITION, imageLoadView.getCurrentItem());
    }

    private class ImagePagerAdapter extends PagerAdapter {

        private String[] images;
        private LayoutInflater inflater;

        ImagePagerAdapter(String[] images) {
            this.images = images;
            inflater = getLayoutInflater();
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
        }

        @Override
        public int getCount() {
            return images.length;
        }

        @Override
        public Object instantiateItem(ViewGroup view, int position) {
            View imageLayout = inflater.inflate(R.layout.item_pager_image, view, false);
            assert imageLayout != null;
            ImageView imageView = (ImageView) imageLayout.findViewById(R.id.image);
            final ProgressBar spinner = (ProgressBar) imageLayout.findViewById(R.id.loading);

            imageLoader.displayImage(images[position], imageView, options, new SimpleImageLoadingListener() {
                @Override
                public void onLoadingStarted(String imageUri, View view) {
                    spinner.setVisibility(View.VISIBLE);
                }

                @Override
                public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
                    String message = null;
                    switch (failReason.getType()) {
                        case IO_ERROR:
                            message = "Input/Output error";
                            break;
                        case DECODING_ERROR:
                            message = "Image can't be decoded";
                            break;
                        case NETWORK_DENIED:
                            message = "Downloads are denied";
                            break;
                        case OUT_OF_MEMORY:
                            message = "Out Of Memory error";
                            break;
                        case UNKNOWN:
                            message = "Unknown error";
                            break;
                    }
                    //Toast.makeText(ImagePagerActivity.this, message, Toast.LENGTH_SHORT).show();

                    spinner.setVisibility(View.GONE);
                }

                @Override
                public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
                    spinner.setVisibility(View.GONE);
                }
            });

            view.addView(imageLayout, 0);
            return imageLayout;
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view.equals(object);
        }

        @Override
        public void restoreState(Parcelable state, ClassLoader loader) {
        }

        @Override
        public Parcelable saveState() {
            return null;
        }
    }



}

Error log:

6-12 16:41:30.455: W/dalvikvm(19960): threadid=1: thread exiting with uncaught exception (group=0x41fdfe10)
06-12 16:41:30.455: E/AndroidRuntime(19960): FATAL EXCEPTION: main
06-12 16:41:30.455: E/AndroidRuntime(19960): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.client.clientconfigurator/com.client.clientconfigurator.Viewer}: java.lang.NullPointerException
06-12 16:41:30.455: E/AndroidRuntime(19960):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2245)
06-12 16:41:30.455: E/AndroidRuntime(19960):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2295)
06-12 16:41:30.455: E/AndroidRuntime(19960):    at android.app.ActivityThread.access$700(ActivityThread.java:150)
06-12 16:41:30.455: E/AndroidRuntime(19960):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1280)
06-12 16:41:30.455: E/AndroidRuntime(19960):    at android.os.Handler.dispatchMessage(Handler.java:99)
06-12 16:41:30.455: E/AndroidRuntime(19960):    at android.os.Looper.loop(Looper.java:176)
06-12 16:41:30.455: E/AndroidRuntime(19960):    at android.app.ActivityThread.main(ActivityThread.java:5279)
06-12 16:41:30.455: E/AndroidRuntime(19960):    at java.lang.reflect.Method.invokeNative(Native Method)
06-12 16:41:30.455: E/AndroidRuntime(19960):    at java.lang.reflect.Method.invoke(Method.java:511)
06-12 16:41:30.455: E/AndroidRuntime(19960):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1102)
06-12 16:41:30.455: E/AndroidRuntime(19960):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:869)
06-12 16:41:30.455: E/AndroidRuntime(19960):    at dalvik.system.NativeStart.main(Native Method)
06-12 16:41:30.455: E/AndroidRuntime(19960): Caused by: java.lang.NullPointerException
06-12 16:41:30.455: E/AndroidRuntime(19960):    at com.client.clientconfigurator.Viewer.onCreate(Viewer.java:92)
06-12 16:41:30.455: E/AndroidRuntime(19960):    at android.app.Activity.performCreate(Activity.java:5267)
Community
  • 1
  • 1
dcp3450
  • 10,959
  • 23
  • 58
  • 110

1 Answers1

1

I suggest you use Universal Image Loader, it uses asynchronous image loading, caching and displaying, there you can find examples how to implement it. After you do it you dont have to concern about the number of images.

Here i will give you an example how to use it:

First create this LoadImageUtil.class

public class LoadImageUtil {

    protected ImageLoader imageLoader = ImageLoader.getInstance();
    DisplayImageOptions options;
    public boolean memoryManage(int item) {// use this to clear cache 
        switch (item) {
            case 0:
                imageLoader.clearMemoryCache();
                return true;
            case 1:
                imageLoader.clearDiscCache();
                return true;
            default:
                return false;
        }
    }

    public LoadImageUtil(Context mContext) {
        initUILSettings(mContext);
    }

    public void initUILSettings(Context mContext){

        imageLoader.init(ImageLoaderConfiguration.createDefault(mContext));

        options = new DisplayImageOptions.Builder()
                .showImageOnLoading(R.drawable.calice)
                .showImageForEmptyUri(R.drawable.calice)
                .showImageOnFail(R.drawable.calice)
                .cacheInMemory(true)
                .cacheOnDisc(true)
                .considerExifParams(true)
                .bitmapConfig(Bitmap.Config.RGB_565)
                .build();

    }

    public void loadBitmapToImageView(ImageView myImageView, String ImagePath) {

        imageLoader.displayImage(ImagePath, myImageView, options, new SimpleImageLoadingListener() {
            @Override
            public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
                super.onLoadingComplete(imageUri, view, loadedImage);

            }

            @Override
            public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
               // Log.e("onLoadingFailed : " + imageUri, "failReason: " + failReason);

            }

            @Override
            public void onLoadingCancelled(String imageUri, View view) {
                super.onLoadingCancelled(imageUri, view);                
            }
        });
    }
}

Second from your class where you want to load the images do this:

LoadImageUtil mLoadImageUtil = new LoadImageUtil(getApplicationContext());

Third Put this where you want to load image:

mLoadImageUtil.loadBitmapToImageView(YourImageView, ImagePathOrUrl);

Here you have examples of imagesPath that you should pass at the method for loading images

String imageUri = "http://8.8.8.8/image.png"; // from Web
String imageUri = "file:///mnt/sdcard/image.png"; // from SD card
String imageUri = "content://media/external/audio/albumart/13"; // from content provider
String imageUri = "assets://image.png"; // from assets
String imageUri = "drawable://" + R.drawable.image; // from drawables (only images, non-9patch)

EDIT:

In my opinion it would be best if you would use ViewPager to solve your problem, below i will show you some code how to do that based on the library I told you:

/**
 * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
 */
public class ImagePagerActivity extends Activity {

    private static final String STATE_POSITION = "STATE_POSITION";


    ViewPager pager;
     LoadImageUtil mLoadImageUtil;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.ac_image_pager);

             // in this example the data are passed to this activity using bundle
        Bundle bundle = getIntent().getExtras();
        assert bundle != null;

            // imageUrls is the array that contains the images URL to load
        String[] imageUrls = bundle.getStringArray(Extra.IMAGES);

        int pagerPosition = bundle.getInt(Extra.IMAGE_POSITION, 0);

        if (savedInstanceState != null) {
            pagerPosition = savedInstanceState.getInt(STATE_POSITION);
        }

             mLoadImageUtil = new LoadImageUtil(getApplicationContext());


        pager = (ViewPager) findViewById(R.id.pager);
        pager.setAdapter(new ImagePagerAdapter(imageUrls));
        pager.setCurrentItem(pagerPosition);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        outState.putInt(STATE_POSITION, pager.getCurrentItem());
    }

    private class ImagePagerAdapter extends PagerAdapter {

        private String[] images;
        private LayoutInflater inflater;

        ImagePagerAdapter(String[] images) {
            this.images = images;
            inflater = getLayoutInflater();
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
        }
           // the view pager will have the same number of pages as the length of the Strings array
        @Override
        public int getCount() {
            return images.length;
        }

        @Override
        public Object instantiateItem(ViewGroup view, int position) {
            View imageLayout = inflater.inflate(R.layout.item_pager_image, view, false);
            assert imageLayout != null;
            ImageView imageView = (ImageView) imageLayout.findViewById(R.id.image);
               // here is the part where image is load for each pager
               mLoadImageUtil.loadBitmapToImageView(imageView, images[position]);

            view.addView(imageLayout, 0);
            return imageLayout;
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view.equals(object);
        }

        @Override
        public void restoreState(Parcelable state, ClassLoader loader) {
        }

        @Override
        public Parcelable saveState() {
            return null;
        }
    }
}

Here are the xml files:

ac_image_pager.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/pager"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" />

item_pager_image.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:padding="1dip" >

    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:adjustViewBounds="true"
        android:contentDescription="@string/descr_image" />

</FrameLayout>
Ultimo_m
  • 4,724
  • 4
  • 38
  • 60
  • I'm on a bit of a time crunch and the client likes the current functionality, just need to improve the image quality. Anything I can do in my above code or adjustments I can make to fix this issue? – dcp3450 Jun 09 '14 at 14:05
  • I'm attempting to use the suggested library but can't figure out how to get it to work with my viewer screen. I need to pass it the current image and I assume assign the next and previous images as well(?) – dcp3450 Jun 09 '14 at 14:49
  • Thanks, this is much easier to follow. I'll be working through this over the next couple of days. I do have a follow up: The user is presented with a set of thumbnails, 1->n. They tap on one that send them to a view screen (where I'll be using this library). I'm assuming I'd pass the "current image" via a `SharedPreference` however, how do I tell the library the next and previous image in the set? Right now I pass the image locations as an array and pass the key of the current image via the `SharedPreference`. – dcp3450 Jun 11 '14 at 22:13
  • I didnt understand well what you are asking, can you please update your question and explain it in a more detailed way. I will see that tomorrow – Ultimo_m Jun 11 '14 at 22:19
  • Updated my question with more detail. hope that helps. – dcp3450 Jun 12 '14 at 15:43
  • Got the library to work with the currently selected image. I've updated my question again to reflect/request more info. – dcp3450 Jun 12 '14 at 16:55
  • excellent. I'm looking through the samples provided by the library as well. – dcp3450 Jun 12 '14 at 19:19
  • Edited my question. Got everything up but I'm getting a null pointer at `imageLoadView.setAdapter(new ImagePagerAdapter(imageArraySet));` note that `imageLoadView` is my ViewPager also, `mediaID' is a string of all the ids for the images passed form the previous screen. I pass through though the database to get an array of the image locations stored in the DB. – dcp3450 Jun 12 '14 at 21:04
  • I realized my `setContentView(R.layout.activity_viewer);` was set incorrectly. I changed it to `setContentView(R.layout.ac_image_pager);` and fixed the null pointer. I'm getting a white screen now and a message in the log: `06-12 17:23:45.235: E/ImageLoader(22001): java.lang.UnsupportedOperationException: UIL doesn't support scheme(protocol) by default [/storage/emulated/0/.Client/.2/CA300_CA550_Welding_Lg-test-1_1200_800.jpg]. You should implement this support yourself (BaseImageDownloader.getStreamFromOtherSource(...))` – dcp3450 Jun 12 '14 at 21:26
  • seems like I need to add `file://` to the beginning of each element in my image array? – dcp3450 Jun 12 '14 at 21:39
  • if your images are from web you need to put the url, if they are a file, you need to put the path as shown in the example i have shown file:///mnt.... – Ultimo_m Jun 12 '14 at 22:00
  • Got it to work. Only thing now is if the images don't scale to fit the screen, so small images don't upscale. I know there would be loss of quality there but still need them to fit. Looking to see if there is an option for that. – dcp3450 Jun 12 '14 at 22:05
  • If i am not wrong, this library will take dimensions of ImageView to create bitmap, so if you put `ImageView` match parent, it will fit the screen, if you will use `wrap content` it will be the same size as Bitmap i think so – Ultimo_m Jun 12 '14 at 22:14
  • set the `imageScaleType` to `.imageScaleType(ImageScaleType.EXACTLY_STRETCHED)` and it fixed the issue. Thanks for all your help, looks like I got it going. – dcp3450 Jun 12 '14 at 22:22