Refreshing ListView each time I search a new term - android

I am using google book API to search a book with the title.It shows me the result for the first time. The next I search it clears the ListView but not showing the new search result.
There is a peculiar error. When I search a book it shows me 3 to 4 books whereas I have set MaxResult to 10. When I manually call the API in the browser I get 10 results.
Here is the code I am using.
public class MainActivity extends AppCompatActivity implements LoaderCallbacks<List<Book>>{
private static final int BOOK_ID = 1;
public static final String LOG_TAG = MainActivity.class.getName();
private static String book_url;
private BookAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button bt=(Button)findViewById(R.id.search);
bt.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
EditText searchTerm=(EditText)findViewById(R.id.search_book);
String search=searchTerm.getText().toString();
book_url="https://www.googleapis.com/books/v1/volumes?q="+search+"&maxResults=10";
LoaderManager loaderManager = getLoaderManager();
loaderManager.initLoader(BOOK_ID, null, MainActivity.this);
mAdapter=new BookAdapter(MainActivity.this,new ArrayList<Book>());
ListView bookListView=(ListView)findViewById(R.id.list);
mAdapter.notifyDataSetChanged();
bookListView.setAdapter(mAdapter);
}
});
}
public Loader<List<Book>> onCreateLoader(int i,Bundle bundle){
return new BookLoader(this,book_url);
}
public void onLoadFinished(Loader<List<Book>> loader,List<Book> books){
mAdapter.clear();
if(books!=null&&!books.isEmpty()){
mAdapter.addAll(books);
}
}
public void onLoaderReset(Loader<List<Book>> loader){
mAdapter.clear();
}
}
I tried notifyDataSetChanged(). But it doesn't work.
Here is the code for custom adapter
package com.example.android.booksearch;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import java.util.List;
/**
* Created by AKSPAN12 on 19-07-2017.
*/
public class BookAdapter extends ArrayAdapter<Book> {
public BookAdapter(Context context, List<Book> books) {
super(context, 0,books);
}
public View getView(int position, View convertView, ViewGroup parent){
View listItemView=convertView;
if(listItemView==null){
listItemView= LayoutInflater.from(getContext()).inflate(R.layout.book_list,parent,false);
}
Book currentBook=getItem(position);
String author=currentBook.getAuthor();
TextView authorView=(TextView)listItemView.findViewById(R.id.book_author);
authorView.setText(author);
String bookName=currentBook.getBook();
TextView bookView=(TextView)listItemView.findViewById(R.id.book_name);
bookView.setText(bookName);
return listItemView;
}
}

It shows me the result for the first time. The next I search it clears the ListView but not showing the new search result.
You probably need to restart your loader.

Related

Call another activity in Android using onPostExecute method list item click event in AsyncTask

