Custom Listview with Separate Headers
Wednesday, 30 October 2013
Sunday, 25 August 2013
Android Custom ListView with Seperate Header
In this tutorial we will display a custom listview with header like,
........
Android Layout Files.
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView
android:id="@+id/Listview"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
list_item_header.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list_header_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Large Text"
android:textAppearance="?android:attr/textAppearanceLarge" />
home_list_item_view.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dip" >
<LinearLayout
android:id="@+id/thumbnail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginRight="5dip"
android:padding="3dip" >
<ImageView
android:id="@+id/list_image"
android:layout_width="50dip"
android:layout_height="50dip"
android:scaleType="fitXY"
android:src="@drawable/ic_launcher" />
</LinearLayout>
<LinearLayout
android:id="@+id/detailsLL"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/BtnClose"
android:layout_toRightOf="@+id/thumbnail"
android:orientation="vertical" >
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Name"
android:textColor="#040404"
android:textSize="15dip"
android:textStyle="bold"
android:typeface="sans" />
<TextView
android:id="@+id/description"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="1dip"
android:maxLines="2"
android:text="Description"
android:textColor="#343434"
android:textSize="10dip" />
</LinearLayout>
</RelativeLayout>
Java Files.
MainActivity.Java
import java.util.ArrayList;
import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Toast;
public class MainActivity extends Activity {
private ListView listView;
private HomeListAdapter HomeListAdapter;
private SeparatedListAdapter adapter;
private ArrayList<HomeItem> HomeItemList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
listView = (ListView) findViewById(R.id.Listview);
listView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
HomeItem homeItem = (HomeItem) adapter.getItem(position);
Toast.makeText(getBaseContext(), homeItem.getHomeItemName().toString(), Toast.LENGTH_LONG).show();
System.out.println("Selected Item : " + homeItem.getHomeItemID());
}
});
PopulateHomePageItemList();
}
private void PopulateHomePageItemList(){
adapter = new SeparatedListAdapter(this);
HomeItemList = new ArrayList<HomeItem>();
for(int i =0; i< 4 ; i++){
HomeItem homeItem = new HomeItem();
homeItem.setHomeItemID(i);
homeItem.setHomeItemName("Name"+i);
homeItem.setHomeItemDescription("Description"+i);
homeItem.setImageUrl("https://www.google.co.in/images/srpr/logo4w.png");
HomeItemList.add(homeItem);
}
HomeListAdapter = new HomeListAdapter(getApplicationContext(), 0, HomeItemList);
if(HomeListAdapter!= null)
adapter.addSection(" ONE ", HomeListAdapter );
HomeItemList = new ArrayList<HomeItem>();
for(int i = 5; i< 8 ; i++){
HomeItem homeItem = new HomeItem();
homeItem.setHomeItemID(i);
homeItem.setHomeItemName("Name"+i);
homeItem.setHomeItemDescription("Description"+i);
homeItem.setImageUrl("https://www.google.co.in/images/srpr/logo4w.png");
HomeItemList.add(homeItem);
}
HomeListAdapter = new HomeListAdapter(getApplicationContext(), 0, HomeItemList);
if(HomeListAdapter!= null)
adapter.addSection(" TWO ", HomeListAdapter );
HomeItemList = new ArrayList<HomeItem>();
for(int i =9; i< 12 ; i++){
HomeItem homeItem = new HomeItem();
homeItem.setHomeItemID(i);
homeItem.setHomeItemName("Name"+i);
homeItem.setHomeItemDescription("Description"+i);
homeItem.setImageUrl("https://www.google.co.in/images/srpr/logo4w.png");
HomeItemList.add(homeItem);
}
HomeListAdapter = new HomeListAdapter(getApplicationContext(), 0, HomeItemList);
if(HomeListAdapter!= null)
adapter.addSection(" THREE ", HomeListAdapter );
listView.setAdapter(adapter);
}
}
HomeListAdapter.java
import java.util.ArrayList;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class HomeListAdapter extends ArrayAdapter<HomeItem> implements OnClickListener {
public Context context;
public ArrayList<HomeItem> HomeItemList;
public ImageLoader imageLoader;
public HomeListAdapter(Context context, int resource, ArrayList<HomeItem> HomeItemLst) {
super(context, resource, HomeItemLst);
this.context = context;
this.HomeItemList = HomeItemLst;
imageLoader = new ImageLoader(context);
}
@Override
public int getCount() {
if (HomeItemList != null)
return HomeItemList.size();
return 0;
}
@Override
public HomeItem getItem(int position) {
return HomeItemList.get(position);
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
View myConvertView = null;
try {
final HomeItem HomeItem = HomeItemList.get(position);
myConvertView = convertView;
if (myConvertView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
myConvertView = inflater.inflate(R.layout.home_list_item_view, null);
}
ImageView CatImage = (ImageView) myConvertView.findViewById(R.id.list_image);
TextView CatHeader = (TextView) myConvertView.findViewById(R.id.name);
TextView CatTitle = (TextView) myConvertView.findViewById(R.id.description);
CatHeader.setText(HomeItem.getHomeItemName());
CatTitle.setText(HomeItem.getHomeItemDescription());
imageLoader.DisplayImage(HomeItem.getImageUrl(), CatImage);
} catch (Exception e) {
e.printStackTrace();
}
return myConvertView;
}
@Override
public long getItemId(int position) {
return position;
}
public void onClick(View v) {
// TODO Auto-generated method stub
}
}
SeparatedListAdapter.java
import java.util.LinkedHashMap;
import java.util.Map;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Adapter;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
public class SeparatedListAdapter extends BaseAdapter
{
public final Map<String, Adapter> sections = new LinkedHashMap<String, Adapter>();
public final ArrayAdapter<String> headers;
public final static int TYPE_SECTION_HEADER = 0;
public SeparatedListAdapter(Context context)
{
headers = new ArrayAdapter<String>(context, R.layout.list_item_header);
}
public void addSection(String section, Adapter adapter)
{
this.headers.add(section);
this.sections.put(section, adapter);
}
public Object getItem(int position)
{
for (Object section : this.sections.keySet())
{
Adapter adapter = sections.get(section);
int size = adapter.getCount() + 1;
// check if position inside this section
if (position == 0) return section;
if (position < size) return adapter.getItem(position - 1);
// otherwise jump into next section
position -= size;
}
return null;
}
public int getCount()
{
// total together all sections, plus one for each section header
int total = 0;
for (Adapter adapter : this.sections.values())
total += adapter.getCount() + 1;
return total;
}
@Override
public int getViewTypeCount()
{
// assume that headers count as one, then total all sections
int total = 1;
for (Adapter adapter : this.sections.values())
total += adapter.getViewTypeCount();
return total;
}
@Override
public int getItemViewType(int position)
{
int type = 1;
for (Object section : this.sections.keySet())
{
Adapter adapter = sections.get(section);
int size = adapter.getCount()+1;
// check if position inside this section
if (position == 0) return TYPE_SECTION_HEADER;
if (position < size) return type + adapter.getItemViewType(position - 1);
// otherwise jump into next section
position -= size;
type += adapter.getViewTypeCount();
}
return -1;
}
public boolean areAllItemsSelectable()
{
return false;
}
@Override
public boolean isEnabled(int position)
{
return (getItemViewType(position) != TYPE_SECTION_HEADER);
}
public View getView(int position, View convertView, ViewGroup parent)
{
int sectionnum = 0;
for (Object section : this.sections.keySet())
{
Adapter adapter = sections.get(section);
int size = adapter.getCount() + 1;
// check if position inside this section
if (position == 0) return headers.getView(sectionnum, convertView, parent);
if (position < size) return adapter.getView(position - 1, convertView, parent);
// otherwise jump into next section
position -= size;
sectionnum++;
}
return null;
}
public long getItemId(int position)
{
return position;
}
}
HomeItem.java
public class HomeItem {
private String ImageUrl;
private int HomeItemID;
private String HomeItemName;
private String HomeItemDescription;
public String getImageUrl() {
return ImageUrl;
}
public void setImageUrl(String imageUrl) {
this.ImageUrl = imageUrl;
}
public int getHomeItemID() {
return HomeItemID;
}
public void setHomeItemID(int ID) {
this.HomeItemID = ID;
}
public String getHomeItemName() {
return HomeItemName;
}
public void setHomeItemName(String Name) {
this.HomeItemName = Name;
}
public String getHomeItemDescription() {
return HomeItemDescription;
}
public void setHomeItemDescription(String Description) {
this.HomeItemDescription = Description;
}
}
ImageLoader.java
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.widget.ImageView;
public class ImageLoader {
MemoryCache memoryCache=new MemoryCache();
FileCache fileCache;
private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
ExecutorService executorService;
Handler handler=new Handler();//handler to display images in UI thread
public ImageLoader(Context context){
fileCache=new FileCache(context);
executorService=Executors.newFixedThreadPool(5);
}
final int stub_id=R.drawable.ic_launcher;
public void DisplayImage(String url, ImageView imageView)
{
imageViews.put(imageView, url);
Bitmap bitmap=memoryCache.get(url);
if(bitmap!=null)
imageView.setImageBitmap(bitmap);
else
{
queuePhoto(url, imageView);
imageView.setImageResource(stub_id);
}
}
private void queuePhoto(String url, ImageView imageView)
{
PhotoToLoad p=new PhotoToLoad(url, imageView);
executorService.submit(new PhotosLoader(p));
}
private Bitmap getBitmap(String url)
{
File f=fileCache.getFile(url);
//from SD cache
Bitmap b = decodeFile(f);
if(b!=null)
return b;
//from web
try {
Bitmap bitmap=null;
URL imageUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
conn.setConnectTimeout(30000);
conn.setReadTimeout(30000);
conn.setInstanceFollowRedirects(true);
InputStream is=conn.getInputStream();
OutputStream os = new FileOutputStream(f);
Utils.CopyStream(is, os);
os.close();
conn.disconnect();
bitmap = decodeFile(f);
return bitmap;
} catch (Throwable ex){
ex.printStackTrace();
if(ex instanceof OutOfMemoryError)
memoryCache.clear();
return null;
}
}
//decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f){
try {
//decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
FileInputStream stream1=new FileInputStream(f);
BitmapFactory.decodeStream(stream1,null,o);
stream1.close();
//Find the correct scale value. It should be the power of 2.
final int REQUIRED_SIZE=70;
int width_tmp=o.outWidth, height_tmp=o.outHeight;
int scale=1;
while(true){
if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
break;
width_tmp/=2;
height_tmp/=2;
scale*=2;
}
//decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
FileInputStream stream2=new FileInputStream(f);
Bitmap bitmap=BitmapFactory.decodeStream(stream2, null, o2);
stream2.close();
return bitmap;
} catch (FileNotFoundException e) {
}
catch (IOException e) {
e.printStackTrace();
}
return null;
}
//Task for the queue
private class PhotoToLoad
{
public String url;
public ImageView imageView;
public PhotoToLoad(String u, ImageView i){
url=u;
imageView=i;
}
}
class PhotosLoader implements Runnable {
PhotoToLoad photoToLoad;
PhotosLoader(PhotoToLoad photoToLoad){
this.photoToLoad=photoToLoad;
}
public void run() {
try{
if(imageViewReused(photoToLoad))
return;
Bitmap bmp=getBitmap(photoToLoad.url);
memoryCache.put(photoToLoad.url, bmp);
if(imageViewReused(photoToLoad))
return;
BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad);
handler.post(bd);
}catch(Throwable th){
th.printStackTrace();
}
}
}
boolean imageViewReused(PhotoToLoad photoToLoad){
String tag=imageViews.get(photoToLoad.imageView);
if(tag==null || !tag.equals(photoToLoad.url))
return true;
return false;
}
//Used to display bitmap in the UI thread
class BitmapDisplayer implements Runnable
{
Bitmap bitmap;
PhotoToLoad photoToLoad;
public BitmapDisplayer(Bitmap b, PhotoToLoad p){bitmap=b;photoToLoad=p;}
public void run()
{
if(imageViewReused(photoToLoad))
return;
if(bitmap!=null)
photoToLoad.imageView.setImageBitmap(bitmap);
else
photoToLoad.imageView.setImageResource(stub_id);
}
}
public void clearCache() {
memoryCache.clear();
fileCache.clear();
}
}
FileCache.java
import java.io.File;
import android.content.Context;
public class FileCache {
private File cacheDir;
public FileCache(Context context){
//Find the dir to save cached images
if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"LazyList");
else
cacheDir=context.getCacheDir();
if(!cacheDir.exists())
cacheDir.mkdirs();
}
public File getFile(String url){
//I identify images by hashcode. Not a perfect solution, good for the demo.
String filename=String.valueOf(url.hashCode());
//Another possible solution (thanks to grantland)
//String filename = URLEncoder.encode(url);
File f = new File(cacheDir, filename);
return f;
}
public void clear(){
File[] files=cacheDir.listFiles();
if(files==null)
return;
for(File f:files)
f.delete();
}
}
MemoryCache.java
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import android.graphics.Bitmap;
import android.util.Log;
public class MemoryCache {
private static final String TAG = "MemoryCache";
private Map<String, Bitmap> cache=Collections.synchronizedMap(
new LinkedHashMap<String, Bitmap>(10,1.5f,true));//Last argument true for LRU ordering
private long size=0;//current allocated size
private long limit=1000000;//max memory in bytes
public MemoryCache(){
//use 25% of available heap size
setLimit(Runtime.getRuntime().maxMemory()/4);
}
public void setLimit(long new_limit){
limit=new_limit;
Log.i(TAG, "MemoryCache will use up to "+limit/1024./1024.+"MB");
}
public Bitmap get(String id){
try{
if(!cache.containsKey(id))
return null;
//NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78
return cache.get(id);
}catch(NullPointerException ex){
ex.printStackTrace();
return null;
}
}
public void put(String id, Bitmap bitmap){
try{
if(cache.containsKey(id))
size-=getSizeInBytes(cache.get(id));
cache.put(id, bitmap);
size+=getSizeInBytes(bitmap);
checkSize();
}catch(Throwable th){
th.printStackTrace();
}
}
private void checkSize() {
Log.i(TAG, "cache size="+size+" length="+cache.size());
if(size>limit){
Iterator<Entry<String, Bitmap>> iter=cache.entrySet().iterator();//least recently accessed item will be the first one iterated
while(iter.hasNext()){
Entry<String, Bitmap> entry=iter.next();
size-=getSizeInBytes(entry.getValue());
iter.remove();
if(size<=limit)
break;
}
Log.i(TAG, "Clean cache. New size "+cache.size());
}
}
public void clear() {
try{
//NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78
cache.clear();
size=0;
}catch(NullPointerException ex){
ex.printStackTrace();
}
}
long getSizeInBytes(Bitmap bitmap) {
if(bitmap==null)
return 0;
return bitmap.getRowBytes() * bitmap.getHeight();
}
}
Utils.java
import java.io.InputStream;
import java.io.OutputStream;
public class Utils {
public static void CopyStream(InputStream is, OutputStream os)
{
final int buffer_size=1024;
try
{
byte[] bytes=new byte[buffer_size];
for(;;)
{
int count=is.read(bytes, 0, buffer_size);
if(count==-1)
break;
os.write(bytes, 0, count);
}
}
catch(Exception ex){}
}
}
........
Android Layout Files.
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView
android:id="@+id/Listview"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
list_item_header.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list_header_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Large Text"
android:textAppearance="?android:attr/textAppearanceLarge" />
home_list_item_view.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dip" >
<LinearLayout
android:id="@+id/thumbnail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginRight="5dip"
android:padding="3dip" >
<ImageView
android:id="@+id/list_image"
android:layout_width="50dip"
android:layout_height="50dip"
android:scaleType="fitXY"
android:src="@drawable/ic_launcher" />
</LinearLayout>
<LinearLayout
android:id="@+id/detailsLL"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/BtnClose"
android:layout_toRightOf="@+id/thumbnail"
android:orientation="vertical" >
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Name"
android:textColor="#040404"
android:textSize="15dip"
android:textStyle="bold"
android:typeface="sans" />
<TextView
android:id="@+id/description"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="1dip"
android:maxLines="2"
android:text="Description"
android:textColor="#343434"
android:textSize="10dip" />
</LinearLayout>
</RelativeLayout>
Java Files.
MainActivity.Java
import java.util.ArrayList;
import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Toast;
public class MainActivity extends Activity {
private ListView listView;
private HomeListAdapter HomeListAdapter;
private SeparatedListAdapter adapter;
private ArrayList<HomeItem> HomeItemList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
listView = (ListView) findViewById(R.id.Listview);
listView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
HomeItem homeItem = (HomeItem) adapter.getItem(position);
Toast.makeText(getBaseContext(), homeItem.getHomeItemName().toString(), Toast.LENGTH_LONG).show();
System.out.println("Selected Item : " + homeItem.getHomeItemID());
}
});
PopulateHomePageItemList();
}
private void PopulateHomePageItemList(){
adapter = new SeparatedListAdapter(this);
HomeItemList = new ArrayList<HomeItem>();
for(int i =0; i< 4 ; i++){
HomeItem homeItem = new HomeItem();
homeItem.setHomeItemID(i);
homeItem.setHomeItemName("Name"+i);
homeItem.setHomeItemDescription("Description"+i);
homeItem.setImageUrl("https://www.google.co.in/images/srpr/logo4w.png");
HomeItemList.add(homeItem);
}
HomeListAdapter = new HomeListAdapter(getApplicationContext(), 0, HomeItemList);
if(HomeListAdapter!= null)
adapter.addSection(" ONE ", HomeListAdapter );
HomeItemList = new ArrayList<HomeItem>();
for(int i = 5; i< 8 ; i++){
HomeItem homeItem = new HomeItem();
homeItem.setHomeItemID(i);
homeItem.setHomeItemName("Name"+i);
homeItem.setHomeItemDescription("Description"+i);
homeItem.setImageUrl("https://www.google.co.in/images/srpr/logo4w.png");
HomeItemList.add(homeItem);
}
HomeListAdapter = new HomeListAdapter(getApplicationContext(), 0, HomeItemList);
if(HomeListAdapter!= null)
adapter.addSection(" TWO ", HomeListAdapter );
HomeItemList = new ArrayList<HomeItem>();
for(int i =9; i< 12 ; i++){
HomeItem homeItem = new HomeItem();
homeItem.setHomeItemID(i);
homeItem.setHomeItemName("Name"+i);
homeItem.setHomeItemDescription("Description"+i);
homeItem.setImageUrl("https://www.google.co.in/images/srpr/logo4w.png");
HomeItemList.add(homeItem);
}
HomeListAdapter = new HomeListAdapter(getApplicationContext(), 0, HomeItemList);
if(HomeListAdapter!= null)
adapter.addSection(" THREE ", HomeListAdapter );
listView.setAdapter(adapter);
}
}
HomeListAdapter.java
import java.util.ArrayList;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class HomeListAdapter extends ArrayAdapter<HomeItem> implements OnClickListener {
public Context context;
public ArrayList<HomeItem> HomeItemList;
public ImageLoader imageLoader;
public HomeListAdapter(Context context, int resource, ArrayList<HomeItem> HomeItemLst) {
super(context, resource, HomeItemLst);
this.context = context;
this.HomeItemList = HomeItemLst;
imageLoader = new ImageLoader(context);
}
@Override
public int getCount() {
if (HomeItemList != null)
return HomeItemList.size();
return 0;
}
@Override
public HomeItem getItem(int position) {
return HomeItemList.get(position);
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
View myConvertView = null;
try {
final HomeItem HomeItem = HomeItemList.get(position);
myConvertView = convertView;
if (myConvertView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
myConvertView = inflater.inflate(R.layout.home_list_item_view, null);
}
ImageView CatImage = (ImageView) myConvertView.findViewById(R.id.list_image);
TextView CatHeader = (TextView) myConvertView.findViewById(R.id.name);
TextView CatTitle = (TextView) myConvertView.findViewById(R.id.description);
CatHeader.setText(HomeItem.getHomeItemName());
CatTitle.setText(HomeItem.getHomeItemDescription());
imageLoader.DisplayImage(HomeItem.getImageUrl(), CatImage);
} catch (Exception e) {
e.printStackTrace();
}
return myConvertView;
}
@Override
public long getItemId(int position) {
return position;
}
public void onClick(View v) {
// TODO Auto-generated method stub
}
}
SeparatedListAdapter.java
import java.util.LinkedHashMap;
import java.util.Map;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Adapter;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
public class SeparatedListAdapter extends BaseAdapter
{
public final Map<String, Adapter> sections = new LinkedHashMap<String, Adapter>();
public final ArrayAdapter<String> headers;
public final static int TYPE_SECTION_HEADER = 0;
public SeparatedListAdapter(Context context)
{
headers = new ArrayAdapter<String>(context, R.layout.list_item_header);
}
public void addSection(String section, Adapter adapter)
{
this.headers.add(section);
this.sections.put(section, adapter);
}
public Object getItem(int position)
{
for (Object section : this.sections.keySet())
{
Adapter adapter = sections.get(section);
int size = adapter.getCount() + 1;
// check if position inside this section
if (position == 0) return section;
if (position < size) return adapter.getItem(position - 1);
// otherwise jump into next section
position -= size;
}
return null;
}
public int getCount()
{
// total together all sections, plus one for each section header
int total = 0;
for (Adapter adapter : this.sections.values())
total += adapter.getCount() + 1;
return total;
}
@Override
public int getViewTypeCount()
{
// assume that headers count as one, then total all sections
int total = 1;
for (Adapter adapter : this.sections.values())
total += adapter.getViewTypeCount();
return total;
}
@Override
public int getItemViewType(int position)
{
int type = 1;
for (Object section : this.sections.keySet())
{
Adapter adapter = sections.get(section);
int size = adapter.getCount()+1;
// check if position inside this section
if (position == 0) return TYPE_SECTION_HEADER;
if (position < size) return type + adapter.getItemViewType(position - 1);
// otherwise jump into next section
position -= size;
type += adapter.getViewTypeCount();
}
return -1;
}
public boolean areAllItemsSelectable()
{
return false;
}
@Override
public boolean isEnabled(int position)
{
return (getItemViewType(position) != TYPE_SECTION_HEADER);
}
public View getView(int position, View convertView, ViewGroup parent)
{
int sectionnum = 0;
for (Object section : this.sections.keySet())
{
Adapter adapter = sections.get(section);
int size = adapter.getCount() + 1;
// check if position inside this section
if (position == 0) return headers.getView(sectionnum, convertView, parent);
if (position < size) return adapter.getView(position - 1, convertView, parent);
// otherwise jump into next section
position -= size;
sectionnum++;
}
return null;
}
public long getItemId(int position)
{
return position;
}
}
HomeItem.java
public class HomeItem {
private String ImageUrl;
private int HomeItemID;
private String HomeItemName;
private String HomeItemDescription;
public String getImageUrl() {
return ImageUrl;
}
public void setImageUrl(String imageUrl) {
this.ImageUrl = imageUrl;
}
public int getHomeItemID() {
return HomeItemID;
}
public void setHomeItemID(int ID) {
this.HomeItemID = ID;
}
public String getHomeItemName() {
return HomeItemName;
}
public void setHomeItemName(String Name) {
this.HomeItemName = Name;
}
public String getHomeItemDescription() {
return HomeItemDescription;
}
public void setHomeItemDescription(String Description) {
this.HomeItemDescription = Description;
}
}
ImageLoader.java
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.widget.ImageView;
public class ImageLoader {
MemoryCache memoryCache=new MemoryCache();
FileCache fileCache;
private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
ExecutorService executorService;
Handler handler=new Handler();//handler to display images in UI thread
public ImageLoader(Context context){
fileCache=new FileCache(context);
executorService=Executors.newFixedThreadPool(5);
}
final int stub_id=R.drawable.ic_launcher;
public void DisplayImage(String url, ImageView imageView)
{
imageViews.put(imageView, url);
Bitmap bitmap=memoryCache.get(url);
if(bitmap!=null)
imageView.setImageBitmap(bitmap);
else
{
queuePhoto(url, imageView);
imageView.setImageResource(stub_id);
}
}
private void queuePhoto(String url, ImageView imageView)
{
PhotoToLoad p=new PhotoToLoad(url, imageView);
executorService.submit(new PhotosLoader(p));
}
private Bitmap getBitmap(String url)
{
File f=fileCache.getFile(url);
//from SD cache
Bitmap b = decodeFile(f);
if(b!=null)
return b;
//from web
try {
Bitmap bitmap=null;
URL imageUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
conn.setConnectTimeout(30000);
conn.setReadTimeout(30000);
conn.setInstanceFollowRedirects(true);
InputStream is=conn.getInputStream();
OutputStream os = new FileOutputStream(f);
Utils.CopyStream(is, os);
os.close();
conn.disconnect();
bitmap = decodeFile(f);
return bitmap;
} catch (Throwable ex){
ex.printStackTrace();
if(ex instanceof OutOfMemoryError)
memoryCache.clear();
return null;
}
}
//decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f){
try {
//decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
FileInputStream stream1=new FileInputStream(f);
BitmapFactory.decodeStream(stream1,null,o);
stream1.close();
//Find the correct scale value. It should be the power of 2.
final int REQUIRED_SIZE=70;
int width_tmp=o.outWidth, height_tmp=o.outHeight;
int scale=1;
while(true){
if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
break;
width_tmp/=2;
height_tmp/=2;
scale*=2;
}
//decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
FileInputStream stream2=new FileInputStream(f);
Bitmap bitmap=BitmapFactory.decodeStream(stream2, null, o2);
stream2.close();
return bitmap;
} catch (FileNotFoundException e) {
}
catch (IOException e) {
e.printStackTrace();
}
return null;
}
//Task for the queue
private class PhotoToLoad
{
public String url;
public ImageView imageView;
public PhotoToLoad(String u, ImageView i){
url=u;
imageView=i;
}
}
class PhotosLoader implements Runnable {
PhotoToLoad photoToLoad;
PhotosLoader(PhotoToLoad photoToLoad){
this.photoToLoad=photoToLoad;
}
public void run() {
try{
if(imageViewReused(photoToLoad))
return;
Bitmap bmp=getBitmap(photoToLoad.url);
memoryCache.put(photoToLoad.url, bmp);
if(imageViewReused(photoToLoad))
return;
BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad);
handler.post(bd);
}catch(Throwable th){
th.printStackTrace();
}
}
}
boolean imageViewReused(PhotoToLoad photoToLoad){
String tag=imageViews.get(photoToLoad.imageView);
if(tag==null || !tag.equals(photoToLoad.url))
return true;
return false;
}
//Used to display bitmap in the UI thread
class BitmapDisplayer implements Runnable
{
Bitmap bitmap;
PhotoToLoad photoToLoad;
public BitmapDisplayer(Bitmap b, PhotoToLoad p){bitmap=b;photoToLoad=p;}
public void run()
{
if(imageViewReused(photoToLoad))
return;
if(bitmap!=null)
photoToLoad.imageView.setImageBitmap(bitmap);
else
photoToLoad.imageView.setImageResource(stub_id);
}
}
public void clearCache() {
memoryCache.clear();
fileCache.clear();
}
}
FileCache.java
import java.io.File;
import android.content.Context;
public class FileCache {
private File cacheDir;
public FileCache(Context context){
//Find the dir to save cached images
if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"LazyList");
else
cacheDir=context.getCacheDir();
if(!cacheDir.exists())
cacheDir.mkdirs();
}
public File getFile(String url){
//I identify images by hashcode. Not a perfect solution, good for the demo.
String filename=String.valueOf(url.hashCode());
//Another possible solution (thanks to grantland)
//String filename = URLEncoder.encode(url);
File f = new File(cacheDir, filename);
return f;
}
public void clear(){
File[] files=cacheDir.listFiles();
if(files==null)
return;
for(File f:files)
f.delete();
}
}
MemoryCache.java
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import android.graphics.Bitmap;
import android.util.Log;
public class MemoryCache {
private static final String TAG = "MemoryCache";
private Map<String, Bitmap> cache=Collections.synchronizedMap(
new LinkedHashMap<String, Bitmap>(10,1.5f,true));//Last argument true for LRU ordering
private long size=0;//current allocated size
private long limit=1000000;//max memory in bytes
public MemoryCache(){
//use 25% of available heap size
setLimit(Runtime.getRuntime().maxMemory()/4);
}
public void setLimit(long new_limit){
limit=new_limit;
Log.i(TAG, "MemoryCache will use up to "+limit/1024./1024.+"MB");
}
public Bitmap get(String id){
try{
if(!cache.containsKey(id))
return null;
//NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78
return cache.get(id);
}catch(NullPointerException ex){
ex.printStackTrace();
return null;
}
}
public void put(String id, Bitmap bitmap){
try{
if(cache.containsKey(id))
size-=getSizeInBytes(cache.get(id));
cache.put(id, bitmap);
size+=getSizeInBytes(bitmap);
checkSize();
}catch(Throwable th){
th.printStackTrace();
}
}
private void checkSize() {
Log.i(TAG, "cache size="+size+" length="+cache.size());
if(size>limit){
Iterator<Entry<String, Bitmap>> iter=cache.entrySet().iterator();//least recently accessed item will be the first one iterated
while(iter.hasNext()){
Entry<String, Bitmap> entry=iter.next();
size-=getSizeInBytes(entry.getValue());
iter.remove();
if(size<=limit)
break;
}
Log.i(TAG, "Clean cache. New size "+cache.size());
}
}
public void clear() {
try{
//NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78
cache.clear();
size=0;
}catch(NullPointerException ex){
ex.printStackTrace();
}
}
long getSizeInBytes(Bitmap bitmap) {
if(bitmap==null)
return 0;
return bitmap.getRowBytes() * bitmap.getHeight();
}
}
Utils.java
import java.io.InputStream;
import java.io.OutputStream;
public class Utils {
public static void CopyStream(InputStream is, OutputStream os)
{
final int buffer_size=1024;
try
{
byte[] bytes=new byte[buffer_size];
for(;;)
{
int count=is.read(bytes, 0, buffer_size);
if(count==-1)
break;
os.write(bytes, 0, count);
}
}
catch(Exception ex){}
}
}
Click Here to Read my other Blogs..
Subscribe to:
Posts (Atom)