How to update a fragment? - android

I have Messanger App that has a Master/Detail Flow Layout with 2 fragments. I used the template provided by Android Studio.
When the ListFragment is in the foreground and a new user logs in the ListView is not immediately updated and the User does not appear on the List.
When I select a different activity and go back to the ListFragment afterwards the list is updated.
this is where my data is stored. If a new user logs in the addUser method is called and this adds the user to the List and Map.
public static List<ChatContent> ITEMS = new ArrayList<ChatContent>();
public static Map<String, ChatContent> ITEM_MAP = new HashMap<String, ChatContent>();
protected static void addUser(ChatContent userChatContent) {
ITEMS.add(userChatContent);
ITEM_MAP.put(userChatContent.user, userChatContent);
}
This is how I set the listAdapter in the onCreate method of the ListFragment.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ArrayAdapter arrayAdapter = new ArrayAdapter<ChatData.ChatContent>(
getActivity(),
android.R.layout.simple_list_item_activated_1,
android.R.id.text1,
ChatData.ITEMS);
setListAdapter(arrayAdapter);
}
FYI: The same thing happens when I'm on the detailsFragment and a new chat message arrives: the message does not appear unless I leave the activity and come back.

When you update your data ,you should use these code to update the UI.
arrayAdapter.notifyDataSetChanged();

Related

Saving items in listview when added from another activity

I have a listview of items in my ShoppingListActivity.
Items are added from another activity thought an intent. I want to make sure that all items are kept in the list when going between both activities; however, right now my list only has the last item added from the previous activity.
My ShoppingListActivity.class
public class ShoppingListActivity extends Activity {
private ListView mainListView ;
private ArrayAdapter<String> listAdapter ;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_shopping_list);
// Find the ListView resource.
mainListView = (ListView) findViewById( R.id.mainListView );
ArrayList<String> shoppingList = new ArrayList<String>();
shoppingList.add(itemLookup());
// Create ArrayAdapter using the shopping list.
listAdapter = new ArrayAdapter<String>(this, R.layout.simplerow, shoppingList);
// Set the ArrayAdapter as the ListView's adapter.
mainListView.setAdapter( listAdapter );
}
//Lookup item by ID
public String itemLookup() {
String itemName = "";
Intent intent = getIntent();
Bundle extras = intent.getExtras();
if (intent != null) {
String itemId = extras.getString("BARCODE_ID");
try {
itemName = ItemLookup.lookupById(itemId);
} catch (IOException e) {
e.printStackTrace();
}
}
return itemName;
}
#Override
public void onBackPressed() {
startActivity(new Intent(ShoppingListActivity.this, MainActivity.class));
}
}
I have a feeling I should be putting my add somewhere else. I'm pretty sure I should be passing the list back and forth in a putExtra, but if that's how I have to do it, it's fine.
How can I make sure that the list is maintained between activities?
One way around your problem is Singleton Pattern.
In your case you can implement something like this:
public class ShoppingListManager {
private static ShoppingListManager instance = new ShoppingListManager();
private List<String> shoppingList;
public static ShoppingListManager getInstance() {
return instance;
}
public List<String> getShoppingList() {
return shoppingList;
}
// Make the constructor private so that this class cannot be instantiated
private ShoppingListManager(){
shoppingList = new ArrayList<String>();
}
}
Then access it anywhere in your code.
ShoppingListManager.getInstance().getShoppingList();
One point to remember never store context in singleton classes as it will lead to memory leaks.
Keeping your data structures in an Activity makes your app prone to data loss because Activities can be destroyed at various times and for a variety of reasons, including rotating the device between portrait and landscape.
You should use a separate class to store and track which items are in the shopping list. The Activity with the ListView should only get the list of items stored and display them. Anything that causes an item to be added should simply trigger a reload of the list (if the Activity is running in the foreground), otherwise the Activity should see that new item anyway the next time it starts.
If you also need your data to be persistent after your process is terminated, you should look into the possible data storage options available.

How to notifydatasetchanged between two activities