I am working on a project which populating SQLite database table for list view and using simple array adapter.
I'm using Asyntask for that purpose and I have problem when:
I want to call another activity and
pass some values which I get from the setOnItemClickListener
I need to archive this two things in onPostExecute setOnItemClickListener method. This is my code for that.
package com.me.doctor.doctor_me;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.AsyncTask;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;
public class BackgroundTask extends AsyncTask<String,Doctor,String> {
Context ctx;
DoctorAdapter doctorAdapter;
Activity activity;
ListView listView;
Doctor doctor;
DisplayDoctor displayDoctor;
BackgroundTask(Context ctx){
this.ctx = ctx;
activity = (Activity) ctx;
doctor = new Doctor();
displayDoctor = new DisplayDoctor();
}
#Override
protected String doInBackground(String... strings) {
String method = strings[0];
DatabaseOperation databaseOperation = new DatabaseOperation(ctx);
if(method.equals("get_info")){
listView = activity.findViewById(R.id.display_list_view);
SQLiteDatabase db = databaseOperation.getReadableDatabase();
Cursor cursor = databaseOperation.getInformation(db);
doctorAdapter = new DoctorAdapter(ctx,R.layout.display_doctor_row);
String name, category, hospital;
int id;
while(cursor.moveToNext()){
id = cursor.getInt(cursor.getColumnIndex("d_id"));
name = cursor.getString(cursor.getColumnIndex("d_name"));
category = cursor.getString(cursor.getColumnIndex("d_category"));
hospital = cursor.getString(cursor.getColumnIndex("d_hospital"));
Doctor doctor = new Doctor(id,name,category,hospital);
publishProgress(doctor);
}
return "get_info";
}
return null;
}
#Override
protected void onProgressUpdate(Doctor... values) {
// add each of doctor class object add method inside the adapter class
doctorAdapter.add(values[0]);
}
#Override
protected void onPostExecute(String s) {
if(s.equals("get_info")){
listView.setAdapter(doctorAdapter);
listView.setOnItemClickListener(new android.widget.AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
final int position, long id) {
Doctor doctor = (Doctor)parent.getItemAtPosition(position);
String ID = Integer.toString(doctor.getId());
Toast.makeText(ctx,ID,Toast.LENGTH_LONG).show();
// I need fire another activity and pass some values which i getting here
}
});
}else{
Toast.makeText(ctx,s,Toast.LENGTH_LONG).show();
}
}
}
And this is the class which call to the AsyncTask Class
package com.me.doctor.doctor_me;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class DisplayDoctor extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.display_doctor_layout);
BackgroundTask backgroundTask = new BackgroundTask(this);
backgroundTask.execute("get_info");
}
}
I had investigate closed question on Stack Overflow, but I did not found a solution.
Short answer:
you already have context in Background task
Context ctx;
use this this to call next activiry
ctx.startActivity(nextActivityIntent)
u can add values to the intent like this
Intent nextActivityIntent = new Intent(ctx,NextActivity.class);
nextActivityIntent.putExtra("data", "some data");
with async task i guess you are trying to query data base on another thread
You can use loaders for the same
Loaders run on separate thread
here is a simple example of cursor loader
example taken from github
public class ForecastFragment extends Fragment implements LoaderManager.LoaderCallbacks {
public static final int LOADER_ID = 0;
private ArrayAdapter<String> forecastAdapter;
private ForecastAdapter mForecastAdapter;
public ForecastFragment() { }
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
getLoaderManager().initLoader(LOADER_ID, null, this);
super.onActivityCreated(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
return rootView;
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
//some database query
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
//some action
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
}
}

Android - pass data between from an Activity class to an Adapter class

