Friday, July 20, 2012

Implement Gallery-like HorizontalScrollView

As mentioned in the post "Implement Android Gallery widget" - android.widget.Gallery is no longer supported. Other horizontally scrolling widgets include HorizontalScrollView and ViewPager from the support library.

It's a example to implement Gallery-like view using HorizontalScrollView. Please note that in this example, the bitmaps in HorizontalScrollView will not be removed even not in screen. So if too much bitmaps loaded, error of java.lang.OutOfMemoryError will be thrown!

Gallery-like HorizontalScrollView


Add HorizontalScrollView in layout:
<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">

    <HorizontalScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
        <LinearLayout
            android:id="@+id/mygallery"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            />
    </HorizontalScrollView>

</LinearLayout>


Main Java code:
package com.example.androidhorizontalscrollviewgallery;

import java.io.File;

import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;

public class MainActivity extends Activity {

 LinearLayout myGallery;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        myGallery = (LinearLayout)findViewById(R.id.mygallery);
        
        String ExternalStorageDirectoryPath = Environment
          .getExternalStorageDirectory()
          .getAbsolutePath();
        
        String targetPath = ExternalStorageDirectoryPath + "/test/";
        
        Toast.makeText(getApplicationContext(), targetPath, Toast.LENGTH_LONG).show();
        File targetDirector = new File(targetPath);
                 
        File[] files = targetDirector.listFiles();
        for (File file : files){
         myGallery.addView(insertPhoto(file.getAbsolutePath()));
        }    
    }

    View insertPhoto(String path){
     Bitmap bm = decodeSampledBitmapFromUri(path, 220, 220);
     
     LinearLayout layout = new LinearLayout(getApplicationContext());
     layout.setLayoutParams(new LayoutParams(250, 250));
     layout.setGravity(Gravity.CENTER);
     
     ImageView imageView = new ImageView(getApplicationContext());
     imageView.setLayoutParams(new LayoutParams(220, 220));
     imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
     imageView.setImageBitmap(bm);
     
     layout.addView(imageView);
     return layout;
    }
    
    public Bitmap decodeSampledBitmapFromUri(String path, int reqWidth, int reqHeight) {
     Bitmap bm = null;
     
     // 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;
     bm = BitmapFactory.decodeFile(path, options); 
     
     return bm;  
    }
    
    public 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) {
      if (width > height) {
       inSampleSize = Math.round((float)height / (float)reqHeight);   
      } else {
       inSampleSize = Math.round((float)width / (float)reqWidth);   
      }   
     }
     
     return inSampleSize;   
    }
    
}


Download the files.

Next:
- Implement custom LinearLayout for Gallery-like HorizontalScrollView
- Handle onClick for our custom LinearLayout for Gallery-like HorizontalScrollView


Remark@2015-11-13:
It's a old example. With current Material Design on Android, it's suggested to consider using " RecyclerView + CardView".

Coresponding "Gallery-like RecyclerView + CardView example".


20 comments:

Unknown said...

Hi
Firstly, great tutorial.
I just have one question. Is there anyway to select individual images from the gallery?

Unknown said...

Hello,

This is one of the best horizontalscrollview example on the web.

My question is how to catch clicks on Images in the horizontalScrollView?

Regards..

Anonymous said...

Same questio for me also.

Erik said...

Please read Handle onClick for our custom LinearLayout for Gallery-like HorizontalScrollView.

Anonymous said...

on indentation change of device TimerTask stops and application stops.

Unknown said...

Very useful tutorial
Thanks a lot

eL said...

Hi, what part of the code that populates the images. and how to handle selecting images?

Lawrnz said...

How about if get my data from JSON?

Erik said...

hello eL,

please read the insertPhoto() in the code.

Padda said...

I've 500+ imgaes its throwing OOM. How do i over come..? Please give me any link that i can refer

Erik said...

Hello Padda,

If you load too much bitmap in memory, it will cause Out Of Memory.

May be you can consider to implement Memory Cache to keep your bitmap, refer to Apply LruCache on GridView. But be careful to assign the memory for the cache, read LruCache with different size. A cache that is too small causes additional overhead with no benefit, a cache that is too large can once again cause java.lang.OutOfMemory exceptions and leave the rest of your app little memory to work with. I can't find any solution to find the memory size for all device!

Anonymous said...

hello, I want to load image from a directory which is located in this path : mnt/sdcard/DCIM , it's possible to do ?? Thanks

Erik said...

As I remember, you can store your path in targetPath directly, to load a specified directory.

Unknown said...

How can i use horizontal scrollview inside vertical scrollview?

i tried to use viewflipper inside scrollview but it did not work.


is there any way?


Erik said...

hello Dhaval Manvar,

please read: Horizontal-Vertical scrollable view.

Unknown said...

I'm getting this error at the line bm=decodeFile(path,options);

java.lang.OutOfMemoryError: Failed to allocate a 42024972 byte allocation with 8666540 free bytes and 8MB until OOM

Please help.
Thanks.

Erik said...

hello Anirudh GP,
Please check Handle error of 'OutOfMemoryError' and 'Bitmap too large to be uploaded into a texture' for images/bitmaps.

Anonymous said...

I am fetching the images from the server. I have the URL of the images. Can any one guide me how can i achieve that. thanks in advance

Unknown said...

Hi.. I got good example from you.. Thank you. Now I want add image with close image in the right top corner. So If I click the close button that image should delete. You please tell me how to add image with close image. It is very urgent. Please help me.

Thanks and Regards
C. Vijayadhas

Erik said...

hello Vijayadhas Chandran,

It's a old example. It's suggested to consider RecyclerView + CardView