Populating a ListView using AsyncTaskLoader - android

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.

Related

How to update an item on a recyclerview after excuting AsyncTask doInBackground?

I'm creating a chat feature for an application and it works super fine. But I would like to show the user that message has been sent or it still wating for the server's response.
Fields:
List<ChatMessage> chatMessages;
ChatAdapter chatAdapter;
RecyclerView chatRecyclerView;
ImageButton submitMessageBtn;
this how I send a message on my ChatActivity class:
public void submitMessage(final String messageType, final byte[] message){
final ChatMessageResponse messageObject = new ChatMessageResponse();
new AsyncTask<Void, Void, Void>() {
#Override
protected void onPreExecute() {
super.onPreExecute();
messageObject.setMessage( message);
messageObject.setYours(true);
messageObject.setUserNickname(getNickname());
messageObject.setCreationDate(DateTime.now().withZone(DateTimeZone.UTC));
messageObject.setType(messageType);
AddMessage(messageObject);
}
#Override
protected Void doInBackground(Void... voids) {
try {
chatClient.chat().sendMessage(eventId, messageType, message);
runOnUiThread(new Runnable() {
#Override
public void run() {
// Update message on the list after has been sent to server
}
});
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}.execute();
}
public void AddMessage(ChatMessage message)
{
chatMessages.add(message);
chatAdapter.notifyDataSetChanged();
chatRecyclerView.scrollToPosition(chatMessages.size() -1);
}
When message is immediatly added to the adapter it should look like this:
my ChatAdapter class is setup like this:
public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ChatViewHolder> {
private static final int VIEW_TYPE_MESSAGE_THIS_USER = 0;
private static final int VIEW_TYPE_MESSAGE_OTHER_USER = 1;
private final Activity activity;
public List<ChatMessage> chats=new ArrayList<>();
ArrayList<String> usercolor=new ArrayList<>();
Context mContext;
View view;
public ChatAdapter(List<ChatMessage> chats, Context mContext, Activity activity) {
this.chats = chats;
this.mContext = mContext;
this.activity = activity;
}
#Override
public ChatViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
mContext = parent.getContext();
if (viewType == VIEW_TYPE_MESSAGE_OTHER_USER) {
view = View.inflate(mContext, R.layout.message_item_left, null);
} else if (viewType == VIEW_TYPE_MESSAGE_THIS_USER){
view = View.inflate(mContext, R.layout.message_item, null);
}
return new ChatViewHolder(view,(View.OnLongClickListener)activity);
}
#Override
public void onBindViewHolder(final ChatViewHolder holder, int position){
final ChatMessageResponse m = (ChatMessageResponse) chats.get(position);
if (getItemViewType(position) == VIEW_TYPE_MESSAGE_OTHER_USER){
holder.bindToView1(m);
} else if (getItemViewType(position) == VIEW_TYPE_MESSAGE_THIS_USER)
{
holder.bindToView(m);
}
}
#Override
public int getItemCount() {
return chats.size();
}
#Override
public int getItemViewType(int position) {
return chats.get(position).isYours() ? VIEW_TYPE_MESSAGE_THIS_USER : VIEW_TYPE_MESSAGE_OTHER_USER;
}
}
When the server's response is positive the views in the ChatViewHolder (that I don't show the code because is too long) should change visibility state
Someone told me to get a referece for the view and change it on the activity's asynctask or create a Callback listener for my adapter.
But I have no Idea how to do either one of then any help is appreciated.
Are you familiar with the use of "Callbacks" or "Interfaces"? You can create an interface and implement it in your activity. Pass the callback by parameters in the "AsyncTask" and use it there.
//Interface class
/**
* Created by gmora
*/
public interface IProcess {
void updateAdapter(String result);
}
On Activity:
public class YourActivity extends AppCompatActivity {
private IProcess mProcess;
private Adapter mRecyclerAdapter;
private RecyclerView mRecyclerView;
private List<ChatMessage> chats; //update chats on activity and refresh your adapter
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mProcess = new IProceso() {
#Override
public void updateAdapter(String pException) {
//update chats ... and update mAdater.notifyDataChange()...
// or mRecyclerView.setAdapter(new Adpater.... with new list chats)..
}
};
mRecyclerView = find....
// etc....
mRecyclerAdapter = new RecyclerAdapter( chats, ...);
mRecyclerView.setAdapter(mRecyclerAdapter);
}
}
Finally on AsyncTask... create a external class from AsyncTask please!
/**
* Created by gmora.
*/
public class YourAsyncTaskClass extends AsyncTask<String, Void, String > {
private IProcess iProcess;
public StarSearchPrinterTask(IProcess pIProcess) {
this.iProcess= pIProcess;
}
#Override
protected void onPreExecute() {
//loading... its optional
}
#Override
protected String doInBackground(String... interfaceType) {
// execute webservice or api and get results..
return results;
}
#Override
protected void onPostExecute(String results) {
mIProceso.updateAdapter(results);
}
}

