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:

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

    ReplyDelete
  2. 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..

    ReplyDelete
  3. Same questio for me also.

    ReplyDelete
  4. on indentation change of device TimerTask stops and application stops.

    ReplyDelete
  5. Very useful tutorial
    Thanks a lot

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

    ReplyDelete
  7. How about if get my data from JSON?

    ReplyDelete
  8. hello eL,

    please read the insertPhoto() in the code.

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

    ReplyDelete
  10. 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!

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

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

    ReplyDelete
  13. 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?


    ReplyDelete
  14. 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.

    ReplyDelete
  15. 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

    ReplyDelete
  16. 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

    ReplyDelete
  17. hello Vijayadhas Chandran,

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

    ReplyDelete