Showing posts with label GridView. Show all posts
Showing posts with label GridView. Show all posts

Monday, January 19, 2015

Drag and Drop between ListView and GridView

Previous example show how to implement Drag-and-Drop between ListViews. As both ListView and Gridiew subclasses of AbsListView, it's easy to implement Drag-and-Drop between ListView and GridView.


Refactory LinearLayoutListView.java to LinearLayoutAbsListView.java, to implement our custom LinearLayout associate with AbsListView, for ListView or GridView.
package com.example.androidimageviewlist;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.AbsListView;
import android.widget.LinearLayout;

public class LinearLayoutAbsListView extends LinearLayout {
 
 AbsListView absListView;

 public LinearLayoutAbsListView(Context context) {
  super(context);
  // TODO Auto-generated constructor stub
 }

 public LinearLayoutAbsListView(Context context, AttributeSet attrs) {
  super(context, attrs);
  // TODO Auto-generated constructor stub
 }

 public LinearLayoutAbsListView(Context context, AttributeSet attrs,
   int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  // TODO Auto-generated constructor stub
 }

 public void setAbsListView(AbsListView alv){
  absListView = alv;
 }

}

Create /res/layout/gridrow.xml to define row layout for GridView.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal" >

    <ImageView
        android:id="@+id/gridrowImageView"
        android:layout_gravity="center"
        android:layout_width="48dp"
        android:layout_height="48dp" />

</LinearLayout>

Modify /res/layout/activity_main.xml to include three <com.example.androidimageviewlist.LinearLayoutAbsListView>
<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"
    android:padding="4dp"
    tools:context="com.example.androidimageviewlist.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2"
        android:background="@android:color/background_dark"
        android:orientation="horizontal" >

        <com.example.androidimageviewlist.LinearLayoutAbsListView
            android:id="@+id/pane1"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="4dp"
            android:layout_weight="1"
            android:background="@android:color/background_light"
            android:orientation="vertical" >

            <ListView
                android:id="@+id/listview1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </com.example.androidimageviewlist.LinearLayoutAbsListView>

        <com.example.androidimageviewlist.LinearLayoutAbsListView
            android:id="@+id/pane2"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="4dp"
            android:layout_weight="1"
            android:background="@android:color/background_light"
            android:orientation="vertical" >

            <ListView
                android:id="@+id/listview2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </com.example.androidimageviewlist.LinearLayoutAbsListView>
        
        <com.example.androidimageviewlist.LinearLayoutAbsListView
            android:id="@+id/pane3"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="4dp"
            android:layout_weight="1"
            android:background="@android:color/background_light"
            android:orientation="vertical" >

            <GridView
                android:id="@+id/gridview3"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:numColumns="auto_fit"
                android:verticalSpacing="10dp"
                android:horizontalSpacing="10dp"
                android:columnWidth="60dp"
                android:stretchMode="columnWidth"
                android:gravity="center" />
        </com.example.androidimageviewlist.LinearLayoutAbsListView>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="horizontal" >

        <TextView
            android:id="@+id/prompt"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/black"
            android:gravity="bottom"
            android:textColor="@android:color/white" />
    </LinearLayout>

</LinearLayout>

MainActivity.java
package com.example.androidimageviewlist;

import java.util.ArrayList;
import java.util.List;