What is the wrong with this onPostexecute method in Asynctask?

public class ShopsList extends AppCompatActivity {
private RecyclerView listView;
private StoreListAdapter mAdapter;
private ArrayList<Stores> stores;
public static final String LOG_TAG = ShopsList.class.getName();
private String sampleURL = "http://104.199.230.125/stores/1.json/";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_shops_list);
StoresAsyncTask task = new StoresAsyncTask();
task.execute(sampleURL);
mAdapter = new StoreListAdapter(this, R.layout.list_item_layout, stores);
listView = (RecyclerView) findViewById(R.id.store_list);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
listView.setLayoutManager(layoutManager);
listView.setAdapter(mAdapter);
private class StoresAsyncTask extends AsyncTask<String, Void, List<Stores>> {
#Override
protected List<Stores> doInBackground(String... URLs) {
if (URLs.length < 1 || URLs[0] == null) {
Log.e("QueryUtils", "URL is is null");
return null;
}
Log.e("QueryUtils", "URL is not null" + URLs[0]);
return QueryHandler.fetchStoreData(URLs[0]);
}
#Override
protected void onPostExecute(List<Stores> data) {
mAdapter.notifyDataSetChanged();
listView.setAdapter(mAdapter);
super.onPostExecute(data);
}
}
}
it doesn't display the list, it just displays an empty list. I am using recyclerview.adapter. There is also this problem that getItemCount() throws nullpointer exception when [return this.stores.size();] is used and the app doesn't open, when i change this line to [return this.stores == null ? 0 : stores.size();] it opens but with empty list.
public int getItemCount() {
Log.e(LOG_TAG, "stores size");
// return this.stores.size();
return this.stores == null ? 0 : stores.size();
}
when i use List view the postexecute method body is this, and it works.
protected void onPostExecute(List<Quakes> data) {
mAdapter.clear();
if (data != null && !data.isEmpty()) {
mAdapter.addAll(data);
}
}
how to correctly execute postexecute method in Asynctask that is related to recyclerview.adapter?
The JSON parsing is error free, only i am unable to load it into the adapter.
This is the adapter
public class StoreListAdapter extends RecyclerView.Adapter {
private ArrayList<Stores> stores = new ArrayList<>();
private int itemResource;
private Context context;
public StoreListAdapter(Context context, int itemResource, ArrayList<Stores> stores) {
this.stores = stores;
this.itemResource = itemResource;
this.context = context;
}
#Override
public storeViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(
R.layout.list_item_layout, parent, false);
return new storeViewHolder(this.context, view);
}
#Override
public void onBindViewHolder(storeViewHolder holder, int position) {
Stores stores = this.stores.get(position);
holder.bindStoreData(stores);
}
#Override
public int getItemCount() {
Log.e(LOG_TAG, "stores size");
return this.stores.size();
}}
When you get code in onPostExecute, you should pass this data in List Adapter
#Override
protected void onPostExecute(List<Stores> data) {
mAdapter = new StoreListAdapter(this, R.layout.list_item_layout, data);
listView.setAdapter(mAdapter);
super.onPostExecute(data);
}
Other way,i think better, if you are making custom list adapter make getter and setter or method like addAll to update data in list adapter.
After edited
Create getter and setter of ArrayList<Stores> stores and then
#Override
protected void onPostExecute(List<Stores> data) {
mAdapter.setStores(data);
mAdapter.notifyDataSetChanged();
}
Also in shopList class private ArrayList<Stores> stores= = new ArrayList<>(); may avoid null exception
You had given answer of your own question programmatically:
Solution 1 from you:
#Override
protected void onPostExecute(List<Stores> data) {
mAdapter.notifyDataSetChanged();
listView.setAdapter(mAdapter);
super.onPostExecute(data);
}
Solution 2 from you:
#Override
protected void onPostExecute(List<Quakes> data) {
mAdapter.clear();
if (data != null && !data.isEmpty()) {
mAdapter.addAll(data);
}
}
In solution 1, you are adding adapter to listview again but doing updating list data to adapter. So this is the mistake you are making here, which is reolved in solution 2.
Conclusion: There is no issue with onPostExecute. Problem is with passing updated data to adapter and making notifyDataDetChange to apply updated data to listview.
So add the data to adapter by creating custome method like you had done with mAdapter.addAll(data/new data/) and then do notifyDataSetChange() to get refresh newly updated data with listview.
#Override
protected void onPostExecute(List<Quakes> data) {
if (data != null && !data.isEmpty()) {
mAdapter.addAll(data);
mAdapter.notifyDataSetChange();
}
}

