Friday, March 29, 2013

Gallery with ViewFlipper

Gallery with ViewFlipper Often in the development gallery in mobile applications from the developer is a problem out of memory when working with a lot of of images. In this article, we will develop the gallery, which will help prevent such messages as OutOfMemoryError or OutOfMemoryException.


Gallery features

  • With a large number of images the application will work stably without OutOfMemoryError;
  • Animated transitions between images;
  • Ability to loop the gallery.

Preparing resources

Source code strings.xml
<resources>
    <string name="app_name">ViewFlipperExample</string>
    <string name="str_loop">Looped</string>
    <string name="str_pages">%1$s/%2$s</string>
</resources>

From here you can download the necessary images to the gallery.
Will also create animation files for transitions between images.
Source code: anim/go_next_in.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
        android:fromXDelta="100%p"
        android:toXDelta="0"
        android:duration="400"/>
<alpha
        android:fromAlpha="1.0"
        android:toAlpha="1.0"
        android:duration="400" />
</set>

Source code: anim/go_next_out.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:fromXDelta="0"
        android:toXDelta="-100%p"
        android:duration="400"/>
    <alpha   
        android:fromAlpha="1.0"
        android:toAlpha="1.0"
        android:duration="400" />
</set>

Source code: anim/go_prev_in.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
      android:fromXDelta="-100%p"
        android:toXDelta="0"
        android:duration="400"/>
    <alpha
      android:fromAlpha="1.0"
        android:toAlpha="1.0"
        android:duration="400" />
</set>

Source code: anim/go_prev_out.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
      android:fromXDelta="0"
        android:toXDelta="100%p"
        android:duration="400"/>
    <alpha
        android:fromAlpha="1.0"
        android:toAlpha="1.0"
        android:duration="400" />
</set>

Both the layout of the application. It will consist of ViewFlipper, TextView to display the current page and the CheckBox to be able to loop the gallery.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ViewFlipper
        android:id="@+id/gallery"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <CheckBox
            android:id="@+id/checkBoxLoop"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/str_loop" />

        <TextView
            android:id="@+id/txtPages"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"/>
    </LinearLayout>

</LinearLayout>

Source code gallery_item.xml:
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/gallery_item"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scaleType="centerInside" > 
</ImageView>

Development

The idea of ​​the gallery is to store in memory only one View from the current image. In the transition to the next image, add a new View to the ViewFlipper, and the current to remove. Such a way application memory is not increased.
Will develop the galleries of the main Activity (MainActivity).
We need the following attributes:
  • UI elements
private ViewFlipper gallery;
private TextView txtPages;
private CheckBox checkBoxLoop;
private LayoutInflater inflater = null;
  • Coordinate X when clicking on ViewFlipper
private float fromPosition;
  • The index of the current image
private int count;
  • List of images
private List<Bitmap> items;

Develop the main methods for the gallery:
  • Preparing View with the image
private View addImage(Bitmap bitmap)
 {
  ImageView view = (ImageView)inflater.inflate(R.layout.gallery_item, null);
  view.setImageBitmap(bitmap);
  
  return view;
 }

  • Deleting an image from the gallery
private void removeImages()
 {
  if (gallery.getChildCount() > 2)
  {
   gallery.removeViewAt(0);
   System.gc();
  }
 }

  • Adding images to the gallery, depending on the direction
private void addNextImage(int position, boolean isLeft)
 {  
  if (isLeft)
  {
   if (position >= 0)
   {
    gallery.addView(addImage(items.get(position)));
   }
  } else 
  {
   if (position < items.size())
    gallery.addView(addImage(items.get(position)));
  }
 }

  • Update UI elements
private void updateTextView()
 {
  String pages = String.format(getString(R.string.str_pages), (count + 1), items.size());
  txtPages.setText(pages);
 }

  • Go to the next image
public void next()
    {
  if (count >= items.size() - 1 && !checkBoxLoop.isChecked())
   return;
  else if (count >= items.size() - 1 && checkBoxLoop.isChecked())
   count = -1;
  
  count++;
  addNextImage(count, false);
     gallery.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.go_next_in));
        gallery.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.go_next_out));
        gallery.showNext();   
        removeImages(); 
        updateTextView();
    }

  • Go to previous image
public void previous()
    {
  if (count <= 0 && !checkBoxLoop.isChecked())
   return;
  else if (count <= 0 && checkBoxLoop.isChecked())
   count = items.size();
  
  count--;
  addNextImage(count, true);
     gallery.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.go_prev_in));
        gallery.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.go_prev_out));
        gallery.showNext();
        removeImages(); 
        updateTextView();
    }

  • Initialize the list with images
private void initList()
 {
  items = new ArrayList();
  items.add(BitmapFactory.decodeResource(getResources(), R.drawable.sample_0));
  items.add(BitmapFactory.decodeResource(getResources(), R.drawable.sample_1));
  items.add(BitmapFactory.decodeResource(getResources(), R.drawable.sample_2));
  items.add(BitmapFactory.decodeResource(getResources(), R.drawable.sample_3));
  items.add(BitmapFactory.decodeResource(getResources(), R.drawable.sample_4));
  items.add(BitmapFactory.decodeResource(getResources(), R.drawable.sample_5));
  items.add(BitmapFactory.decodeResource(getResources(), R.drawable.sample_6));
  items.add(BitmapFactory.decodeResource(getResources(), R.drawable.sample_7));
 }

Finally, consistent with the work of galleries in the method onCreate our Activity:
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  
  initList();
  
  inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  
  this.txtPages = (TextView)findViewById(R.id.txtPages);
  this.checkBoxLoop = (CheckBox)findViewById(R.id.checkBoxLoop);
  this.gallery = (ViewFlipper)findViewById(R.id.gallery);  
  this.gallery.setOnTouchListener(new OnTouchListener() {
   
   @Override
   public boolean onTouch(View v, MotionEvent event) {
    switch (event.getAction())
          {
          case MotionEvent.ACTION_DOWN:
              fromPosition = event.getX();
              break;
          case MotionEvent.ACTION_UP:
              float toPosition = event.getX();
              if (fromPosition > toPosition + 20)
              {
               next();
               return true;
              }
              else if (fromPosition < toPosition - 20)
              {
            previous();
               return true;
              } 
          default:
              break;
          }
          return true;
   }
  });
  
  gallery.addView(addImage(items.get(0)));
  updateTextView();
 }

Links

  • The source codes of this project can be downloaded here: zip