import android.support.v7.app.ActionBarActivity;
import android.text.method.ScrollingMovementMethod;
import android.app.Activity;
import android.content.ClipData;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.DragEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.DragShadowBuilder;
import android.view.View.OnDragListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity {

 //items stored in ListView
 public class Item {
  Drawable ItemDrawable;
  String ItemString;
  Item(Drawable drawable, String t){
   ItemDrawable = drawable;
   ItemString = t;
  }
 }
 
 //objects passed in Drag and Drop operation
 class PassObject{
  View view;
  Item item;
  List<Item> srcList;
  
  PassObject(View v, Item i, List<Item> s){
   view = v;
   item = i;
   srcList = s;
  }
 }
 
 static class ViewHolder {
  ImageView icon;
  TextView text; 
 }
 
 static class GridViewHolder {
  ImageView icon; 
 }
 
 public class ItemBaseAdapter extends BaseAdapter {

  Context context;
  List<Item> list;

  ItemBaseAdapter(Context c, List<Item> l){
   context = c;
   list = l;
  }

  @Override
  public int getCount() {
   return list.size();
  }

  @Override
  public Object getItem(int position) {
   return list.get(position);
  }

  @Override
  public long getItemId(int position) {
   return position;
  }
  
  public List<Item> getList(){
   return list;
  }

  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
   // TODO Auto-generated method stub
   return null;
  }
  
 }
 

 public class ItemListAdapter extends ItemBaseAdapter {
  
  ItemListAdapter(Context c, List<Item> l) {
   super(c, l);
  }

  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
   View rowView = convertView;
   
      // reuse views
      if (rowView == null) {
       LayoutInflater inflater = ((Activity) context).getLayoutInflater();
       rowView = inflater.inflate(R.layout.row, null);

       ViewHolder viewHolder = new ViewHolder();
       viewHolder.icon = (ImageView) rowView.findViewById(R.id.rowImageView);
       viewHolder.text = (TextView) rowView.findViewById(R.id.rowTextView);
       rowView.setTag(viewHolder); 
      }

      ViewHolder holder = (ViewHolder) rowView.getTag();
      holder.icon.setImageDrawable(list.get(position).ItemDrawable);
      holder.text.setText(list.get(position).ItemString);
      
      rowView.setOnDragListener(new ItemOnDragListener(list.get(position)));

      return rowView;
  }

 }
 
 public class ItemGridAdapter extends ItemBaseAdapter {

  ItemGridAdapter(Context c, List<Item> l) {
   super(c, l);
  }
  
  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
   View gridrowView = convertView;
   
      // reuse views
      if (gridrowView == null) {
       LayoutInflater inflater = ((Activity) context).getLayoutInflater();
       gridrowView = inflater.inflate(R.layout.gridrow, null);

       GridViewHolder gridviewHolder = new GridViewHolder();
       gridviewHolder.icon = (ImageView) gridrowView.findViewById(R.id.gridrowImageView);
       gridrowView.setTag(gridviewHolder); 
      }

      GridViewHolder holder = (GridViewHolder) gridrowView.getTag();
      holder.icon.setImageDrawable(list.get(position).ItemDrawable);

      gridrowView.setOnDragListener(new ItemOnDragListener(list.get(position)));

      return gridrowView;
  }
  
 }

 List<Item> items1, items2, items3;
 ListView listView1, listView2;
 GridView gridView3;
 ItemListAdapter myItemListAdapter1, myItemListAdapter2;
 ItemGridAdapter myItemGridAdapter3;
 LinearLayoutAbsListView area1, area2, area3;
 TextView prompt;
 
 //Used to resume original color in drop ended/exited
 int resumeColor;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  listView1 = (ListView)findViewById(R.id.listview1);
  listView2 = (ListView)findViewById(R.id.listview2);
  gridView3 = (GridView)findViewById(R.id.gridview3);
  
  area1 = (LinearLayoutAbsListView)findViewById(R.id.pane1);
  area2 = (LinearLayoutAbsListView)findViewById(R.id.pane2);
  area3 = (LinearLayoutAbsListView)findViewById(R.id.pane3);
  area1.setOnDragListener(myOnDragListener);
  area2.setOnDragListener(myOnDragListener);
  area3.setOnDragListener(myOnDragListener);
  area1.setAbsListView(listView1);
  area2.setAbsListView(listView2);
  area3.setAbsListView(gridView3);
  
  initItems();
  myItemListAdapter1 = new ItemListAdapter(this, items1);
  myItemListAdapter2 = new ItemListAdapter(this, items2);
  myItemGridAdapter3 = new ItemGridAdapter(this, items3);
  listView1.setAdapter(myItemListAdapter1);
  listView2.setAdapter(myItemListAdapter2);
  gridView3.setAdapter(myItemGridAdapter3);
  
  listView1.setOnItemClickListener(listOnItemClickListener);
  listView2.setOnItemClickListener(listOnItemClickListener);
  gridView3.setOnItemClickListener(listOnItemClickListener);
  
  listView1.setOnItemLongClickListener(myOnItemLongClickListener);
  listView2.setOnItemLongClickListener(myOnItemLongClickListener);
  gridView3.setOnItemLongClickListener(myOnItemLongClickListener);
  
  prompt = (TextView) findViewById(R.id.prompt);
  // make TextView scrollable
  prompt.setMovementMethod(new ScrollingMovementMethod());
  //clear prompt area if LongClick
  prompt.setOnLongClickListener(new OnLongClickListener(){
   
   @Override
   public boolean onLongClick(View v) {
    prompt.setText("");
    return true; 
   }});
  
  resumeColor  = getResources().getColor(android.R.color.background_light);

 }
 
 OnItemLongClickListener myOnItemLongClickListener = new OnItemLongClickListener(){

  @Override
  public boolean onItemLongClick(AdapterView<?> parent, View view,
    int position, long id) {
   Item selectedItem = (Item)(parent.getItemAtPosition(position));
   
   ItemBaseAdapter associatedAdapter = (ItemBaseAdapter)(parent.getAdapter());
      List<Item> associatedList = associatedAdapter.getList();
   
   PassObject passObj = new PassObject(view, selectedItem, associatedList);

   ClipData data = ClipData.newPlainText("", "");
   DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
   view.startDrag(data, shadowBuilder, passObj, 0);
   
   return true;
  }
  
 };
 
 OnDragListener myOnDragListener = new OnDragListener() {

  @Override
  public boolean onDrag(View v, DragEvent event) {
   String area;
   if(v == area1){
    area = "area1"; 
   }else if(v == area2){
    area = "area2"; 
   }else if(v == area3){
    area = "area3"; 
   }else{
    area = "unknown"; 
   }
   
   switch (event.getAction()) {
    case DragEvent.ACTION_DRAG_STARTED:
     prompt.append("ACTION_DRAG_STARTED: " + area  + "\n");
     break; 
    case DragEvent.ACTION_DRAG_ENTERED:
     prompt.append("ACTION_DRAG_ENTERED: " + area  + "\n");
     break; 
    case DragEvent.ACTION_DRAG_EXITED:
     prompt.append("ACTION_DRAG_EXITED: " + area  + "\n");
     break; 
    case DragEvent.ACTION_DROP:
     prompt.append("ACTION_DROP: " + area  + "\n");

     PassObject passObj = (PassObject)event.getLocalState();
     View view = passObj.view;
     Item passedItem = passObj.item;
     List<Item> srcList = passObj.srcList;
     AbsListView oldParent = (AbsListView)view.getParent();
     ItemBaseAdapter srcAdapter = (ItemBaseAdapter)(oldParent.getAdapter());
     
     LinearLayoutAbsListView newParent = (LinearLayoutAbsListView)v;
     ItemBaseAdapter destAdapter = (ItemBaseAdapter)(newParent.absListView.getAdapter());
        List<Item> destList = destAdapter.getList();
     
     if(removeItemToList(srcList, passedItem)){
      addItemToList(destList, passedItem);
     }
     
     srcAdapter.notifyDataSetChanged();
     destAdapter.notifyDataSetChanged();
     
     //smooth scroll to bottom
     newParent.absListView.smoothScrollToPosition(destAdapter.getCount()-1);
     
     break;
      case DragEvent.ACTION_DRAG_ENDED:
       prompt.append("ACTION_DRAG_ENDED: " + area  + "\n");  
      default:
       break;    
   }
      
   return true;
  }
  
 };
 
 class ItemOnDragListener implements OnDragListener{
  
  Item  me;
  
  ItemOnDragListener(Item i){
   me = i;
  }

  @Override
  public boolean onDrag(View v, DragEvent event) {
   switch (event.getAction()) {
   case DragEvent.ACTION_DRAG_STARTED:
    prompt.append("Item ACTION_DRAG_STARTED: " + "\n");
    break; 
   case DragEvent.ACTION_DRAG_ENTERED:
    prompt.append("Item ACTION_DRAG_ENTERED: " + "\n");
    v.setBackgroundColor(0x30000000);
    break; 
   case DragEvent.ACTION_DRAG_EXITED:
    prompt.append("Item ACTION_DRAG_EXITED: " + "\n");
    v.setBackgroundColor(resumeColor);
    break; 
   case DragEvent.ACTION_DROP:
    prompt.append("Item ACTION_DROP: " + "\n");

    PassObject passObj = (PassObject)event.getLocalState();
    View view = passObj.view;
    Item passedItem = passObj.item;
    List<Item> srcList = passObj.srcList;
    AbsListView oldParent = (AbsListView)view.getParent();
    ItemBaseAdapter srcAdapter = (ItemBaseAdapter)(oldParent.getAdapter());
    
    AbsListView newParent = (AbsListView)v.getParent();
    ItemBaseAdapter destAdapter = (ItemBaseAdapter)(newParent.getAdapter());
    List<Item> destList = destAdapter.getList();
    
    int removeLocation = srcList.indexOf(passedItem);
    int insertLocation = destList.indexOf(me);
    /*
     * If drag and drop on the same list, same position,
     * ignore
     */
    if(srcList != destList || removeLocation != insertLocation){
     if(removeItemToList(srcList, passedItem)){
      destList.add(insertLocation, passedItem);
     }
     
     srcAdapter.notifyDataSetChanged();
     destAdapter.notifyDataSetChanged();
    }

    v.setBackgroundColor(resumeColor);
    
    break;
     case DragEvent.ACTION_DRAG_ENDED:
      prompt.append("Item ACTION_DRAG_ENDED: "  + "\n");
      v.setBackgroundColor(resumeColor);
     default:
      break;    
  }
     
  return true;
  }
  
 }
 
 OnItemClickListener listOnItemClickListener = new OnItemClickListener(){

  @Override
  public void onItemClick(AdapterView<?> parent, View view, int position,
    long id) {
   Toast.makeText(MainActivity.this, 
     ((Item)(parent.getItemAtPosition(position))).ItemString, 
     Toast.LENGTH_SHORT).show();
  }
  
 };

 private void initItems(){
  items1 = new ArrayList<Item>();
  items2 = new ArrayList<Item>();
  items3 = new ArrayList<Item>();
  
  TypedArray arrayDrawable = getResources().obtainTypedArray(R.array.resicon);
  TypedArray arrayText = getResources().obtainTypedArray(R.array.restext);
  
  for(int i=0; i<arrayDrawable.length(); i++){
   Drawable d = arrayDrawable.getDrawable(i);
   String s = arrayText.getString(i);
   Item item = new Item(d, s);
   items1.add(item);
  }
  
  arrayDrawable.recycle();
  arrayText.recycle();
 }
 
 private boolean removeItemToList(List<Item> l, Item it){
  boolean result = l.remove(it);
  return result;
 }
 
 private boolean addItemToList(List<Item> l, Item it){
  boolean result = l.add(it);
  return result;
 }

}