Updating/Creating Custom Listview on PostExecute inside a Fragment

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.

Listview does not refresh when underlying Loader data changes

First, I'll preface my question with the fact that I'm not using a CursorLoader.
I'm pulling in data from a SQLlite database to populate a listview in a ListFragment. The initial load works well, but once the data is manipulated (i.e. an addition is made to the list), the listview NEVER refreshes to show the new data. I am implementing the Loader callbacks like so:
public class BillListingFragment extends ListFragment implements LoaderManager.LoaderCallbacks<List<Bill>> {
private billListAdapter mAdapter;
private static final int LOADER_ID = 1;
private SQLiteDatabase mDatabase;
private BillsDataSource mDataSource;
private BillsStoreDatabaseHelper mDbHelper;
/**
* The fragment argument representing the fragment type (archive or outstanding)
*/
private static final String ARG_FRAGMENT_TYPE = "fragment_type";
/**
* Returns a new instance of this fragment based on type
*/
public static BillListingFragment newInstance(String type) {
// TODO: Make the fragment type an enum
BillListingFragment fragment = new BillListingFragment();
Bundle args = new Bundle();
args.putString(ARG_FRAGMENT_TYPE, type);
fragment.setArguments(args);
return fragment;
}
public BillListingFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.bill_view_layout, container, false);
return rootView;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mDbHelper = new BillsStoreDatabaseHelper(getActivity());
mDatabase = mDbHelper.getWritableDatabase();
mDataSource = new BillsDataSource(mDatabase);
mAdapter = new billListAdapter(getActivity(), R.layout.bill_row_layout);
setListAdapter(mAdapter);
getLoaderManager().initLoader(LOADER_ID, null, this);
}
#Override
public Loader<List<Bill>> onCreateLoader(int id, Bundle args) {
BillDataLoader loader = new BillDataLoader(getActivity(), mDataSource);
return loader;
}
#Override
public void onLoadFinished(Loader<List<Bill>> loader, List<Bill> data) {
for(Bill bill: data){
mAdapter.add(bill);
}
setListAdapter(mAdapter);
}
#Override
public void onLoaderReset(Loader<List<Bill>> loader) {
mAdapter.clear();
}
#Override
public void onDestroy() {
super.onDestroy();
mDbHelper.close();
mDatabase.close();
mDataSource = null;
mDbHelper = null;
mDatabase = null;
}
public void reload(){
getLoaderManager().restartLoader(LOADER_ID, null, this);
}
private class billListAdapter extends ArrayAdapter<Bill> {
Context context;
public billListAdapter(Context context, int resourceID){
super(context, resourceID);
this.context = context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = getActivity().getLayoutInflater().inflate(R.layout.bill_row_layout, parent, false);
}
TextView payToField = (TextView) convertView.findViewById(R.id.nameField);
TextView dueDateField = (TextView) convertView.findViewById(R.id.overdueField);
payToField.setText(getItem(position).getPayTo());
// calculate days until due
Bill bill = getItem(position);
// TODO: Add how many days until bill in overdue field + add color
JodaTimeAndroid.init(getActivity());
DateTime dueDateDt = new DateTime(bill.getDateDue());
DateTime currentDt = new DateTime();
int daysDifference = Days.daysBetween(currentDt.toLocalDate(), dueDateDt.toLocalDate()).getDays();
// depending on what that differential looks like set text / color
if (daysDifference > 1) {
dueDateField.setText(Integer.toString(daysDifference) + " Days");
} else {
if (daysDifference == 0) {
dueDateField.setText("DUE TODAY");
} else {
if (daysDifference < 0) {
}
}
}
return convertView;
}
}
}
I have debugged my code so I know that the onLoadFinished callback is being made after the data has been manipulated. I also know that adapter contains the updated data at this point. I have tried resetting the adapter via setListAdapter(mAdatper) and every notifyDataChanged-like method I can find, but to no avail. What is going on here and how can I get the listview to update?

Android: Middle fragment doesn't show up