I have two activities that each contain fragments. Activity A has a fragment with a ListView. Rows of the ListView are obtained from a SQLiteDatabase.
Activity B contains a fragment that enables entries of the database to be edited. When a user edits a database entry, saves the data, then returns to Activity A via the backbutton or:
actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeButtonEnabled(true);
How do I notify the ListView in Activity A that there has been a change to the database? I understand how to communicate between an Activity and it's fragments, but how do I implement a notifydatasetchanged when the fragments are in two different activities?
I suspect that going back to Activity A via the back button is simply recalling the view from the stack, but that view has changed when there is a database change.
You could simply call the notifyDataSetChanged in onResume in your Activity A. So when you close or go back from Activity B to A, it will update the data. Don't forget to check if the adapter is different than null, otherwise it could crash the first time you open the App.
You could also make a public function in your Activity A which updates the list and call it from your Activity B.
class ActivityA
public static void updateList() {
if(adapter != null){
adapter.notifyDataSetChanged;
}
}
class ActivityB
ActivityA.updateList();
Hope it helps :)
you could start activity with startActivityForResult(start activityB) and override method onActivityResult in activityA and notifyDataSetChanged when needed.
You will need to read the updated dataset from your SQLite database and pass it adapter again. After you need to notify adapter. For example, like in the code below:
In your Activity A
#Override
protected void onResume() {
super.onResume();
// method to read new dataset
Database ourDB = new Database(this);
ourDB.openDB();
data = ourDB.getData();
ourDB.closeDB();
if(adapter == null) {
// initializes adapter when first launched
adapter = new RecyclerViewAdapter(data, this);
yourListView.setAdapter(adapter);
}
else {
// in subsequent returns to activity we are replacing
// old data with new and reusing existing adapter
adapter.updateData(data);
}
}
In your adapter class include similar method:
public void updateData(ArrayList<String> newData) {
data = newData;
this.notifyDataSetChanged();
}

Android increasing database performance

Well I hava a listview and if clicked a listview element a Viewpager openning to show content.
both listview and Viewpager are same adaptor which is coming from database.
The problem is: when viewpager openning it needs to create same list adaptor to use.
How can I use Listview's list for viewpager without creating again and again.
I take this codes from Big Nerd's book:
This is listfragment:
public class framelist extends ListFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
TestAdapter mDbHelper = new TestAdapter(getActivity());
mDbHelper.createDatabase();
mDbHelper.open();
List<element> mElements = mDbHelper.getTestData();
This is Viewpager FragmentActivity
public class CrimePagerActivity extends FragmentActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mViewPager = new ViewPager(this);
mViewPager.setId(R.id.viewPager);
setContentView(mViewPager);
TestAdapter mDbHelper = new TestAdapter(this);
mDbHelper.createDatabase();
mDbHelper.open();
mElements = mDbHelper.getTestData();
And this is Viewpager fragment:
public class CrimeFragment extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
mNum2 = (int) getArguments().getInt("num");
TestAdapter mDbHelper = new TestAdapter(getActivity());
mDbHelper.createDatabase();
mDbHelper.open();
List<element> mElements = mDbHelper.getTestData();
melement = mElements.get(mNum2);
I suggest you use the Loader framework in order to query things. If nothing else, refer to my answer to similar question, it provides some very relevant links, that could help you in your case study.
One more hint: if you just use straightforward approach, which is "query database through OpenSqliteDbHelper instance from your Activity", you have to be very careful and take care about your app lifecycle and Cursor's lifecycle management. And of course, it is very hard to improve performance, if you use such approach. All these problems disappear like magic if you use the suggested Loader approach, as it persist data on configuration change and you do not have to query your data again and again.
One more hint: if you choose to use the straightforward (that is, without Loader) approach, make sure you query your data on a separate thread. That will keep your UI from being frozen.
Similar questions were already asked and answered here, use search, if nothing else
What are you trying to achieve by showing the same content in both list as well as viewpager
Anyway, in your CrimePagerActivity instead of recreating a list again, why not copy the contents to the list
List<element> newList = mDbHelper.getTestData();
mElements.clear();
mElements..addAll(newList);
One more advice, your subject says increasing db performance , but you are asking all about list
Follow singleton pattern for DB and donot create new DB everytime, and make sure you synchronize any calls to db

AsyncTask runs on each page of the ViewPager