download filesDownload the files.

Wednesday, December 3, 2014

GridView example

Android example of using GridView


activity_main.xml
<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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.androiddtmfpiano.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <GridView
        android:id="@+id/gridView"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:columnWidth="50dp"
        android:gravity="center"
        android:numColumns="3"
        android:stretchMode="columnWidth" >
    </GridView>

</LinearLayout>

MainActivity.java
package com.example.androiddtmfpiano;

import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.GridView;
import android.widget.TextView;
import android.widget.Toast;
import android.os.Bundle;


public class MainActivity extends ActionBarActivity {
 
 GridView gridView;
  
 static final String[] numbers = new String[] { 
   "1", "2", "3",
   "4", "5", "6",
   "7", "8", "9",
   "*", "0", "#"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        gridView = (GridView) findViewById(R.id.gridView);
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(
         this, android.R.layout.simple_list_item_1, numbers);
        
        gridView.setAdapter(adapter);
        
        gridView.setOnItemClickListener(new OnItemClickListener() {

   @Override
   public void onItemClick(AdapterView<?> parent, View view,
     int position, long id) {
    Toast.makeText(getApplicationContext(),
      ((TextView)view).getText(), 
      Toast.LENGTH_SHORT).show();
   }});
    }

}


Sunday, September 21, 2014