I have 3 view which are set to display on below the other. But the problem is that this view(the middle one) isn't showing up at all on screen.
I am not exactly sure as to how to send this list to the parent view.
This is the tutorial I'm using : http://tausiq.wordpress.com/2012/12/12/android-custom-adapter-listview-with-listfragment-and-loadermanager-inside-fragmentactivity/
Here is the code :
public class MyActivity extends Fragment{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View view = inflater.inflate(R.layout.my_activity_layout, container, false);
DataListFragment list = new DataListFragment();
return view;
}
public static class DataListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<List<Model>> {
CustomListViewAdapter mAdapter;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setEmptyText("No Data Here");
mAdapter = new CustomListViewAdapter(getActivity());
setListAdapter(mAdapter);
setListShown(false);
// Prepare the loader.
getLoaderManager().initLoader(0, null, this);
}
#Override
public Loader<List<Model>> onCreateLoader(int arg0, Bundle arg1) {
System.out.println("DataListFragment.onCreateLoader");
return new DataListLoader(getActivity());
}
#Override
public void onLoadFinished(Loader<List<Model>> arg0, List<Model> data) {
mAdapter.setData(data);
System.out.println("DataListFragment.onLoadFinished");
// The list should now be shown.
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
#Override
public void onLoaderReset(Loader<List<Model>> arg0) {
mAdapter.setData(null);
}
}
public static class DataListLoader extends AsyncTaskLoader<List<Model>> {
List<Model> mModels;
public DataListLoader(Context context) {
super(context);
}
#Override
public List<Model> loadInBackground() {
System.out.println("DataListLoader.loadInBackground");
List<Model> entries = new ArrayList<Model>(5);
entries.add(new Model("1", "2","3"));
entries.add(new Model("1", "2","3"));
entries.add(new Model("1", "2","3"));
return entries;
}
/**
* Called when there is new data to deliver to the client.
*/
#Override public void deliverResult(List<Model> listOfData) {
if (isReset()) {
// An async query came in while the loader is stopped. We
// don't need the result.
if (listOfData != null) {
onReleaseResources(listOfData);
}
}
List<Model> oldApps = listOfData;
mModels = listOfData;
if (isStarted()) {
// If the Loader is currently started, we can immediately
// deliver its results.
super.deliverResult(listOfData);
}
// At this point we can release the resources associated with
// 'oldApps' if needed; now that the new result is delivered we
// know that it is no longer in use.
if (oldApps != null) {
onReleaseResources(oldApps);
}
}
/**
* Handles a request to start the Loader.
*/
#Override protected void onStartLoading() {
if (mModels != null) {
// If we currently have a result available, deliver it
// immediately.
deliverResult(mModels);
}
if (takeContentChanged() || mModels == null) {
// If the data has changed since the last time it was loaded
// or is not currently available, start a load.
forceLoad();
}
}
/**
* Handles a request to stop the Loader.
*/
#Override protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
/**
* Handles a request to cancel a load.
*/
#Override public void onCanceled(List<Model> apps) {
super.onCanceled(apps);
// At this point we can release the resources associated with 'apps'
// if needed.
onReleaseResources(apps);
}
/**
* Handles a request to completely reset the Loader.
*/
#Override protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
// At this point we can release the resources associated with 'apps'
// if needed.
if (mModels != null) {
onReleaseResources(mModels);
mModels = null;
}
}
/**
* Helper function to take care of releasing resources associated
* with an actively loaded data set.
*/
protected void onReleaseResources(List<Model> apps) {}
}
}
Here is the custom adapter:
public class CustomListViewAdapter extends ArrayAdapter<Model>{
private final LayoutInflater mInflater;
public CustomListViewAdapter(Context context) {
super(context, android.R.layout.simple_list_item_2);
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public void setData(List<Model> data) {
clear();
if (data != null) {
for (Model appEntry : data) {
add(appEntry);
}
}
}
/**
* Populate new items in the list.
*/
#Override public View getView(int position, View convertView, ViewGroup parent) {
View view;
if (convertView == null) {
view = mInflater.inflate(R.layout.single_item, parent, false);
} else {
view = convertView;
}
Model item = getItem(position);
((TextView)view.findViewById(R.id.tV_1)).setText(item.getA());
((TextView)view.findViewById(R.id.tV_2)).setText(item.getB());
((TextView)view.findViewById(R.id.tV_3)).setText(item.getC());
return view;
}
}
And this is my main activity:
public class MainActivity extends Activity{
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.song_info_fragment, new TopActivity());
fragmentTransaction.add(R.id.playlist_activity_fragment, new MyActivity());
fragmentTransaction.add(R.id.controls_fragment, new BottomActivity());
fragmentTransaction.commit();
}
}
When I run the code, I only see the top and bottom activities. How can I fix this?
Ok, I was finally able to get this working.
I copied the DataListFragment class into a new class file under the same name and removed the static keywork.
Further, I copied the other section called dataListLoader into another file called DataListLoader.java.
And moved the fragment.add() transaction into the mainActivity file. And that did it!

Categories

Resources