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


15 comments:

Kent Hawkings said...

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

Volkan Levent 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..

Ankit Goyal said...

Same questio for me also.

Andr.oid Eric 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?

Andr.oid Eric 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

Andr.oid Eric 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

Andr.oid Eric said...

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

Dhaval Manvar 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?


Andr.oid Eric said...

hello Dhaval Manvar,

please read: Horizontal-Vertical scrollable view.