I have 3 Tabs like in the android development tutorial
Now what I want to do is very simple I use Fragments on each page. I want to show different content from a rss feed on each page.
The problem is when I go to the next tab it runs AsyncTask (which is in onCreateView) of the previous Fragment.
So you start on Page 1 it loads the content fine. Then when you go to Page 2 is runs the onCreateView of the Fragment of Page 1 again. And obviously gives an NullException. The point is it should not be running AsyncTask of Page 1 at all at that Page 2.
I don't think there is any example code needed if so tell me which part you need to see. Then I will edit my question.
AsyncTask inside a ListFragment :
public class MyAsyncTask extends AsyncTask<List<String>, Void, List<String>>
{
// List of messages of the rss feed
private List<Message> messages;
private volatile boolean running = true;
#SuppressWarnings("unused")
private WeakReference<NieuwsSectionFragment> fragmentWeakRef;
private MyAsyncTask(NieuwsSectionFragment fragment)
{
this.fragmentWeakRef = new WeakReference<NieuwsSectionFragment>(fragment);
}
#Override
protected void onPreExecute()
{
super.onPreExecute();
mProgress.show();
// progress.setVisibility(View.VISIBLE); //<< set here
}
#Override
protected void onCancelled()
{
Log.w("onCancelled", "now cancelled");
running = false;
}
#Override
protected List<String> doInBackground(List<String>... urls)
{
FeedParser parser = FeedParserFactory.getParser();
messages = parser.parse();
List<String> titles = new ArrayList<String>(messages.size());
for (Message msg : messages)
{
titles.add(msg.getTitle());
// Log.w("doInBackground", msg.getTitle());
}
return titles;
}
#Override
protected void onPostExecute(List<String> result)
{
super.onPostExecute(result);
mProgress.dismiss();
if (result != null)
{
PostData data = null;
listData = new PostData[result.size()];
for (int i = 0; i < result.size(); i++)
{
data = new PostData();
data.postTitle = result.get(i);
data.postThumbUrl = "http://igo.nl/foto/app_thumb/28991-Taxi-vast-na-poging-tot-nemen-van-sluiproute.jpg";
listData[i] = data;
Log.w("onPostExecute", "" + listData[i].postTitle);
}
adapter = new PostItemAdapter (getActivity(), android.R.layout.simple_list_item_1, listData);
setListAdapter(adapter);
adapter.notifyDataSetChanged();
}
}
}
It's called inside a method and that method is executed inside the onCreateView of the ListFragment :
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
startNewAsyncTask();
View rootView = inflater.inflate(R.layout.fragment_section_nieuws, container, false);
return rootView;
}
#SuppressWarnings("unchecked")
public void startNewAsyncTask()
{
MyAsyncTask asyncTask = new MyAsyncTask(this);
this.asyncTaskWeakRef = new WeakReference<MyAsyncTask>(asyncTask);
asyncTask.execute();
}
The LogCat :
Try using isAdded() before onPostExecute(). isAdded() returns true if the fragment is currently added to its activity.
http://developer.android.com/reference/android/app/Fragment.html#isAdded()
#Override
protected void postExecute(){
if(isAdded()){
//perform Display Changes
}
}
Move your
startNewAsyncTask();
to onActivityCreated()
I'm assuming your using FragmentPagerAdapter with your ViewPager.
To enable smooth animations, the ViewPager by default keeps the current fragment and the two neighbors in resumed state. So onCreateView is not the best place to start the AsyncTask.
Instead you need to create a custom listener interface. The fragments in the ViewPager should implement it, and call the new interface from the ViewPager's OnPageChangeListener.
Check out my answer to this question or you can read the whole tutorial here.
You're getting that exception because you're calling getActivity() too early. You should do it after onActivityCreated() (see this diagram)
Executing of onCreateView() in background is fine and actually is default behaviour. The thing is, ViewPager is optimised to load a content of neighbour non-visible pages in background to improve UX. You can do this:
mViewPager.setOffscreenPageLimit(2); (default value is 1) to load all 3 pages at once (1 is loading as currently visible and other 2 as optimisation). Or set it to 0 to disable this behaviour, but it's not the best idea.
In general, you should cash your loaded data and do not load it again by making your fragment's lifecycle methods as light as possible. Page limit of 2 is fine for 3 pages, but if you'll have for example 10 pages, limit of 9 is too much.
If I've understood your question right, I think you need unique content with each Fragment right?
Try using the varible arguments of the execute method. For example:
yourTask.execute(<some-unique-URL>, parameter, one-more-parameter);
In this way you can pass a unique URL per fragment form which you can get your content.
I feel you already have this. The doInBackground method has the List of URLs. You just need to pass that information in the execute method and utilize it in doInBackground.
Hope this helps!
It is normal that it runs the AsyncTask from the adjacent Fragments, since the ViewPager + PagerAdapter combo, works loading the current, previous and next Fragment.
You should focus the problem not to stop AsyncTask from running, but to let it run w/o throwing a NullPointerException.
The following should be called inside onCreateView()
adapter = new PostItemAdapter (getActivity(), android.R.layout.simple_list_item_1, myList);
setListAdapter(adapter);
And then, onPostExecute()
myList.clear();
myList.addAll(listData);
adapter.notifyDataSetChanged();
The ViewPager will create views for fragments in adjacent pages and destroy views for fragments which are not adjacent to current page. Thus, the onCreateView of page 1 will get called if you navigate from page1->page2->page3->page2. You can have the viewpager keep more pages in memory by using ViewPager.setOffscreenPageLimit.
The fragmentPagerAdapter retains the fragment objects. Only the views are destroyed. Thus, when viewpage recreates page1's view, the fragment object is the same. Hence, all the fields in the fragment will get retained.
As in most applications where there's no realtime data, it is not required not load the data every time the view of the fragment is created, you can store the data in the fragment object after loading. Then, before starting the AsyncTask in onCreateView/onActivityCreated, check if the data has been previously loaded or not.
class PageFragment {
private List<String> mData;
...
void onActivityCreated() {
if (data == null) { // OR if the data is expired
startAsyncTask();
} else {
updateViews();
}
}
void updateViews() {
// Display mData in views
}
class LoadDataTask extends AsyncTask<List<String>, ..., ...> {
...
void onPostExecute(List<String> result) {
PageFragment.this.mData = result;
PageFragment.this.updateViews();
}
}
I recommend that you use loaders for loading data for a fragment. For your purpose, you can configure a loader to load data only once.
This is a great tutorial on Loaders.
http://www.androiddesignpatterns.com/2012/07/loaders-and-loadermanager-background.html
In the tutorial, the loader is configured to return previous data immediately if available, and then fetch data in background and return it after fetching completes. Thus, the UI will get updated after fresh data gets downloaded but at the same time, it will show the previous data initially while the download happens.
You can use another activity - this activity will run asynctask and then move to your fragment related activity. In this way it should call only once.
In case you need to update Fragment UI using this AsyncTask then use a static method to call through AsyncTask.

android - pass data from one activity to a list in a second activity

new to droid programming. im having a small problem that im sure is simply fixed but ive done some searching and a bunch of tutorials but cant seem to find just what i need so i figured id ask. My app has 2 activites, the first activity is just a simple form where a user enters course information(class title, professor..etc.)
the first activity passes the data which is supposed to be stored in a list in the second activity. problem is that only the first course gets stored in the list, after the first time nothing new gets added to the second activity. Can someone point me in the right direction please? thanks in advance
First Activity
public class CourseDetail extends Activity {
//Course c = new Course();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button save=(Button)findViewById(R.id.save);
save.setOnClickListener(onSave);
}
private View.OnClickListener onSave=new View.OnClickListener() {
public void onClick(View v) {
EditText course=(EditText)findViewById(R.id.course);
EditText professor=(EditText)findViewById(R.id.professor);
EditText location=(EditText)findViewById(R.id.location);
EditText officehrs=(EditText)findViewById(R.id.officehrs);
Intent i=new Intent(CourseDetail.this, CourseList.class);
i.putExtra("myCourse", course.getText().toString());
i.putExtra("myProfessor", professor.getText().toString());
i.putExtra("myLocation", location.getText().toString());
i.putExtra("myOfficehrs", officehrs.getText().toString());
startActivity(i);
}
};
}
Second Activity
public class CourseList extends Activity {
Button btnCourse;
List<Course> model = new ArrayList<Course>();
CourseAdapter adapter=null;
private String dCourse="";
private String dProfessor="";
private String dLocation="";
private String dOfficehrs="";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.clist);
ListView list =(ListView)findViewById(R.id.courses);
adapter=new CourseAdapter();
list.setAdapter(adapter);
Course c = new Course();
Bundle extras = getIntent().getExtras();
dCourse = extras !=null ? extras.getString("myCourse") :"no value entered";
dProfessor = extras !=null ? extras.getString("myProfessor") :"no value entered";
dLocation = extras !=null ? extras.getString("myLocation") :"no value entered";
dOfficehrs = extras !=null ? extras.getString("myOfficehrs") :"no value entered";
c.setCourse(dCourse);
c.setProfessor(dProfessor);
c.setLocation(dLocation);
c.setOfficeHrs(dOfficehrs);
btnCourse =(Button)findViewById(R.id.btnCourse);
btnCourse.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
finish();
}
});
}
You are just getting the user entered value in CourseDetail activity and displaying the received value inside the CourseList activity, that means you are not storing these values permanently.
Go through this Android - Data Storage document.
When you move to 2nd activity i.e. CourseList activity, at that time fetch the data from the SQLite table and display the same. whenever you get new values from previous activity, at that time just update the list by adding the new data in ArrayList and make a call on adapter.notifyDataSetChanged()
Some suggestions:
Have your CourseList extend ListActivity instead of just Activity - check out some tutorials on that which should help you set things up correctly.
There seems to be a bit of confusion with how you're handling your lists - you have your model variable but don't seem to be doing anything with it. Again, have a look at a ListView tutorial (just google "android listview tutorial").
You seem to have figured out that you can use "intents" to pass information from one activity to another, but since you're only doing this in the onCreate() method, it's only happening once. Try doing this in your ListActivity's adapter once for each item.
Don't give up on Android, keep trying :-)
Some suggestion:
You have to add your object to the adapter: adapter.add(c); after you get the data.
Call adapter.notifyDataSetChanged() to notify the system that your data for the listView has been changed. Call list.invalidate() to refresh it.
I noticed that you set the button with the finish() method. Hmm, if you do so, the next time you get to CourseList Activity from CourseDetail, the adapter will be null again. No previously received data will be available. Is this what you really want?
The problem is you are not adding the newly added items to the List.So before setting adapter you have to add all your objects like
list.add(c);

Categories

Resources