Touch GridView to show photo

Further work on last exercise of GridView "Improve JPG loading with ExifInterface.getThumbnail()", to open select photo when any item clicked.



Create /res/layout/jpgdialog.xml to define the layout of dialog to show clicked photo.
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout_root"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:paddingLeft="10dip"
    android:paddingRight="10dip" >

    <TextView
        android:id="@+id/textpath"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    
    <ImageView
        android:id="@+id/image"
        android:layout_width="500dp"
        android:layout_height="350dp" />

    <Button
        android:id="@+id/okdialogbutton"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="OK" />

</LinearLayout>

Modify MainActivity.java to open dialog to show selected photo. In order to make it simple, the loading of photos for dialog is implemented in UI thread.
package com.example.androidgridview;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;

import android.media.ExifInterface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;

public class MainActivity extends Activity {
 
 AsyncTaskLoadFiles myAsyncTaskLoadFiles;
 
 static final int ID_JPGDIALOG = 0;
 ImageView jpgdialigImage;
 TextView jpgdialigText;
 File jpgdialigFile;
 //have to match width and height of 
 //"@+id/image" in jpgdialog.xml
 final int DIALOG_IMAGE_WIDTH = 500;
 final int DIALOG_IMAGE_HEIGHT = 350;

 public class AsyncTaskLoadFiles extends AsyncTask<Void, String, Void> {
  
