I am building an sms app which lists the contact pic,name,message,date of a conversation. I'm using a custom adapter to list them.Below is the code
public class ConvRowAdapter extends ArrayAdapter<ConvItem> {
private Context my_context;
// View lookup cache
private static class ViewHolder {
TextView name,body,date;
ImageView img;
}
public ConvRowAdapter(Context context, ArrayList<ConvItem> ConvItems) {
super(context, R.layout.convitem, ConvItems);
my_context=context;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
ConvItem ConvItem = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
ViewHolder viewHolder; // view lookup cache stored in tag
if (convertView == null) {
viewHolder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(R.layout.convitem, null);
viewHolder.img = (ImageView) convertView.findViewById(R.id.iv_photo);
viewHolder.name = (TextView) convertView.findViewById(R.id.tv_name);
viewHolder.date = (TextView) convertView.findViewById(R.id.tv_date);
viewHolder.body = (TextView) convertView.findViewById(R.id.tv_body);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
// Populate the data into the template view using the data object
if(ConvItem.getPhotoUri()!=null)
{
Bitmap bitmap=BitmapFactory.decodeStream(my_context.getContentResolver().openInputStream(ConvItem.getPhotoUri()));
viewHolder.img.setImageBitmap(bitmap);//Image is set
}
else
{
Bitmap bitmap=BitmapFactory.decodeResource(my_context.getResources(),R.drawable.contact_blue);
viewHolder.img.setImageBitmap(bitmap);
}
if(ConvItem.getDisplayName()==null)
viewHolder.name.setText(ConvItem.getAddress());//Phone number set
else
viewHolder.name.setText(ConvItem.getDisplayName());//If phone number exists in contacts display contact name
viewHolder.date.setText(ConvItem.getDate());//set date
viewHolder.body.setText(ConvItem.getBody());//setting the body of the message
// Return the completed view to render on screen
return convertView;
}
}
It takes 8-9 seconds to display the list. Is there a way to improve the speed? If so please post the correct way to do so.
Thanks in advance :)
Edited:
To check if the delay in loading is caused by the loading of images i tried deleting all photo setting statements and ran it.Still the loading takes the same time. By using a simplecursoradapter i could display the body,date and message in no delay.But to display the photo with them i tried using the custom adapter. This is the function which returns all sms into an arraylist.Please check the efficiency of the code:
public ArrayList<ConvItem> getSMS(){
ArrayList<ConvItem> ConvItems = new ArrayList<ConvItem>();
Uri uriSMSURI = Uri.parse("content://mms-sms/conversations?simple=true");
Cursor cur = getActivity().getContentResolver().query(uriSMSURI, null, null, null,
"date desc");
cur.moveToFirst();
while (!cur.isAfterLast()) {
ConvItem ConvItem = new ConvItem();
String address = null, body = null, dname = null,res=null,id_dummy=null;
Long date=null;
Uri uriphotoid=null;
//to obtain address from canonical-addresses
res = cur.getString(cur.getColumnIndex("recipient_ids"));
Uri ad =Uri.parse("content://mms-sms/canonical-addresses/");
Cursor curad=getActivity().getContentResolver().query(ad, null,null, null, null);
curad.moveToFirst();
while(!curad.isAfterLast())
{
id_dummy=curad.getString(curad.getColumnIndexOrThrow("_id"));
if(id_dummy.equals(res))
address=curad.getString(curad.getColumnIndexOrThrow("address"));
curad.moveToNext();
}
curad.close();
body = cur.getString(cur.getColumnIndexOrThrow("snippet"));
date = cur.getLong(cur.getColumnIndexOrThrow("date"));
Date datenew=new Date(date);
String formatted_date=new SimpleDateFormat(" "+"dd/MM/yyyy").format(datenew);
thread_id = cur.getString(cur.getColumnIndexOrThrow("_id"));
Long ContactID = fetchContactIdFromPhoneNumber(address);
//uriphotoid = getPhotoUri(ContactID);
dname = getcontactname(address);
ConvItem.setDisplayName(dname);
ConvItem.setThreadId(thread_id);
ConvItem.setAddress(address);
//ConvItem.setPhotoUri(uriphotoid);
ConvItem.setDate(formatted_date);
ConvItem.setBody(body);
ConvItems.add(ConvItem);
cur.moveToNext();
}
cur.close();
return ConvItems;
}
This function is running on my onCreate method in the main activity class.
I used loader.callbacks to correct the delay issue.
public class InboxLoaderFragment extends Fragment implements LoaderCallbacks<Cursor>{
private static final int LOADER_ID = 1;//identify which loader
LoaderManager lm;
SimpleCursorAdapter mAdapter;
ListView lv;
private LoaderManager.LoaderCallbacks<Cursor> mCallbacks;
public InboxLoaderFragment(){}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootview=inflater.inflate(R.layout.inboxloaderfragment,container,false);
lv=(ListView)rootview.findViewById(R.id.list);
// Create an empty adapter we will use to display the loaded data.
String[] uiBindFrom = {"recipient_ids","recipient_ids","snippet"};
int[] uiBindTo = {R.id.iv_photo,R.id.tv_name,R.id.tv_body};
mAdapter = new SimpleCursorAdapter(getActivity(),R.layout.convitem,null,uiBindFrom,uiBindTo, 0);
mAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
@Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
switch(view.getId())
{
case R.id.iv_photo:
String res=null,address=null;
//to obtain address from canonical-addresses
res = cursor.getString(cursor.getColumnIndex("recipient_ids"));
address=getadd(res);
Long ContactID = fetchContactIdFromPhoneNumber(address);
Uri uriphotoid = getPhotoUri(ContactID);
setcontactimage(uriphotoid,view);
//set photo using uri
return true;
case R.id.tv_name:
String res1=null,address1=null;
res1 = cursor.getString(cursor.getColumnIndex("recipient_ids"));
address1=getadd(res1);//to obtain address from canonical-addresses
String dname = getcontactname(address1);
if(dname!=null)//contact exits
((TextView)view).setText(dname);//contact exists
else
((TextView)view).setText(address1);//set display name
return true;
case R.id.tv_body:
String body = cursor.getString(cursor.getColumnIndexOrThrow("snippet"));
((TextView)view).setText(body);//set message body
return true;
default:
return false;
}
}
});
lv.setAdapter(mAdapter);
mCallbacks=this;
lm = getLoaderManager();
//Initiating the loader
lm.initLoader(LOADER_ID, null,mCallbacks);
return rootview;
}
@Override
public android.support.v4.content.Loader<Cursor> onCreateLoader(
int arg0, Bundle arg1) {
Uri baseUri = Uri.parse("content://mms-sms/conversations?simple=true");
return new CursorLoader(getActivity(), baseUri,
null, null, null,"date desc");
}
@Override
public void onLoadFinished(
android.support.v4.content.Loader<Cursor> arg0, Cursor arg1) {
switch (arg0.getId()) {
case LOADER_ID:
mAdapter.swapCursor(arg1);
break;
}
// The listview now displays the queried data
}
@Override
public void onLoaderReset(android.support.v4.content.Loader<Cursor> arg0) {
mAdapter.swapCursor(null);
}
}///main activity
App loads very fast now.But scrolling is laggy.How to implement viewholder design pattern inside a custom simplecursoradapter or a cursoradapter?