So I have a Sections Pager Application in Android. On my fourth fragment, I run an asynctask that connects to a device via bluetooth and updates a custom list that I created (supposedly) However, the list either updates late or doesn't update at all. I'm not exactly sure what to do on the postexecute to allow update so I updated it outside of the asynctask.
Code is below:
public class FourthFragment extends Fragment {
private WeakReference<getBeacons> getBeaconTaskWeakRef;
ArrayList<ArtInfo> ArtList = new ArrayList<>();
;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
startNewBeaconsAsyncTask();
}
ArrayList<String> titles = new ArrayList<>();
ArrayList<String> artists = new ArrayList<>();
ArrayList<String> years = new ArrayList<>();
ArrayList<Integer> images = new ArrayList<>();
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
for (int i = 0; i < ArtList.size(); i++) {
titles.add(ArtList.get(i).getArtTitle());
artists.add(ArtList.get(i).getArtistName());
years.add(ArtList.get(i).getYear());
int resID = getResources().getIdentifier(ArtList.get(i).getImageFilename(), "drawable", "com.acuart.acumen.acuart");
images.add(resID);
}
View v = inflater.inflate(R.layout.frag_list, container, false);
ListView byTitleList = (ListView) v.findViewById(R.id.byTitleList);
byTitleList.setAdapter(new titleList(getActivity(), R.layout.custom_list, titles));
return v;
}
private void startNewBeaconsAsyncTask() {
getBeacons newbeacons = new getBeacons(this);
this.getBeaconTaskWeakRef = new WeakReference<getBeacons>(newbeacons);
newbeacons.execute();
}
class titleList extends ArrayAdapter<String> {
public titleList(Context context, int resource, ArrayList<String> objects) {
super(context, resource, objects);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = ((Activity) getContext()).getLayoutInflater().inflate(R.layout.custom_list, null);
TextView title = (TextView) v.findViewById(R.id.row_title);
TextView artist = (TextView) v.findViewById(R.id.row_artist);
TextView year = (TextView) v.findViewById(R.id.row_year);
ImageView image = (ImageView) v.findViewById(R.id.row_image);
title.setText(titles.get(position));
artist.setText(artists.get(position));
year.setText(years.get(position));
image.setBackgroundResource(images.get(position));
return v;
}
}
private class getBeacons extends AsyncTask<Void, Void, Void> {
private WeakReference<FourthFragment> fragmentWeakReference;
private getBeacons(FourthFragment fragment) {
this.fragmentWeakReference = new WeakReference<FourthFragment>(fragment);
}
ProgressDialog dialog = new ProgressDialog(getActivity());
Context context = getApplicationContext();
int artCount = 0;
SQLHelper markerDBHelper = new SQLHelper(context);
#Override
protected void onPreExecute() {
dialog.setMessage("Loading, please wait...");
dialog.show();
}
#Override
protected Void doInBackground(Void... params) {
checkBluetooth();
}
#Override
protected void onPostExecute(Void v) {
dialog.dismiss();
}
} //processing bluetooth data and creating a query for database return.
}
Any help/comments/ideas are appreciated.
Code in onPostExecute() runs on the UI thread, so you should be able to update your list adapter there.
I'm a bit confused by your question, are you saying that it takes a long time for onPostExecute() to run? Did you have your code in there to update the list, and then moved it out because onPostExecute() took too long to be called?
Do you have a bunch of other async tasks running?
I didn't have time to test compile/test this, so there could very well be some syntax mistakes, but this is just to give you an idea
In titleList add a method to update the data backing the adapter list so:
public void updateAdapterData(ArrayList<String> newData) {
clear();
addAll(newData);
notifyDataSetChanged();
}
And the async task could do something like this
private titleList mTitleList; //Set this in your onCreateView
private class getBeacons extends AsyncTask<Void, Void, ArrayList<String>> {
#Override
protected void onPreExecute() {
dialog.setMessage("Loading, please wait...");
dialog.show();
}
#Override
protected ArrayList<Object> doInBackground(Void... params) {
//If checkbluetooth returns a list..
return checkBluetooth();
}
#Override
protected void onPostExecute(ArrayList<String> newList) {
mTitleList.updateAdapterData(newList)
dialog.dismiss();
}
}
At First set the ListView adapter as follows:
titleList adapter=new titleList(getActivity(), R.layout.custom_list, titles));
byTitleList.setAdapter(adapter);
After doing the background task if you get an List of "titles", then in "onPostExecute" method you can do the following:-
private class getBeacons extends AsyncTask<Void, Void, ArrayList<String> > {
ArrayList<String> titles = new ArrayList<String>();
private getBeacons() {
}
#Override
protected void onPreExecute() {
}
#Override
protected ArrayList<String> doInBackground(Void... params) {
//call a method for assigning values to titles
return titles;
}
#Override
protected void onPostExecute(ArrayList<String> titles) {
//Now assign this arraylist referrence to your actual titles arraylist
adapter.notifyDataSetChanged();
}
}
You just need to update the ArrayList in your titleList adapter and call notifyDataSetChanged() on the adapter. I suggest doing this with a setList() method in the titleList class. You also need to keep a reference of the adapter where it is accessible by your AsyncTask.
Related
hope you fine and well,
i have the following main class :
public class MainActivity extends AppCompatActivity {
Activity activity;
ViewPager viewPager;
CustomAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager=(ViewPager)findViewById(R.id.view_pager);
adapter=new CustomAdapter(this);
viewPager.setAdapter(adapter);
ConnectionAsyncTask connectionAsyncTask = new
ConnectionAsyncTask(MainActivity.this);
connectionAsyncTask.execute("http://www.mocky.io/v2/570d3677270000f600dc29b6");
}
public void showUploader()
{
// findViewById(R.id.progressBar1).setVisibility(View.VISIBLE);
}
public void hideUploader()
{
//findViewById(R.id.progressBar1).setVisibility(View.GONE);
}
public void DisplyOnTextView(List< Student > students) {
List <Student> my = students ;
}
}
when i run the app, this main activity will use another class to read json data from link as follows:
public class StudentJasonParser {
public static List<Student> getObjectFromJason(String jason)
{
List<Student> students;
try {
JSONArray jsonArray = new JSONArray(jason);
students = new ArrayList<>();
for(int i=0;i<jsonArray.length();i++)
{
JSONObject jsonObject = new JSONObject();
jsonObject= (JSONObject) jsonArray.get(i);
Student student = new Student();
student.setID(jsonObject.getInt("id"));
student.setName(jsonObject.getString("name"));
student.setUrl(jsonObject.getString("url"));
student.setDes(jsonObject.getString("des\n"));
student.setRate(jsonObject.getDouble("rate"));
student.setLon(jsonObject.getDouble("lon"));
student.setLat(jsonObject.getDouble("lat"));
students.add(student);
}
} catch (JSONException e) {
e.printStackTrace();
return null;
}
return students;
}
}
now this class will return the data to the following class :
public class ConnectionAsyncTask extends AsyncTask<String,String,String> {
Activity activity;
public ConnectionAsyncTask(Activity activity) {
this.activity=activity;
}
#Override
protected void onPreExecute() {
//((MainActivity)activity).DisplyOnTextView();
((MainActivity)activity).showUploader();
}
#Override
protected String doInBackground(String... params) {
String content =HttpManager.getData(params[0]);
return content;
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
}
#Override
protected void onPostExecute(String s) {
((MainActivity)activity).hideUploader();
List<Student> students= StudentJasonParser.getObjectFromJason(s);
if (students != null) {
((MainActivity) activity).DisplyOnTextView(students);
}
}
}
this line : ((MainActivity)activity).DisplyOnTextView(students);
will return the fetched data to the main class in the following function (mentioned in the main class ! )
public void DisplyOnTextView(List< Student > students) {
List <Student> my = students ;
}
now what i want is to pass this list to the following class in order to use it in the imageView and textView in the viewPager instead of the pre-defined data in the class :
public class CustomAdapter extends PagerAdapter {
private int[] images = {R.drawable.sample_0,R.drawable.sample_1};
private Context ctx;
private LayoutInflater LayoutInflater;
public CustomAdapter(Context ctx)
{
this.ctx=ctx;
}
#Override
public int getCount() {
return images.length;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return (view==(LinearLayout)object);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
LayoutInflater = (LayoutInflater)ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = LayoutInflater.inflate(R.layout.slide_layout,container,false);
ImageView imageView =(ImageView) view.findViewById(R.id.image_view);
TextView textView = (TextView)view.findViewById(R.id.image_count);
imageView.setImageResource(images[position]);
textView.setText(""+position);
container.addView(view);
return view;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((LinearLayout) object);
}
}
any idea ?!
thanks, regards.
what i want is to pass this list to the following class in order to use it in the imageView and textView in the viewPager
Then simply pass in the list as a parameter to the adapter and add a member variable for it. The usage of this adapter is at the bottom of this post, because I want to mention some other stuff.
class CustomAdapter extends PagerAdapter {
private Context ctx;
private List<Student> data;
public CustomAdapter(Context ctx, List<Student> students) {
this.ctx = ctx;
this.data = students;
}
If you want to use that data variable in the instantiateItem method, then you can do Student s = this.data.get(position); and use the various methods on the Student object to load the TextView or ImageView.
Please note that you will need an image loading library (Picasso, Glide, Fresco, etc.) to load a URL into an ImageView. While on the topic of libraries, though, you will save yourself much development time by looking into Gson for JSON parsing and Retrofit or Volley for HTTP network calls with JSON data.
As for your usage of the AsyncTask, passing around the Activity variable is bad practice. Try to use an asynchronous callback to the Activity instead.
public interface AsyncResponse<T> {
void onResponse(T response);
}
public class ConnectionAsyncTask extends AsyncTask<String, Void, List<Student>> {
private AsyncResponse<List<Student>> callback;
public ConnectionAsyncTask(AsyncResponse<List<Student>> callback) {
this.callback = callback;
}
#Override
protected List<User> doInBackground(String... params) {
String url = params[0];
final List<Student> students = new ArrayList<Student>();
// TODO: JSON stuff
return students;
}
#Override
protected void onPostExecute(List<Student> result) {
if (this.callback != null) {
this.callback.onResponse(result);
} else {
Log.w("ConnectionAsyncTask", "Ignoring result");
}
}
}
public class SampleViewPagerActivity extends Activity {
private ViewPager pager;
private PagerAdapter adapter;
private ArrayList<Student> students;
private ProgressDialog progress;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 1. Inflate a layout
setContentView(R.layout.viewpager_activity);
// 2. Initialize the views
this.pager = (ViewPager) findViewById(R.id.pager);
this.progress = new ProgressDialog(this);
this.progress.setTitle("Loading");
this.progress.setMessage("Please wait");
// 3. Populate the views with data
this.students = new ArrayList<Student>();
this.adapter = new CustomAdapter(this, students);
this.pager.setAdapter(adapter);
// This code runs later, after 'execute' is called and the response is returned
ConnectionAsyncTask task = new ConnectionAsyncTask(new AsyncResponse<List<Student>>() {
#Override
public void onResponse(List<Student> response) {
students.clear();
students.addAll(response);
adapter.notifyDataSetChanged();
progress.hide();
}
});
// Optionally show some progress while waiting
this.progress.show();
// TODO: Use real URL
task.execute("http://www.somesite.com/data");
}
}
I want to send a list of bitmap i retreived from mysql database using asyncTask to the fragment Fragment_ListView.class, in this fragment class i want to set the adapter of the listView with the bitmap token from asyncTask but i don't know how to do that.
Async Task
#Override
protected void onPostExecute(ArrayList<Bitmap> bitmapArrayList) {
super.onPostExecute(bitmapArrayList);
loading.dismiss();
// now after getting images from server , i want to send this bitmapArrayList
// to Fragment_ListView where i set the adapter of the
}
#Override
protected ArrayList<Bitmap> doInBackground(String... params) {
imageList = new ArrayList();
String add1 = "http://192.168.1.11/save/load_image_from_db.php?id=1";
String add2 = "http://192.168.1.11/save/load_image_from_db.php?id=2";
String add3 = "http://192.168.1.11/save/load_image_from_db.php?id=3";
URL url;
Bitmap image = null;
String[] adds = {add1, add2, add3};
for (int i = 0; i < adds.length; i++) {
try {
url = new URL(adds[i]);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
image = BitmapFactory.decodeStream(connection.getInputStream());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
imageList.add(image);
image = null;
}
return imageList;
OnCreate of MainActivity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listfrg = new Fragment_ListView();
getFragmentManager().beginTransaction().add(R.id.frml, listfrg).commit();
}
Fragment_ListView :
public class Fragment_ListView extends Fragment {
ListView mListView;
static ArrayList<Bitmap> bitmaps;
static MySimpleArrayAdapter adapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.frglist, container, false);
mListView = (ListView) view.findViewById(R.id.listView);
bitmaps = new ArrayList<>();
adapter = new MySimpleArrayAdapter(getActivity(), bitmaps);
mListView.setAdapter(adapter);
return view;
}
Something like this,
Create a new interface file
public interface BitMapArrayCallBack {
abstract void bitmapArray(ArrayList<Bitmap> bitmaps);
}
Then in AsyncTask ... i don't know your class name so i will assume the class ServerRequest
public class ServerRequest {
Context context;
public ServerRequest(Context context) {
this.context = context;
}
public void doAsyncTask(BitMapArrayCallBack bitmapCallback) {
new MyAsyncTask(bitmapCallback).execute();
}
public class MyAsyncTask extends AsyncTask<Void, Void, Void> {
private BitMapArrayCallBack bitmapCallback;
public MyAsyncTask(BitMapArrayCallBack bitmapCallback) {
this.bitmapCallback = bitmapCallback;
}
//call do in background etc..
#Override
protected void onPostExecute(ArrayList<Bitmap> bitmapArrayList) {
super.onPostExecute(bitmapArrayList);
loading.dismiss();
bitmapCallback.bitmapArray(bitmapArrayList);
// This will hold and return your arraylist
}
}
}
Now you must call your asynctask in fragment listview
public class Fragment_ListView extends Fragment {
ListView mListView;
static MySimpleArrayAdapter adapter;
private ServerRequest serverRequest;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.frglist, container, false);
mListView = (ListView) view.findViewById(R.id.listView);
serverRequest = new ServerRequest(getActivity);
serverRequest.doAsyncTask(new BitMapArrayCallBack {
#Override
void bitMapArray(ArrayList<Bitmap> bitmaps) {
// in here you have access to the bitmaps array
adapter = new MySimpleArrayAdapter(getActivity(), bitmaps);
mListView.setAdapter(adapter);
}
})
return view;
}
AsyncTask updates the adapter content but does not update the listview. All the code is working fine but when i am updating my adapter, my listview does not reflect any update from adapter. I have also searched over google for this problem but not find any proper solution. it show the exception as below :-
java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification.
Here is my code, please have a look what should i change :-
public class SearchResultScreen extends Fragment {
ListView searchResult;
SearchResultAdapter adapter;
TextView total_jobs,displaying_jobs;
int currentPage=0,threshold=10;
ProgressDialog progress;
JobService jobService;
TextView load_more_jobs;
int flag = 0;
public SearchResultScreen(){}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.search_result_screen, container, false);
searchResult = (ListView) rootView.findViewById(R.id.search_result);
jobService = new JobService();
jobService.setCurrent_page(currentPage);
load_more_jobs = new TextView(getActivity());
load_more_jobs.setText("Load More Jobs");
load_more_jobs.setGravity(Gravity.CENTER_HORIZONTAL);
load_more_jobs.setPadding(5,5,5,5);
load_more_jobs.setBackgroundColor(Color.GRAY);
Map params = new HashMap();
params.put("skills", getArguments().getString("skills"));
params.put("exp_from", getArguments().getString("exp_from"));
params.put("exp_to", getArguments().getString("exp_to"));
params.put("location", getArguments().getString("location"));
params.put("func_area", getArguments().getString("functional_area"));
params.put("start", jobService.getCurrent_page());
total_jobs = (TextView) rootView.findViewById(R.id.total_jobs_label);
displaying_jobs = (TextView) rootView.findViewById(R.id.displaying_jobs);
progress = new ProgressDialog(getActivity());
progress.setTitle("Please Wait!!");
progress.setMessage("Wait!!");
progress.setCancelable(false);
progress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
new MyAsyncTask(params, progress, searchResult, getActivity(), jobService, total_jobs, displaying_jobs, load_more_jobs, flag, adapter).execute("rest/search_jobs.rest");
currentPage++;
load_more_jobs.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(currentPage < jobService.getTotal_page()) {
jobService.setCurrent_page(currentPage);
Map params = new HashMap();
params.put("skills", getArguments().getString("skills"));
params.put("exp_from", getArguments().getString("exp_from"));
params.put("exp_to", getArguments().getString("exp_to"));
params.put("location", getArguments().getString("location"));
params.put("func_area", getArguments().getString("functional_area"));
params.put("start", jobService.getCurrent_page());
flag = 1;
new MyAsyncTask(params, progress, searchResult, getActivity(), jobService, total_jobs, displaying_jobs, load_more_jobs, flag, adapter).execute("rest/search_jobs.rest");
currentPage++;
}
}
});
searchResult.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
JobSearchModel jobs = (JobSearchModel) adapterView.getItemAtPosition(i);
FragmentManager fragmentManager = getFragmentManager();
Fragment search_result_screen = new SearchResultScreen();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
JobDescription jobDescription = new JobDescription();
Bundle args = new Bundle();
args.putSerializable("jobs", jobs);
jobDescription.setArguments(args);
fragmentTransaction.replace(R.id.content_frame, jobDescription);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
//Toast.makeText(getActivity(), "Id is : " + jobs.getJob_id(), Toast.LENGTH_LONG).show();
}
});
return rootView;
}
static class MyAsyncTask extends AsyncTask<String, Integer, String> {
private Map params;
private ProgressDialog progress;
private ListView list;
private Activity context;
private JobService jobService;
private TextView total_jobs;
private TextView displaying_jobs;
private TextView load_more_jobs;
private int flag;
private SearchResultAdapter adapter;
Object[] object_response;
public MyAsyncTask(Map params, ProgressDialog progress, ListView searchResult, Activity context, JobService jobService, TextView total_jobs, TextView displaying_jobs, TextView load_more_jobs, int flag, SearchResultAdapter adapter) {
this.params = params;
this.progress = progress;
this.list = searchResult;
this.context = context;
this.jobService = jobService;
this.total_jobs = total_jobs;
this.displaying_jobs = displaying_jobs;
this.load_more_jobs = load_more_jobs;
this.flag = flag;
this.adapter = adapter;
}
#Override
public void onPreExecute() {
this.progress.show();
}
public void onProgressUpdate(Integer filesDownloaded) {
}
protected String doInBackground(String... parameters) {
postData(parameters[0]);
return "done";
}
protected void onPostExecute(String result){
context.runOnUiThread(new Runnable() {
#Override
public void run() {
if(flag == 0) {
adapter = new SearchResultAdapter(context, jobService.getModel(object_response));
list.addFooterView(load_more_jobs, null, false);
list.setAdapter(adapter);
} else {
adapter = new SearchResultAdapter(context, jobService.getModel(object_response));
}
total_jobs.setText(""+jobService.getTotal_jobs());
displaying_jobs.setText(""+jobService.displayingJobs());
}
});
adapter.notifyDataSetChanged();
this.progress.dismiss();
}
protected void onProgressUpdate(String progress){
}
public void postData(final String request) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
object_response = restTemplate.postForObject(UtilClass.baseURL+request, params, JobSearchModel[].class);
}
}
}
You can fixed your error while adding below line
adpater.notifyDataSetChanged();
on whenever the list is changed you must call the above function.
Note: make sure call notifyDataSetChanged() in the UI thread.
Exception is:
java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification.
So you need to notify listview, that adapter data is changed.
Call
listView.invalidateViews();
just after you call
adapter.notifyDataSetChanged();
Hope it helps.
i am using a view pager, with actionbar.
this action bar contains 3 tabs.
one of them is players, i called it like this:
#Override
public Fragment getItem(int arg0) {
switch (arg0) {
case 0:
return new TeamInformation();
case 1:
return new TeamPlayers();
default:
return new TeamInformation();
}
}
and the TeamPlayers class is :
public class TeamPlayers extends Fragment {
private ListView lv_players;
private TeamPlayersAdapter adapter;
private List<Player> players = new LinkedList<Player>();
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.team_players, container, false);
lv_players = (ListView) v.findViewById(R.id.lv_players);
adapter = new TeamPlayersAdapter(this.getActivity(), players);
lv_players.setAdapter(adapter);
new getTeamPlayers().execute(1);
return v;
}
when i call this screen, i got nothing, i mean not any data show, although, if you notice i call the getTeamPlayers which is:
private class getTeamPlayers extends
AsyncTask<Integer, Integer, List<Player>> {
#Override
protected List<Player> doInBackground(Integer... params) {
List<Player> players = new LinkedList<Player>();
Player player = new Player();
player.setID(1);
player.setEn_name("Totti");
players.add(player);
player = new Player();
player.setID(2);
player.setEn_name("De Rossi");
players.add(player);
return players;
}
#Override
protected void onPostExecute(List<Player> result) {
super.onPostExecute(result);
players = new LinkedList<Player>();
players.addAll(result);
adapter.notifyDataSetChanged();
}
}
see i put data in it. i make a log to check if the application called the onCreateView and i can see the log.
help appreciated
Set adapter when the list is loaded means on postExecute
#Override
protected void onPostExecute(List<Player> result) {
super.onPostExecute(result);
players = new LinkedList<Player>();
players.addAll(result);
adapter = new TeamPlayersAdapter(this.getActivity(), players);
lv_players.setAdapter(adapter);
}
this may help you... no need to set the Adapter to ListView multiple times...
#Override
protected void onPostExecute(List<Player> result) {
if(result != null) {
players.clear();
players.addAll(result);
adapter.notifyDataSetChanged();
}
}
I'm having problems using AsyncTaskLoader. This is my first attempt populating a ListView from a SQLite database using a loader.
Everything seems ok, when I rotate the screen the data is cached and no query is done again. But when I press the home button and launch my app again, the data is loaded again.
Note: Usuario means User, so I'm populating the ListView with a list of users.
public class Main extends SherlockFragmentActivity
implements LoaderManager.LoaderCallbacks<ArrayList<Usuario>> {
UsuarioAdapter adapter;
ListView listView;
Database db;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
listView = (ListView) findViewById(R.id.lista);
db = new Database(this);
adapter = new UsuarioAdapter(this, new ArrayList<Usuario>());
listView.setAdapter(adapter);
getSupportLoaderManager().initLoader(0, null, this);
}
#Override
public Loader<ArrayList<Usuario>> onCreateLoader(int id, Bundle args) {
return new UsuariosLoader(this, db);
}
#Override
public void onLoadFinished(Loader<ArrayList<Usuario>> loader,
ArrayList<Usuario> usuarios) {
//adapter.notifyDataSetChanged();
listView.setAdapter(new UsuarioAdapter(this, usuarios));
// ((BaseAdapter) listView.getAdapter()).notifyDataSetChanged();
}
#Override
public void onLoaderReset(Loader<ArrayList<Usuario>> loader) {
listView.setAdapter(null);
}
}
// THE LOADER
class UsuariosLoader extends AsyncTaskLoader<ArrayList<Usuario>> {
private ArrayList<Usuario> usuarios;
private Database db;
public UsuariosLoader(Context context, Database db) {
super(context);
this.db = db;
}
#Override
protected void onStartLoading() {
if (usuarios != null) {
deliverResult(usuarios); // Use the cache
}
forceLoad();
}
#Override
protected void onStopLoading() {
// The Loader is in a stopped state, so we should attempt to cancel the
// current load (if there is one).
cancelLoad();
}
#Override
public ArrayList<Usuario> loadInBackground() {
db.open(); // Query the database
ArrayList<Usuario> usuarios = db.getUsuarios();
db.close();
return usuarios;
}
#Override
public void deliverResult(ArrayList<Usuario> data) {
usuarios = data; // Caching
super.deliverResult(data);
}
#Override
protected void onReset() {
super.onReset();
// Stop the loader if it is currently running
onStopLoading();
// Get rid of our cache if it exists
usuarios = null;
}
#Override
public void onCanceled(ArrayList<Usuario> data) {
// Attempt to cancel the current async load
super.onCanceled(data);
usuarios = null;
}
}
And I think this snippet is not well done. I'm creating a new Adapter instead of updating the data.
#Override
public void onLoadFinished(Loader<ArrayList<Usuario>> loader,
ArrayList<Usuario> usuarios) {
//adapter.notifyDataSetChanged();
listView.setAdapter(new UsuarioAdapter(this, usuarios));
//((BaseAdapter) listView.getAdapter()).notifyDataSetChanged();
}
Why adapter.notifyDataSetChanged() does not work?
So, basically, my app does not crash but all my data is reloaded again every time I restart the app.
Edit: This is my Adapter code:
class UsuarioAdapter extends BaseAdapter {
private ArrayList<Usuario> usuarios;
private LayoutInflater inflater;
public UsuarioAdapter(Context context, ArrayList<Usuario> usuarios) {
this.usuarios = usuarios;
this.inflater = LayoutInflater.from(context);
}
#Override
public int getCount() { return usuarios.size(); }
#Override
public Object getItem(int pos) { return usuarios.get(pos); }
#Override
public long getItemId(int pos) { return pos; }
#Override
public View getView(int pos, View convertView, ViewGroup arg) {
LinearLayout itemView;
if (convertView == null) {
itemView = (LinearLayout) inflater.inflate(R.layout.list_item, null);
} else {
itemView = (LinearLayout) convertView;
}
ImageView avatar = (ImageView) itemView.findViewById(R.id.avatar);
TextView nombre = (TextView) itemView.findViewById(R.id.nombre);
TextView edad = (TextView)itemView.findViewById(R.id.edad);
// Set the image ... TODO
nombre.setText(usuarios.get(pos).getNombre());
edad.setText(String.valueOf(usuarios.get(pos).getEdad()));
return itemView;
}
}
The call to notifyDataSetChanged() won't change the data your adapter is using. You need to update the data the adapter has, then call that method.
NotifyDataSetChanged() will only tell the adapter it needs to create it's views, but it does not change the data. You need to handle that yourself.
In your adapter add:
public void setUsuario(List<Usuario> usuarios) {
this.usuarios = usuarios;
}
Then in onLoadFinished() call the new method, then notifyDataSetChanged().
listView.getAdapter().setUsuario(usuarios);
listView.getAdapter().notifiyDataSetChanged();
I've found the solution. The onStartLoading was the guilty:
#Override
protected void onStartLoading() {
if (usuarios != null) {
deliverResult(usuarios); // Use cache
} else {
forceLoad();
}
}
In my original post forceLoad was always called. It must be in the else branch.