  File targetDirector;
  ImageAdapter myTaskAdapter;

  public AsyncTaskLoadFiles(ImageAdapter adapter) {
   myTaskAdapter = adapter;
  }

  @Override
  protected void onPreExecute() {
   String ExternalStorageDirectoryPath = Environment
     .getExternalStorageDirectory().getAbsolutePath();

   String targetPath = ExternalStorageDirectoryPath + "/test/";
   targetDirector = new File(targetPath);
   myTaskAdapter.clear();
   
   super.onPreExecute();
  }

  @Override
  protected Void doInBackground(Void... params) {
   
   //open jpg only
   File[] files = targetDirector.listFiles(new FilenameFilter() {
       public boolean accept(File dir, String name)
       {
           return (name.endsWith(".jpg")||name.endsWith(".JPG")); 
       }
   });
   //File[] files = targetDirector.listFiles();
   
   Arrays.sort(files);
   for (File file : files) {
    publishProgress(file.getAbsolutePath());
    if (isCancelled()) break;
   }
   return null;
  }

  @Override
  protected void onProgressUpdate(String... values) {
   myTaskAdapter.add(values[0]);
   super.onProgressUpdate(values);
  }

  @Override
  protected void onPostExecute(Void result) {
   myTaskAdapter.notifyDataSetChanged();
   super.onPostExecute(result);
  }

 }

 public class ImageAdapter extends BaseAdapter {

  private Context mContext;
  ArrayList<String> itemList = new ArrayList<String>();

  public ImageAdapter(Context c) {
   mContext = c;
  }

  void add(String path) {
   itemList.add(path);
  }
  
  void clear() {
   itemList.clear();
  }
  
  void remove(int index){
   itemList.remove(index);
  }

  @Override
  public int getCount() {
   return itemList.size();
  }

  @Override
  public Object getItem(int position) {
   // TODO Auto-generated method stub
   return itemList.get(position);
  }

  @Override
  public long getItemId(int position) {
   // TODO Auto-generated method stub
   return 0;
  }