I have 3 classes: LoginActivity,MapsActivity and MatchAdapter
The first 2 extends AppCompactActivity, the last one ArrayAdapter.
When i make login (if correct, matching on mySQLiteDB) i used to get ID_contact of current user and pass it to MapsActivity with intent in such way:
On my LoginActivity:
String contact=databaseHelper.searchID_Contact(username,password);
Intent intent=new Intent(LoginActivity.this,MapsActivity.class);
intent.putExtra("ID_CONTACT",contact);
startActivity(intent);
On MapsActivity i can easily retrieve this data in such way:
public String getId_contact(String conct){
return conct;
}
#Override
public void onMapReady(GoogleMap googleMap) {
String id_contact1=getIntent().getStringExtra("ID_CONTACT");
String contact=getId_contact(id_contact1);
Toast.makeText(MapsActivity.this, contact, Toast.LENGTH_LONG).show();
}
Till now everything works fine, it appears the id of the current user.
My problem is to pass this data (with intent i don't know how) even to another class named MatchAdapter that extends ArrayAdapter.
I tried this way on MapsActivity:
public class MapsActivity extends AppCompatActivity implements ...{
public String getId_contact(){
String contact=getIntent().getStringExtra("ID_CONTACT");
return contact;
}
So on MatchAdaper trying to retrieve such way:
MapsActivity mapsActivity=new MapsActivity();
String text=mapsActivity.getId_contact().toString();
But nothing..i get NULLPOINTEREXCEPTION...Can someone help me?
Ok...found the solution...On MatchAdapter extends ArrayAdapter
DatabaseHelper databaseHelper=new DatabaseHelper(getContext());
...than OnClick function....
databaseHelper.myfunction();
Well you can access method of activity from an adapter by following way, Call this method from constructor of adapter or anywhere you want.
((ActivityName)context).methodName();
When you create a new instance of MapsActivity, that isn't the same Activity instance you got when you called startActivity(). This is basically why you have a null pointer exception.
More importantly, you should never be manually creating Activity instances using "new". Generally the system creates Activity objects for you via mechanisms like startActivity(), and that is how you should obtain them.
Himanshu's suggestion can work, if your activity does happen to be "hosting" your adapter, but this isn't always guaranteed. A better approach is to pass the ID to your MatchAdapter directly, either in the constructor or as a direct setter function. At the least, you should perform a "instanceof" check to make sure your adapter context is really of type MapsActivity.
That's my MatchAdapter `package vincenzo.futsal4you;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.MatchResult;
public class MatchAdapter extends ArrayAdapter{
List list=new ArrayList();
String text1=null;
MatchAdapter matchAdapter;
static String id_contatto3="";
String fatto3="";
Player player=new Player();
public MatchAdapter(Context context, int resource) {
super(context, resource);
}
public void add(Match object) {
list.add(object);
super.add(object);
}
#Override
public int getCount() {
return super.getCount();
}
#Override
public Object getItem(int position) {
return super.getItem(position);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row=convertView;
final Context context = null;
final MatchHolder matchHolder;
final String cc=null;
if (row==null){
LayoutInflater layoutInflater=(LayoutInflater)this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row=layoutInflater.inflate(R.layout.display_match_row,parent,false);
matchHolder=new MatchHolder();
matchHolder.id_match=(TextView)row.findViewById(R.id.id_partita);
matchHolder.id_contact=(TextView)row.findViewById(R.id.id_contatto);
matchHolder.nome_partita=(TextView)row.findViewById(R.id.nome_partita);
matchHolder.citta=(TextView)row.findViewById(R.id.citta);
matchHolder.indirizzo=(TextView)row.findViewById(R.id.indirizzo);
matchHolder.data=(TextView)row.findViewById(R.id.data);
matchHolder.ora=(TextView)row.findViewById(R.id.ora);
// matchHolder.id_contact=row.findViewById()
matchHolder.join_us = (Button) row.findViewById(R.id.join_us);
row.setTag(matchHolder);
}
else {
matchHolder=(MatchHolder)row.getTag();
}
final Match match=(Match)getItem(position);
// matchHolder.id_contact.setText(mapsActivity.getId_partita().toString());
matchHolder.id_match.setText(match.getId().toString());
matchHolder.nome_partita.setText(match.getName().toString());
matchHolder.citta.setText(match.getCitta().toString());
matchHolder.indirizzo.setText(match.getIndirizzo().toString());
matchHolder.data.setText(match.getData().toString());
matchHolder.ora.setText(match.getOra().toString());
// assert ((MapsActivity) context) != null;
// ((MapsActivity) context).getId_partita();
// final String contact=matchHolder.getId_contatto();
Log.e("BOOOOOO", matchHolder.getId_contatto2());
final String fatto=matchHolder.getId_contatto2();
fatto3=matchHolder.getId_contatto2();
matchHolder.join_us.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//
String fatto2=matchHolder.getId_contatto2();
// final String text3=mapsActivity.getId_partita(cc).toString();
RelativeLayout rl = (RelativeLayout) v.getParent();
RelativeLayout r2 = (RelativeLayout) v.getParent();
// TextView tv = (TextView)rl.findViewById(R.id.nome_partita);
TextView tv = (TextView) rl.findViewById(R.id.id_partita);
TextView tv2 = (TextView) r2.findViewById(R.id.id_contatto);
String id_partita = tv.getText().toString();
String text2 = tv2.getText().toString();
Toast.makeText(getContext(), id_partita, Toast.LENGTH_SHORT).show();
// Toast.makeText(getContext(), matchHolder.setId_contatto(contact), Toast.LENGTH_SHORT).show();
Toast.makeText(getContext(),matchHolder.getId_contatto2(),Toast.LENGTH_SHORT).show();
player.setId_contatto(fatto3);
player.setId_partita(id_partita);
// databaseHelper=new DatabaseHelper(context);
// databaseHelper.insertPlayer2(player);
((MapsActivity)context).getJoinPlayer(player);
Toast pass1=Toast.makeText(getContext(), "One Row JOIN US created !", Toast.LENGTH_SHORT);
pass1.show();
}
});
return row;
}
static class MatchHolder{
TextView id_match,nome_partita,citta,indirizzo,data,ora,id_contact;
Button join_us;
public MatchHolder(){}
public String getId_contatto(String id_contatto) {
return id_contatto;
}
public String getId_contatto2() {
return id_contatto3;
}
public void setId_contatto(String id_contatto) {
id_contatto3 = id_contatto;
}
public MatchHolder(String id_contatto){
id_contatto3=id_contatto;
}
}
}
` So the problem was that i declared a String (id_contatto3) inside a static (inner) class (MatchHolder).Declaring it up to MatchAdapter i solve it somehow, but now i wanna call inside onClick a method that is inside another class (DatabaseHelper that extends SQLiteOpenhelper)..
I can't do ((DatabaseHelper)context).mymethod() So i've done the following "trick"...inside class MapsActivity where i've created a method (JoinPlayer) in such way:
public void JoinPlayer(Player player){
databaseHelper.insertPlayer(player);
}
where insertPlayer(Player) in DatabaseHelper is:
public void insertPlayer(Player player){
try{
db=this.getWritableDatabase();
}
catch(Exception e){
Log.e("ERROR","ERROR");
}
ContentValues contentValues=new ContentValues();
String query="select * from player";
Cursor cursor=db.rawQuery(query,null);
int count=cursor.getCount();
contentValues.put(COLUMN_ID_PLAYER,count);
contentValues.put(COLUMN_ID_MATCH_PLAYER,player.getId_partita());
contentValues.put(COLUMN_ID_CONTACT_PLAYER,player.getId_contatto());
db.insert(TABLE_PLAYER, null, contentValues);
db.close();
}
But Android suggest me to add a null condition(if ((MapsActivity)context)!=null) than ((MapsActivity)context).JoinPlayer(player) but it advise me it will be Always null and that's exactly what I get... I think is the context the main problem but have no clue right now how to solve it. Any Idea?

cannot add column with 'put' method in Parse database

I'm developing real-time messenger application with Parse and want to display all of users in ListView. So I also want to reference if user is online or offline, I tried 'put' method to add column named "online" and put information about it. When it went wrong, I added that column myself, but it still did not work. Here it's what I tried in UserList class at all:
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import com.parse.FindCallback;
import com.parse.ParseException;
import com.parse.ParseUser;
import java.util.ArrayList;
import java.util.List;
public class UserList extends AppCompatActivity {
public static ArrayList<ParseUser> userList;
public static String TAG = "UserList";
public static ParseUser user = new ParseUser();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user_list);
updateUserStatus(true);
}
#Override
protected void onDestroy() {
super.onDestroy();
updateUserStatus(false);
}
#Override
protected void onResume() {
super.onResume();
loadUserList();
}
private void updateUserStatus(boolean isOnline) {
user.put("online", isOnline);
user.saveEventually();
//System.out.println("getBoolean's result : " + user.getBoolean("online"));
}
private void loadUserList() {
ParseUser.getQuery().whereNotEqualTo("username", user.getUsername()).findInBackground(new FindCallback<ParseUser>() {
#Override
public void done(List<ParseUser> objects, ParseException e) {
if (objects != null) {
if (objects.size() == 0) System.out.println("No user found");
userList = new ArrayList<>(objects);
ListView list = (ListView) findViewById(R.id.userList);
list.setAdapter(new UserAdapter(UserList.this));
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
startActivity(new Intent(UserList.this, MainActivity.class));
finish();
}
});
}
else {
System.out.println("exception detected while loading user list");
e.printStackTrace();
}
}
});
}
}
UserAdapter class:
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import com.parse.ParseUser;
public class UserAdapter extends BaseAdapter {
public static String TAG = "UserAdapter";
public TextView labelname;
LayoutInflater layoutInflater;
public UserAdapter(Context context){
layoutInflater = LayoutInflater.from(context);
}
#Override
public int getCount() {
return UserList.userList.size();
}
#Override
public ParseUser getItem(int index) {
return UserList.userList.get(index);
}
#Override
public long getItemId(int index) {
return index;
}
#Override
public View getView(int pos, View v, ViewGroup group) {
ParseUser c = getItem(pos);
if (v == null) {
v = layoutInflater.inflate(R.layout.chat_item, null);
}
labelname = (TextView) v;
labelname.setText(c.getUsername());
labelname.setCompoundDrawablesWithIntrinsicBounds(c.getBoolean("online") ? R.drawable.ic_online
: R.drawable.ic_offline, 0, R.drawable.arrow, 0);
return v;
}
}
I tried saveInBackground with SaveCallback, but it throws exception named:
Caused by: java.lang.IllegalArgumentException: Cannot save a ParseUser until it has been signed up. Call signUp first
But I'm puzzled more than I was 1 hour ago. I authorize my user in another class and it seems not to be problem.
This was open bug of Parse. might be possible that not solved yet. look at this and this too.
instead of saveEventually() use saveInBackground()
and
you can't do like that
ParseUser user = new ParseUser();
if you want logged user than get like this
ParseUser user = ParseUser.getCurrentUser();
and before getting logged user you must do sign up

My Android-App doesn't Display ListView with custom Adapter

i'm writing an android app that should fetch song data from a parse.com database and display it with a custom Listview.
I tried it the way Nitin suggested but still dont get any listview displayed.
Here is the new code:
package de.android;
import java.util.ArrayList;
import java.util.List;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import com.parse.FindCallback;
import com.parse.Parse;
import com.parse.ParseObject;
import com.parse.ParseQuery;
public class Lyrics extends ListActivity {
ImageButton back;
ListView songList;
TextView textAnzeige;
private ArrayList<String> Titelliste = new ArrayList<String>();
private ArrayList<String> Interpretliste = new ArrayList<String>();
private ArrayList<String> Dauerliste = new ArrayList<String>();
ProgressDialog dialog;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lyrics);
Parse.initialize(this, "hGxasGU6e0WQAOh5JIOGDfvFBKrYyBJKXIzxBfAG", "WsOPsXerpsFjsjekKKbZnnjAHvXy5PQHVQEB8Cqu");
initDataToView();
}
private void initDataToView() {
new getData().execute();
songList = (ListView)findViewById(android.R.id.list);
}
private class getData extends AsyncTask<Void, Void, SongAdapter>{
ProgressDialog dialog;
#Override
protected void onPreExecute() {
dialog = new ProgressDialog(Lyrics.this);
dialog.setMessage("Please wait, while loading!");
dialog.setIndeterminate(true);
dialog.setCancelable(false);
dialog.show();
Log.d("Parse", "PreExecute");
}
#Override
protected SongAdapter doInBackground(Void... params) {
ParseQuery pq = new ParseQuery("SongDatenbank");
pq.whereExists("Titel");
pq.findInBackground(new FindCallback() {
#Override
public void done(List<ParseObject> liederListe,
com.parse.ParseException e) {
if(e==null){
Log.d("Parse", "Objektliste empfangen");
ParseObject x;
for(int i=0;i<liederListe.size();i++){
x = liederListe.get(i);
Titelliste.add(x.getString("Titel"));
Dauerliste.add(x.getString("Dauer"));
Interpretliste.add(x.getString("Interpret"));
}
initDataToView();
x = liederListe.get(0);
Log.d("Parse", x.getString("Titel"));
Log.d("Parse", x.getString("Dauer"));
Log.d("Parse", x.getString("Interpret"));
}else{
Log.d("Parse", "Objektliste nicht empfangen");
}
}
});
return null;
}
protected void onPostExecute(SongAdapter result) {
songList.setAdapter(result);
dialog.dismiss();
Log.d("Parse", "Postexecute");
}
}
public class SongAdapter extends ArrayAdapter<String> {
private final Context context;
private final ArrayList<String> valuesTitel;
private final ArrayList<String> valuesInterpret;
private final ArrayList<String> valuesDauer;
public SongAdapter(Context context, ArrayList<String> valuesTitel, ArrayList<String> valuesInterpret, ArrayList<String> valuesDauer) {
super(context, R.layout.list_row);
this.context = context;
this.valuesTitel = valuesTitel;
this.valuesInterpret = valuesInterpret;
this.valuesDauer = valuesDauer;
}
public View getView(int position, View convertView, ViewGroup parent){
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.list_row, parent, false);
ImageView imageView = (ImageView)rowView.findViewById(R.id.list_image);
TextView titelText = (TextView)rowView.findViewById(R.id.title);
TextView artistText = (TextView)rowView.findViewById(R.id.artist);
TextView duration = (TextView)rowView.findViewById(R.id.duration);
titelText.setText(valuesTitel.get(position));
artistText.setText(valuesInterpret.get(position));
duration.setText(valuesDauer.get(position));
Log.d("Parse", valuesTitel.get(position));
return rowView;
}
}}
I think the problem is in parsing the data and give them to the adapter as parameter.
Any idea how to that?
I would really appreciate it if someone could look through my code and help me with solving the problem.
thanks, Paul
I suggest you to make a Handler to update the user interface or use Asynctask
Update UI from Thread
check the link in the first answer it tells about long operation in doInBackground method you should fetch the data and in onPostExecute set the adapter
read the comments in the following code.
private class PrepareAdapter1 extends AsyncTask<Void,Void,ContactsListCursorAdapter > {
ProgressDialog dialog;
#Override
protected void onPreExecute() {
dialog = new ProgressDialog(viewContacts.this);
dialog.setMessage(getString(R.string.please_wait_while_loading));
dialog.setIndeterminate(true);
dialog.setCancelable(false);
dialog.show();
}
/* (non-Javadoc)
* #see android.os.AsyncTask#doInBackground(Params[])
*/
#Override
protected ContactsListCursorAdapter doInBackground(Void... params) {
//do here parsing
return adapter1;
}
protected void onPostExecute(ContactsListCursorAdapter result) {
dialog.dismiss();
//Set Adapter
list.setAdapter(result);
}
}
Check R.layout.list_row. that is properly designed or not. i.e the layout is having the height and width as fill_parent. because from your code everything looks fine.
there is no getCount method in the Adapter class , try the following code
#Override
public int getCount()
{
return x.size();// number of datas in the list .
}
this method specifies the number of times the getView to be populated automatically .
............ One more suggession is that use extend BaseAdapter instead of ArrayAdapter<String>

Android loaders, the way to go?

I am used to building lists in android using adapters. If I need some long-to-get data, I use an asynctask, or a simple runnable, to update the data structure on which the adapter rely, and call notifyDataChanged on the adapter.
Although it is not straightforward, I finally find this is a simple model and it allows a good separation of logic presentation (in the asynctask, update a data structure) and the view (an adapter acting as a view factory, mostly).
Nevertheless, I read recently about loaders introduced in HoneyComb and included in the backward compatibility support-library, I tried them and find the introduce a lot of complexity. They are difficult to handle and add some kind of magic to this whole process through loader managers, add a lot of code and don't decrease the number of classes or collaborating items but I may be wrong and would like to hear some good points on loaders.
What are they advantages of loaders in terms of lines of code, clarity and effort ?
What are they advantages of loaders in terms of role separation during data loading, or more broadly, in terms of design ?
Are they the way to go, should I replace all my list data loading to implement them through loaders ?
Ok, this is a developers' forum, so here is an example. Please, make it better with loaders :
package com.sof.test.loader;
import java.util.ArrayList;
import java.util.List;
import android.app.ListActivity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.ArrayAdapter;
import android.widget.TextView;
/** The activity. */
public class LoaderTestActivity extends ListActivity {
private DataSourceOrDomainModel dataSourceOrDomainModel = new DataSourceOrDomainModel();
private List<Person> listPerson;
private PersonListAdapter personListAdapter;
private TextView emptyView;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
listPerson = new ArrayList<Person>();
personListAdapter = new PersonListAdapter( listPerson );
setListAdapter( personListAdapter );
setUpEmptyView();
new PersonLoaderThread().execute();
}
public void setUpEmptyView() {
emptyView = new TextView( this );
emptyView.setLayoutParams( new LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT ) );
emptyView.setVisibility(View.GONE);
((ViewGroup)getListView().getParent()).addView(emptyView);
getListView().setEmptyView(emptyView);
}
/** Simulate a long task to get data. */
private class PersonLoaderThread extends AsyncTask<Void, Integer, List<Person>> {
#Override
protected List<Person> doInBackground(Void... params) {
return dataSourceOrDomainModel.getListPerson( new ProgressHandler());
}
#Override
protected void onProgressUpdate(Integer... values) {
emptyView.setText( "Loading data :" + String.valueOf( values[ 0 ] ) +" %" );
}
#Override
protected void onPostExecute(List<Person> result) {
listPerson.clear();
listPerson.addAll( result );
personListAdapter.notifyDataSetChanged();
}
private class ProgressHandler implements ProgressListener {
#Override
public void personLoaded(int count, int total) {
publishProgress( 100*count / total );
}
}
}
/** List item view factory : the adapter. */
private class PersonListAdapter extends ArrayAdapter<Person> {
public PersonListAdapter( List<Person> listPerson ) {
super(LoaderTestActivity.this, 0, listPerson );
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if( convertView == null ) {
convertView = new PersonView( getContext() );
}
PersonView personView = (PersonView) convertView;
personView.setPerson( (Person) getItem(position) );
return personView;
}
}
}
A small callback interface for progress
package com.sof.test.loader;
/** Callback handler during data load progress. */
public interface ProgressListener {
public void personLoaded(int count, int total );
}
A list item widget
package com.sof.test.loader;
import com.sof.test.loader.R;
import android.content.Context;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
import android.widget.TextView;
/** List Item View, display a person */
public class PersonView extends LinearLayout {
private TextView personNameView;
private TextView personFirstNameView;
public PersonView(Context context) {
super(context);
LayoutInflater inflater= (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate( R.layout.person_view,this );
personNameView = (TextView) findViewById( R.id.person_name );
personFirstNameView = (TextView) findViewById( R.id.person_firstname );
}
public void setPerson( Person person ) {
personNameView.setText( person.getName() );
personFirstNameView.setText( person.getFirstName() );
}
}
It's xml : res/person_view.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/person_view"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<TextView
android:id="#+id/person_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true" />
<TextView
android:id="#+id/person_firstname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#id/person_name" />
</RelativeLayout>
The data source or model, providing data (slowly)
package com.sof.test.loader;
import java.util.ArrayList;
import java.util.List;
/** A source of data, can be a database, a WEB service or a model. */
public class DataSourceOrDomainModel {
private static final int PERSON_COUNT = 100;
public List<Person> getListPerson( ProgressListener listener ) {
List<Person> listPerson = new ArrayList<Person>();
for( int i=0; i < PERSON_COUNT ; i ++ ) {
listPerson.add( new Person( "person", "" + i ) );
//kids, never do that at home !
pause();
if( listener != null ) {
listener.personLoaded(i,PERSON_COUNT);
}//if
}
return listPerson;
}//met
private void pause() {
try {
Thread.sleep( 100 );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
The POJO representing a person :
package com.sof.test.loader;
/** A simple POJO to be displayed in a list, can be manipualted as a domain object. */
public class Person {
private String name;
private String firstName;
public Person(String name, String firstName) {
this.name = name;
this.firstName = firstName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}//class
In case someone is looking for the loader version of my previous example : here it is :
package com.sof.test.loader;
import java.util.ArrayList;
import android.app.LoaderManager;
import java.util.List;
import android.app.ListActivity;
import android.content.AsyncTaskLoader;
import android.content.Context;
import android.content.Loader;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.ArrayAdapter;
import android.widget.TextView;
/** The activity. */
public class LoaderTestActivity2 extends ListActivity implements
LoaderManager.LoaderCallbacks<List<Person>> {
private DataSourceOrDomainModel dataSourceOrDomainModel = new DataSourceOrDomainModel();
private List<Person> listPerson;
private PersonListAdapter personListAdapter;
private TextView emptyView;
private Loader<List<Person>> personLoader;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
listPerson = new ArrayList<Person>();
personListAdapter = new PersonListAdapter(listPerson);
setListAdapter(personListAdapter);
personLoader = new PersonLoader(this, dataSourceOrDomainModel, new ProgressHandler() );
setUpEmptyView();
getLoaderManager().initLoader(0, null, this);
personLoader.forceLoad();
}
public void setUpEmptyView() {
emptyView = new TextView(this);
emptyView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
emptyView.setVisibility(View.GONE);
((ViewGroup) getListView().getParent()).addView(emptyView);
getListView().setEmptyView(emptyView);
}
public void publishProgress(int progress) {
emptyView.setText("Loading data :" + String.valueOf(progress) + " %");
}
#Override
public Loader<List<Person>> onCreateLoader(int arg0, Bundle arg1) {
return personLoader;
}
#Override
public void onLoadFinished(Loader<List<Person>> personLoader, List<Person> result) {
listPerson.clear();
listPerson.addAll(result);
personListAdapter.notifyDataSetChanged();
}
#Override
public void onLoaderReset(Loader<List<Person>> arg0) {
listPerson.clear();
personListAdapter.notifyDataSetChanged();
}
/** List item view factory : the adapter. */
private class PersonListAdapter extends ArrayAdapter<Person> {
public PersonListAdapter(List<Person> listPerson) {
super(LoaderTestActivity2.this, 0, listPerson);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = new PersonView(getContext());
}
PersonView personView = (PersonView) convertView;
personView.setPerson((Person) getItem(position));
return personView;
}
}
private class ProgressHandler implements ProgressListener {
#Override
public void personLoaded(final int count, final int total) {
runOnUiThread( new Runnable() {
#Override
public void run() {
publishProgress(100 * count / total);
}
});
}
}
}
class PersonLoader extends AsyncTaskLoader<List<Person>> {
private DataSourceOrDomainModel dataSourceOrDomainModel;
private ProgressListener progressHandler;
public PersonLoader(Context context, DataSourceOrDomainModel dataSourceOrDomainModel, ProgressListener progressHandler ) {
super(context);
this.dataSourceOrDomainModel = dataSourceOrDomainModel;
this.progressHandler = progressHandler;
}
#Override
public List<Person> loadInBackground() {
return dataSourceOrDomainModel.getListPerson( progressHandler );
}
}
It would be more difficult to add support (support librairy) to this example as there is no equivalent of ListAcitivity in the support librairy. I would have either to create a ListFragment or create an FragmentActivity and give it a layout including a list.
One problem your code has which loaders aim to fix is what happens if your activity is restarted (say due to device rotation or config change) while your async task is still in progress? in your case your restarted activity will start a 2nd instance of the task and throw away the results from the first one. When the first one completes you can end up with crashes due to the fact your async task has a reference is what is now a finished activity.
And yes using loaders often makes for more/more complex code, particularly if you can't use one of the provided loaders.

Categories

Resources