  //getView load bitmap in AsyncTask
  @Override
  public View getView(final int position, View convertView, ViewGroup parent) {
   ViewHolder holder;

   ImageView imageView;
   if (convertView == null) { // if it's not recycled, initialize some
          // attributes
    imageView = new ImageView(mContext);
    imageView.setLayoutParams(new GridView.LayoutParams(220, 220));
    imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
    imageView.setPadding(8, 8, 8, 8);
    
    convertView = imageView;
    
    holder = new ViewHolder();
    holder.image = imageView;
    holder.position = position;
    convertView.setTag(holder);
   } else {
    //imageView = (ImageView) convertView;
    holder = (ViewHolder) convertView.getTag();
    ((ImageView)convertView).setImageBitmap(null);
   }
   
   //Bitmap bm = decodeSampledBitmapFromUri(itemList.get(position), 220, 220);
   // Using an AsyncTask to load the slow images in a background thread
   new AsyncTask<ViewHolder, Void, Bitmap>() {
       private ViewHolder v;

       @Override
       protected Bitmap doInBackground(ViewHolder... params) {
        
        Bitmap bm = null;
        
        boolean haveThumbNail = false;
        
        try {
      ExifInterface exifInterface = 
       new ExifInterface(itemList.get(position));
      if(exifInterface.hasThumbnail()){
       byte[] thumbnail = exifInterface.getThumbnail();
       bm = BitmapFactory.decodeByteArray(thumbnail, 0, thumbnail.length);
      }
      haveThumbNail = true;
     } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
        
        if(!haveThumbNail){
         bm = decodeSampledBitmapFromUri(
           itemList.get(position), 220, 220);
        }

           v = params[0];
           return bm;
       }

       @Override
       protected void onPostExecute(Bitmap result) {
           super.onPostExecute(result);
           //Not work for me!
           /*
           if (v.position == position) {
               // If this item hasn't been recycled already, 
            // show the image
               v.image.setImageBitmap(result);
           }
           */

           v.image.setImageBitmap(result);

       }
   }.execute(holder);

   //imageView.setImageBitmap(bm);
   //return imageView;
   return convertView;
  }

  /*
  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;
  }
  */
  
  class ViewHolder {
            ImageView image;
            int position;
        }

 }

 ImageAdapter myImageAdapter;

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

  final GridView gridview = (GridView) findViewById(R.id.gridview);
  myImageAdapter = new ImageAdapter(this);
  gridview.setAdapter(myImageAdapter);

  myAsyncTaskLoadFiles = new AsyncTaskLoadFiles(myImageAdapter);
  myAsyncTaskLoadFiles.execute();

  gridview.setOnItemClickListener(myOnItemClickListener);
  
  Button buttonReload = (Button)findViewById(R.id.reload);
  buttonReload.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View arg0) {
    
    //Cancel the previous running task, if exist.
    myAsyncTaskLoadFiles.cancel(true);
    
    //new another ImageAdapter, to prevent the adapter have
    //mixed files
    myImageAdapter = new ImageAdapter(MainActivity.this);
    gridview.setAdapter(myImageAdapter);
    myAsyncTaskLoadFiles = new AsyncTaskLoadFiles(myImageAdapter);
    myAsyncTaskLoadFiles.execute();
   }});

 }

 OnItemClickListener myOnItemClickListener = new OnItemClickListener() {

  @Override
  public void onItemClick(AdapterView<?> parent, View view, int position,
    long id) {
   String path = (String) parent.getItemAtPosition(position);

   //Open dialog to show jpg
   jpgdialigFile = new File(path);
   
   showDialog(ID_JPGDIALOG);

  }
 };

 @Override
 @Deprecated
 protected Dialog onCreateDialog(int id) {
  final Dialog jpgDialog = new Dialog(this);
  switch(id){
  case ID_JPGDIALOG:

   jpgDialog.setContentView(R.layout.jpgdialog);
   jpgdialigImage = (ImageView)jpgDialog.findViewById(R.id.image);
   jpgdialigText = (TextView)jpgDialog.findViewById(R.id.textpath);
   
   Button okDialogButton = (Button)jpgDialog.findViewById(R.id.okdialogbutton);
   okDialogButton.setOnClickListener(new OnClickListener(){

    @Override
    public void onClick(View v) {
     jpgDialog.dismiss();
    }});
   break;
   
  default:
   break; 
  }
  
  return jpgDialog;
 }

 @Override
 @Deprecated
 protected void onPrepareDialog(int id, Dialog dialog) {
  switch(id){
  case ID_JPGDIALOG:
   jpgdialigText.setText(jpgdialigFile.getPath());
   //Bitmap bm = BitmapFactory.decodeFile(jpgdialigFile.getPath());
   Bitmap bm = decodeSampledBitmapFromUri(jpgdialigFile.getPath(), 
    DIALOG_IMAGE_WIDTH, DIALOG_IMAGE_HEIGHT);
   jpgdialigImage.setImageBitmap(bm);
   
   break;
   
  default:
   break;
   }
 }
 
 private 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 filesDownload the files.

- Modify this example to ListView

Wednesday, September 17, 2014

getView() to load images in AsyncTask

This post show getView() loading images in AsyncTask, to make UI responsive. Re-call my old "GridView example: load images to GridView from SD Card in background", it load the files in backgroud, to make the GridView start-up faster; but not in loading images in getView. Such that if the GridView going to load large images, it will block the UI in getView() and make the UI unresponsive.


The Android tutorial "Making ListView Scrolling Smooth" show how to improve getView() by loading images in AsyncTask.

The page advise to check (v.position == position) to varify if this item hasn't been recycled already. But it not work in this case. (actually, I don't understand how is the logic) In my implementation, roll-out images will be shown on incorrect cell, but the final images will replace and correct it finally.

To load images in AsyncTask will not speed up your image loading, but improve UI response.


MainActivity.java
package com.example.androidgridview;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;

import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.Toast;

public class MainActivity extends Activity {
 
 AsyncTaskLoadFiles myAsyncTaskLoadFiles;

 public class AsyncTaskLoadFiles extends AsyncTask<Void, String, Void> {
  
  File targetDirector;
  ImageAdapter myTaskAdapter;

  public AsyncTaskLoadFiles(ImageAdapter adapter) {
   myTaskAdapter = adapter;
  }

  @Override
  protected void onPreExecute() {
   String ExternalStorageDirectoryPath = Environment
     .getExternalStorageDirectory().getAbsolutePath();

   String targetPath = ExternalStorageDirectoryPath + "/test/";
   targetDirector = new File(targetPath);
   myTaskAdapter.clear();
   
   super.onPreExecute();
  }

  @Override
  protected Void doInBackground(Void... params) {
   
   File[] files = targetDirector.listFiles();
   Arrays.sort(files);
   for (File file : files) {
    publishProgress(file.getAbsolutePath());
    if (isCancelled()) break;
   }
   return null;
  }

  @Override
  protected void onProgressUpdate(String... values) {
   myTaskAdapter.add(values[0]);
   super.onProgressUpdate(values);
  }

  @Override
  protected void onPostExecute(Void result) {
   myTaskAdapter.notifyDataSetChanged();
   super.onPostExecute(result);
  }

 }

 public class ImageAdapter extends BaseAdapter {

  private Context mContext;
  ArrayList<String> itemList = new ArrayList<String>();

  public ImageAdapter(Context c) {
   mContext = c;
  }

  void add(String path) {
   itemList.add(path);
  }
  
  void clear() {
   itemList.clear();
  }
  
  void remove(int index){
   itemList.remove(index);
  }

  @Override
  public int getCount() {
   return itemList.size();
  }

  @Override
  public Object getItem(int position) {
   // TODO Auto-generated method stub
   return itemList.get(position);
  }

  @Override
  public long getItemId(int position) {
   // TODO Auto-generated method stub
   return 0;
  }

  //getView load bitmap ui thread
  /*
  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
   ImageView imageView;
   if (convertView == null) { // if it's not recycled, initialize some
          // attributes
    imageView = new ImageView(mContext);
    imageView.setLayoutParams(new GridView.LayoutParams(220, 220));
    imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
    imageView.setPadding(8, 8, 8, 8);
   } else {
    imageView = (ImageView) convertView;
   }

   Bitmap bm = decodeSampledBitmapFromUri(itemList.get(position), 220,
     220);

   imageView.setImageBitmap(bm);
   return imageView;
  }
  */

  //getView load bitmap in AsyncTask
  @Override
  public View getView(final int position, View convertView, ViewGroup parent) {
   ViewHolder holder;

   ImageView imageView;
   if (convertView == null) { // if it's not recycled, initialize some
          // attributes
    imageView = new ImageView(mContext);
    imageView.setLayoutParams(new GridView.LayoutParams(220, 220));
    imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
    imageView.setPadding(8, 8, 8, 8);
    
    convertView = imageView;
    
    holder = new ViewHolder();
    holder.image = imageView;
    holder.position = position;
    convertView.setTag(holder);
   } else {
    //imageView = (ImageView) convertView;
    holder = (ViewHolder) convertView.getTag();
    ((ImageView)convertView).setImageBitmap(null);
   }
   
   //Bitmap bm = decodeSampledBitmapFromUri(itemList.get(position), 220, 220);
   // Using an AsyncTask to load the slow images in a background thread
   new AsyncTask<ViewHolder, Void, Bitmap>() {
       private ViewHolder v;

       @Override
       protected Bitmap doInBackground(ViewHolder... params) {
           v = params[0];
           Bitmap bm = decodeSampledBitmapFromUri(itemList.get(position), 220, 220);
           return bm;
       }

       @Override
       protected void onPostExecute(Bitmap result) {
           super.onPostExecute(result);
           //Not work for me!
           /*
           if (v.position == position) {
               // If this item hasn't been recycled already, 
            // show the image
               v.image.setImageBitmap(result);
           }
           */

           v.image.setImageBitmap(result);

       }
   }.execute(holder);

   //imageView.setImageBitmap(bm);
   //return imageView;
   return convertView;
  }

  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;
  }
  
  class ViewHolder {
            ImageView image;
            int position;
        }

 }

 ImageAdapter myImageAdapter;

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

  final GridView gridview = (GridView) findViewById(R.id.gridview);
  myImageAdapter = new ImageAdapter(this);
  gridview.setAdapter(myImageAdapter);

  /*
   * Move to asyncTaskLoadFiles 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){
   * myImageAdapter.add(file.getAbsolutePath()); }
   */
  myAsyncTaskLoadFiles = new AsyncTaskLoadFiles(myImageAdapter);
  myAsyncTaskLoadFiles.execute();

  gridview.setOnItemClickListener(myOnItemClickListener);
  
  Button buttonReload = (Button)findViewById(R.id.reload);
  buttonReload.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View arg0) {
    
    //Cancel the previous running task, if exist.
    myAsyncTaskLoadFiles.cancel(true);
    
    //new another ImageAdapter, to prevent the adapter have
    //mixed files
    myImageAdapter = new ImageAdapter(MainActivity.this);
    gridview.setAdapter(myImageAdapter);
    myAsyncTaskLoadFiles = new AsyncTaskLoadFiles(myImageAdapter);
    myAsyncTaskLoadFiles.execute();
   }});

 }

 OnItemClickListener myOnItemClickListener = new OnItemClickListener() {

  @Override
  public void onItemClick(AdapterView<?> parent, View view, int position,
    long id) {
   String prompt = "remove " + (String) parent.getItemAtPosition(position);
   Toast.makeText(getApplicationContext(), prompt, Toast.LENGTH_SHORT)
     .show();
   
   myImageAdapter.remove(position);
   myImageAdapter.notifyDataSetChanged();

  }
 };

}

/res/layout/activity_main.xml
<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">

    <Button
        android:id="@+id/reload"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Reload"/>
    <GridView
        android:id="@+id/gridview"
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent"
        android:columnWidth="220dp"
        android:numColumns="auto_fit"
        android:verticalSpacing="10dp"
        android:horizontalSpacing="10dp"
        android:stretchMode="columnWidth"
        android:gravity="center"/>

</LinearLayout>

Permission of "android.permission.READ_EXTERNAL_STORAGE" is needed to read storage.

download filesDownload the files.

Next:
Improve JPG loading with ExifInterface.getThumbnail()