I just discovered how to use the action bar to have a OptionsMenu. I just solved a problem with it and I wanted to know if I did it the right way.
What I did
So, I added the menu to a fragment started by an activity, adding to it the setHasOptionsMenu(true) in the onCreate method and setting up the two convenience methods public void onCreateOptionsMenu() and public boolean onOptionsItemSelected() of course setting up the xml first.
The problem
Now, if I rotate my device in landscape mode the button in the action bar duplicates. If I put it in portrait mode again, the number of items stays the same (I'm sure it doesn't actually increase, checked the size in different ways) though.
What I thought and how I solved it
So, I was thinking about some fragment overlaps, but if they actually overlap then I shouldn't notice anything, right?
Is the menu being recreated, while not destroyed when the fragment is destroyed?
I solved the problem removing the item in the menu, in the Fragment.onDestroy() method manually.
Is the menu supposed to be cleared manually, or there is something wrong with my code (eg. fragments overlapping, etc..) ?
domenicop said:
Is the menu supposed to be cleared manually, or there is something wrong with my code (eg. fragments overlapping, etc..) ?
Click to expand...
Click to collapse
Could you please post your code? That would make helping much easier.
nikwen said:
Could you please post your code? That would make helping much easier.
Click to expand...
Click to collapse
Of course. Thanks for the interest.
This is the onCreate method of MainActivity.java, from where I handle the arrangement of fragments on the screen.
Also, place check my comment under the line "Log.d(TAG, "Orientation Portrait detected.");", where I try to explain why I handle fragments in that specific way. It's the way I understood it, but I'm not sure if that's correct.
Code:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Initialize the mDatabase
mContactsDatabase = ContactsDatabase.getInstance(getApplicationContext());
// Determine device orientation
mDualPane = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
// Set the fragment that will be changed
mFragmentToReplace = mDualPane ? R.id.contactsPagerFragmentContainer : R.id.portraitFragmentContainer;
// Set up the GUI
FragmentManager fm = getSupportFragmentManager();
if (!mDualPane) {
Log.d(TAG, "Orientation Portrait detected.");
// First, look for previously saved fragments
// findByFragmentId(int id) look for the fragment that was previously associated
// to the resource that has for id the argument passed in. Then, we try to cast it
// to the type of fragment we want, and if that goes all right, we have our fragment.
// Else, null will be returned.
mContactListFragment = (ContactListFragment) fm.findFragmentById(R.id.portraitFragmentContainer);
if (mContactListFragment == null) {
mContactListFragment = ContactListFragment.newInstance(mContactsDatabase);
fm.beginTransaction()
.replace(R.id.portraitFragmentContainer, mContactListFragment)
.commit();
}
}
else {
Log.d(TAG, "Orientation Landscape detected.");
// First, look for previously saved fragments
mContactListFragment = (ContactListFragment) fm.findFragmentById(R.id.landscapeFragmentContainer);
mContactsPagerFragment = (ContactsPagerFragment) fm.findFragmentById(R.id.contactsPagerFragmentContainer);
if (mContactListFragment == null) {
mContactListFragment = ContactListFragment.newInstance(mContactsDatabase);
fm.beginTransaction()
.replace(R.id.contactListFragmentContainer, mContactListFragment)
.commit();
}
if (mContactsPagerFragment == null) {
final int FIRST_CONTACT_POSITION = 0;
mContactListFragment = ContactListFragment.newInstance(mContactsDatabase);
mContactsPagerFragment =
ContactsPagerFragment.newInstance(FIRST_CONTACT_POSITION, mContactsDatabase);
fm.beginTransaction()
.replace(R.id.contactsPagerFragmentContainer, mContactsPagerFragment)
.commit();
}
}
}
This are the relevant pieces of the ContactListFragment.java, that have something to do with the menu
Note that I changed the way I checked for double menu entries.
Now I check if (menu.size() != 1), because I have just one item in there.
If I remove that clause, there will be two item in the menu after rotating to landscape mode, and if I take the device back to portrait mode, the two items will remain two. That's so even if I change from landscape to portrait and vice versa a hundred times from now, the menu items will always be two.
Code:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
Bundle args = getArguments();
mDatabase = (ContactsDatabase) args.getSerializable(DATABASE);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
if (menu.size() != 1) {
inflater.inflate(R.menu.contact_list_fragment_menu, menu);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.addContactMenu:
mCallback.onAddContactMenuOptionSelected();
default:
return super.onOptionsItemSelected(item);
}
}
domenicop said:
I just discovered how to use the action bar to have a OptionsMenu. I just solved a problem with it and I wanted to know if I did it the right way.
What I did
So, I added the menu to a fragment started by an activity, adding to it the setHasOptionsMenu(true) in the onCreate method and setting up the two convenience methods public void onCreateOptionsMenu() and public boolean onOptionsItemSelected() of course setting up the xml first.
The problem
Now, if I rotate my device in landscape mode the button in the action bar duplicates. If I put it in portrait mode again, the number of items stays the same (I'm sure it doesn't actually increase, checked the size in different ways) though.
What I thought and how I solved it
So, I was thinking about some fragment overlaps, but if they actually overlap then I shouldn't notice anything, right?
Is the menu being recreated, while not destroyed when the fragment is destroyed?
I solved the problem removing the item in the menu, in the Fragment.onDestroy() method manually.
Is the menu supposed to be cleared manually, or there is something wrong with my code (eg. fragments overlapping, etc..) ?
Click to expand...
Click to collapse
I agree with nikwen but broadly the idea is...
- the menu belongs to the Activity not the Fragment.
- for an Activity for which you'd like an options menu you override onCreateOptionsMenu and do 2 things...
1/ call menu.add(...) for each menu item
2/ return 'true' at the end, so that your menu can be displayed
Also, see the Activity docs for onCreateOptionsMenu()
"This is only called once, the first time the options menu is displayed.
To update the menu every time it is displayed, see onPrepareOptionsMenu(Menu)."
If you want to add menu options to the Activity menu from within a Fragment you can also..
- override setHasOptionsMenu() in Fragment to return true
- override onCreateOptionsMenu() to add any items you like (just like you did at the Activity level)
- override onDestroyOptionsMenu()
The key thing here is that onCreateOptionsMenu() in Fragment is adding to the Activity menu.
It's hard to see without debugging exactly what's going on here but watch for calls to all of the above methods in Activity and Fragment and that will probably tell you what's going on.
If it is possible to control all menu options at the Activity level instead of the Fragment level, I would do that.
If not, and you need to use the Fragment menu methods, beware of Android's Fragment management (saving / restoring state)
Also, make sure your Fragments have an ID and/or tag.
(from the docs..)
"The fragment being instantiated must have some kind of unique identifier so that it can be re-associated with a previous instance if the parent activity needs to be destroyed and recreated. This can be provided these ways:
If nothing is explicitly supplied, the view ID of the container will be used.
android:tag can be used in <fragment> to provide a specific tag name for the fragment.
android:id can be used in <fragment> to provide a specific identifier for the fragment."
Finally, if all else fails, using Menu's findItem() before calling add() would be a safeguard, I suppose.
---------- Post added at 10:14 PM ---------- Previous post was at 09:52 PM ----------
domenicop said:
Of course. Thanks for the interest.
Click to expand...
Click to collapse
Sorry caught in the crossfire - have only just seen your code.
I think much of the above applies.
Set the menu up in the Activity instead of the Fragment, if you can.
I would also consider using layout-port and layout-land to control your two different layouts.
PicomatStudios said:
I agree with nikwen but broadly the idea is...
- the menu belongs to the Activity not the Fragment.
- for an Activity for which you'd like an options menu you override onCreateOptionsMenu and do 2 things...
1/ call menu.add(...) for each menu item
2/ return 'true' at the end, so that your menu can be displayed
Also, see the Activity docs for onCreateOptionsMenu()
"This is only called once, the first time the options menu is displayed.
To update the menu every time it is displayed, see onPrepareOptionsMenu(Menu)."
If you want to add menu options to the Activity menu from within a Fragment you can also..
- override setHasOptionsMenu() in Fragment to return true
- override onCreateOptionsMenu() to add any items you like (just like you did at the Activity level)
- override onDestroyOptionsMenu()
The key thing here is that onCreateOptionsMenu() in Fragment is adding to the Activity menu.
It's hard to see without debugging exactly what's going on here but watch for calls to all of the above methods in Activity and Fragment and that will probably tell you what's going on.
If it is possible to control all menu options at the Activity level instead of the Fragment level, I would do that.
If not, and you need to use the Fragment menu methods, beware of Android's Fragment management (saving / restoring state)
Also, make sure your Fragments have an ID and/or tag.
(from the docs..)
"The fragment being instantiated must have some kind of unique identifier so that it can be re-associated with a previous instance if the parent activity needs to be destroyed and recreated. This can be provided these ways:
If nothing is explicitly supplied, the view ID of the container will be used.
android:tag can be used in <fragment> to provide a specific tag name for the fragment.
android:id can be used in <fragment> to provide a specific identifier for the fragment."
Finally, if all else fails, using Menu's findItem() before calling add() would be a safeguard, I suppose.
---------- Post added at 10:14 PM ---------- Previous post was at 09:52 PM ----------
Sorry caught in the crossfire - have only just seen your code.
I think much of the above applies.
Set the menu up in the Activity instead of the Fragment, if you can.
I would also consider using layout-port and layout-land to control your two different layouts.
Click to expand...
Click to collapse
Wow. thanks for that detailed answer. :good: (Why have I used all my thanks?)
I've always put it into the Activity as well but it might make sense to add it to the Fragment sometimes. For example if you need the same Fragment in multiple Activities...
In that case you'd need to add a listener to the item in the Fragment method though.
@PicomatStudio Thank you for the detailed answer, I really appreciate that =)
Now I'm creating the menu in the activity and I don't have to worry about the fragments life cycle anymore.
The only problem is: I didn't get the menuInflater for free here, I guess it's because of the fact that in the Fragment scenario, I had to use the Activity inflater, whereas here in the activity, I can get my own, with getMenuInflater().
Here's the way I modified it, now is in MainActivity.java.
Please report back if you find something strange
Code:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
if (getSupportFragmentManager().findFragmentByTag(CONTACT_LIST_FRAGMENT) != null)
getMenuInflater().inflate(R.menu.contact_list_fragment_menu, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.addContactMenu:
onAddContactMenuOptionSelected();
default:
return super.onOptionsItemSelected(item);
}
}
domenicop said:
@PicomatStudio Thank you for the detailed answer, I really appreciate that =)
Now I'm creating the menu in the activity and I don't have to worry about the fragments life cycle anymore.
The only problem is: I didn't get the menuInflater for free here, I guess it's because of the fact that in the Fragment scenario, I had to use the Activity inflater, whereas here in the activity, I can get my own, with getMenuInflater().
Here's the way I modified it, now is in MainActivity.java.
Please report back if you find something strange
Code:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
if (getSupportFragmentManager().findFragmentByTag(CONTACT_LIST_FRAGMENT) != null)
getMenuInflater().inflate(R.menu.contact_list_fragment_menu, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.addContactMenu:
onAddContactMenuOptionSelected();
default:
return super.onOptionsItemSelected(item);
}
}
Click to expand...
Click to collapse
I don't tend to use MenuInflater (just menu.add()..)
But looking at the docs it takes a Context so could you do..
Code:
new MenuInflater(this);
.. from your Activity ?
On a general design point the less your Activity depends on its Fragments the better, I reckon.
domenicop said:
The only problem is: I didn't get the menuInflater for free here, I guess it's because of the fact that in the Fragment scenario, I had to use the Activity inflater, whereas here in the activity, I can get my own, with getMenuInflater().
Click to expand...
Click to collapse
Well, you can get one:
Code:
getActivity().getMenuInflater();
Related
In my Android app, I have a sound that I want to play when a certain selection has been made from a spinner, but I want it to play the when the user actually makes the proper selection (or just after). My problem is that although the sound does play when they make the correct selection, as long as that selection stays chosen, it also plays every time the app starts up, when it should ONLY play at the time it's chosen. I think I need to change my setOnItemSelectedListener to setOnItemClickListener, but I'm not sure how (still pretty new to java). Can any generous soul out there show me how to change this up (assuming that's how to best solve this problem)?
Here is the code I have now:
Code:
fitnessSpinner = (Spinner) findViewById(R.id.fitness_spinner);
ArrayAdapter adapter4 = ArrayAdapter.createFromResource(
this, R.array.fitness_array, android.R.layout.simple_spinner_item);
adapter4.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
fitnessSpinner.setAdapter(adapter4);
fitnessSpinner.setOnItemSelectedListener(new OnItemSelectedListener()
{
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long i) {
Log.d("test", "p: " + position + " " + i);
if(position == 0) {
//First Entry
MediaPlayer mp = MediaPlayer.create(mContext, R.raw.bowchica);
mp.start();
} if(position == 4) {
MediaPlayer mp = MediaPlayer.create(mContext, R.raw.debbie2);
mp.start();
}
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {
}
});
I haven't try the below code but you can try it on your own and tell us.
In onCreate() declare MediaPlayer mp;
In every if statement that you use for check insert this code:
Code:
if(mp!=null){mp.release();}
int resid = R.raw.yoursound;
mp = MediaPlayer.create(this, resid);
After that override the methods onPause() and onResume() and insert this:
if(mp!=null){mp.release();}
If it is still playing a sound when you start your app, then you should check your code again if you have set as default option any of your selection options.
I would LOVE to try this out...Unfortunately, I'm way too dumb at this point point ot figure out exactly where those code snippets would go inside of what I already have.
Does anyone have a couple of minutes to show me where it would go?
Below is a sample code. Since i don't know your code I give you a snippet that you should adjust it to your code.
Code:
public class SampleSound extends Activity{
private Spinner fitnessSpinner;
private MediaPlayer mp;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);//here goes your layout
setViews();//here you will set all your views(spinners buttons textviews etc..)
setAdapters();//set your adapters here
setListeners();//
}
private void setListeners() {
fitnessSpinner.setOnItemSelectedListener(new OnItemSelectedListener(){
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long i) {
Log.d("test", "p: " + position + " " + i);
if(position == 0) {
//First Entry
if(mp!=null){mp.release();}
int resid = R.raw.bowchica;
mp = MediaPlayer.create(this, resid);
mp.start();
} if(position == 4) {
if(mp!=null){mp.release();}
int resid = R.raw.debbie2;
mp = MediaPlayer.create(this, resid);
mp.start();
}
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {
}
});
}
private void setAdapters() {
ArrayAdapter adapter4 = ArrayAdapter.createFromResource(this, R.array.fitness_array, android.R.layout.simple_spinner_item);
adapter4.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
fitnessSpinner.setAdapter(adapter4);
}
private void setViews() {
fitnessSpinner = (Spinner) findViewById(R.id.fitness_spinner);
}
public void onResume(){
super.onResume();
if(mp!=null){mp.release();}
}
public void onPause(){
super.onPause();
if(mp!=null){mp.release();}
}
}
I really appreciate the help. I put the code in my routine, but it still plays the sound every time the activity is loaded (as long as the selection in the spinner is correct). It should only play the sound when the correct selection is made.
Any other ideas?
I am sure that your Spinner is set to some value (since you have values to display). Because your Spinner points to a selection (doesn't matter if you have selected or it is selected by default) your sound plays (even when you start the app).
A way to stop the sound playing at start is to declare and an other Item like you did with the previous 4 and set it as default selection of your Spinner.
To sum up:
1.You have to append in R.array.fitness_array an Item (like you did with the previous Items) and give it a name.
2.At the end of method setAdapters() insert this:
Code:
fitnessSpiner.setSelection(5);// or whatever is your selection number
Now it should work but you should know that this is not a good practice and you should try make a ListView or something else.
I'd be happy to change this out to a listview, or whatever would work. I just have to give my user a choice of 4 or 5 items, from which they can choose only one. Something like a drop down box, but in Android, I thought my only option was a spinner. But whatever I use, I have to be able to play a sound when certain items are chosen, but ONLY when those items are chosen, NOT whenever the activity is called up.
Any specific ideas of what I might change to?
What if I had another control like a textview or an edittext (with it's visibility property set to false) that I programatically populated with the users selection (when it's the selection that I want) and then have an OnItemClcickListener set to play the sound?
Could that work?
I will answer from the last to the top of your questions.
1.You can do whatever you want with android. You want TextViews and EditTexts with complex and nested Layouts you can do it. Write services that will communicate with your contacts through a content provider? You can do it.
Write, read and test code. Only this way you will actually learn.
2.Read developer.android.com. Read the android tutorials from there and specifically the Notepad example. You will learn a lot.
A good resource with small examples for ListViews is this.
3.Have you tried the changes I told you from the last post? Did it worked?
Since you just started with android and programming you must first be happy if you have the expected result and then read more to make your code better
Your suggested changes (fitnessSpiner.setSelection(5);// or whatever is your selection number) would stop the sound from playing, but defeat the apps purpose. Every time this activity is loaded, the spinners hit preferences to load the previously stored data. So if I force the spinner to a different selection to NOT play sound when the activity loads, then I would be displaying the wrong data for the user.
Yes you are right. So it is better to make a ListActivity. Read developer.android.com and the link i gave you before. You will be ok with this!
You're using "setOnItemSelectedListener", which sounds like when the app starts, its getting "selected" again.
Have you tried using "setOnItemClickListener" instead?
fitnessSpinner.setOnItemClickListener(new AdapterView.OnItemClickListener () {
public void onItemClicked() {}
};
Lakers16 said:
You're using "setOnItemSelectedListener", which sounds like when the app starts, its getting "selected" again.
Have you tried using "setOnItemClickListener" instead?
fitnessSpinner.setOnItemClickListener(new AdapterView.OnItemClickListener () {
public void onItemClicked() {}
};
Click to expand...
Click to collapse
onClickListener doesn't work for the spinner...I wish it did.
I REALLY need the drop down functionality of teh spinner, so I guess I'm going to try and figure out a way to have an invisible edittext that I set to the spinner selection and then use onClickListener or onChange...
Hi!
My self Raj, i have a problem when using the split action in my application.
i am doing an E-Menu for an restaurant. i list out all available category in major listview if the user click on the item of major list it should display all available minor items of its in behind of the Major listview.
I found one solution in Google it is possible by using split action.
Can anyone guide me / give some sample i may know about it..,
I am waiting for your guide.,
Have A Happy Day..,
Thanks to all..,
Simply add the following line to your <activity> or <application> tag in your AndroidManifest :
Code:
uiOptions="splitActionBarWhenNarrow"
EDIT : As a side note, it seems you want the split actionbar to be displayed only after the user clicked something, in that use case, the Contextual ActionBar seems more suited.
To use it, simply define your actionbar items in a menu.xml just like the normal actionbar, then in your java code use something like :
Code:
private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item1:
//do something
mode.finish();
return true;
case R.id.menu_item2:
// do something else
mode.finish();
return true;
case R.id.menu_item3:
// etc....
mode.finish();
return true;
default:
return false;
}
}
@Override
public void onDestroyActionMode(ActionMode mode) {
mActionMode = null;
}
And in the onClickListener() of the component that triggers the contextual ActionBar, call it like that :
Code:
if (mActionMode != null)
// Do nothing
else
mActionMode = startActionMode(mActionModeCallback);
You might want to use the ActionBarSherlock Library in order to keep compatibility with gingerbread/froyo
I am using this android:textIsSelectable and still gets the error:
Code:
TextView does not support text selection. Action mode cancelled.
Can any one please tell me what I might be doing wrong?
obscurant1st said:
I am using this android:textIsSelectable and still gets the error:
Code:
TextView does not support text selection. Action mode cancelled.
Can any one please tell me what I might be doing wrong?
Click to expand...
Click to collapse
Please post the code and also the AP version you are testing the app cant say anything without that
I believe testIsSelectable for TextViews was introduced in Honeycomb, if you want to be able to mimic that feature with lower versions of Android, here's a little workaround :
Use an EditText instead of your TextView and set android:textIsSelectable to false
In your activity's java code, override onLongClickListener and set the EditText to selectable in this method, something like this :
Code:
public class yourClass extends Activity implements onLongClickListener {
EditText yourFakeTextView;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.whatever);
yourFakeTextView = (EditText) findViewById(R.id.editText1);
yourFakeTextView.setOnLongClickListener(this);
}
@Override
public boolean onLongClick(View v) {
switch (v.getId()) {
case R.id.editText1:
yourFakeTextView.setTextIsSelectable(true);
break;
default:
return;
}
return false;
}
}
This will make the EditText look like a regular TextView, and when the user long-clicks it, it will allow him to select the text (just like if it was a normal, selectable TextView).
Hope this helps.
I want to enable this feature on ICS and upper version of android. I am testing this on an ics device. There is something which is messed up. I will post the code as soon as I reach home!
Basicly I have very same code as described here http://www.learn2crack.com/2013/12/android-swipe-view-tab-layout-example.html:
In each of the fragments I have different asynctask, that just fetches data from website. Fragment class looks like this :
Code:
TextView text;
String content;
View today;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
today = inflater.inflate(R.layout.today, container, false);
text = (TextView) today.findViewById(R.id.textView);
// ((TextView) today.findViewById(R.id.textView)).setText("Today");
new RetriveSiteData().execute("http://menza.lupajz.eu/?den=dnes");
content = (String) text.getText();
return today;
<here is the asynctask, too long >
}
I am wondering if the data is always fetched, while swiping through fragments if the TabPagerAdapter class (on the website) has this method implemented the same way
Code:
@Override
public Fragment getItem(int i) {
switch (i) {
case 0:
//Fragement for Android Tab
return new Android();
case 1:
//Fragment for Ios Tab
return new Ios();
case 2:
//Fragment for Windows Tab
return new Windows();
}
return null;
}
In case if the data is always fetched and the asynctask is always executed how can I prevent that ? Maybe adding some onResume() methods to each fragment ?
What is the best solution just to make connection to website, fetch data once and then just keep displaying them while I am swiping between fragments ?
lupajz said:
Basicly I have very same code as described here http://www.learn2crack.com/2013/12/android-swipe-view-tab-layout-example.html:
In each of the fragments I have different asynctask, that just fetches data from website. Fragment class looks like this :
Code:
TextView text;
String content;
View today;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
today = inflater.inflate(R.layout.today, container, false);
text = (TextView) today.findViewById(R.id.textView);
// ((TextView) today.findViewById(R.id.textView)).setText("Today");
new RetriveSiteData().execute("http://menza.lupajz.eu/?den=dnes");
content = (String) text.getText();
return today;
}
I am wondering if the data is always fetched, while swiping through fragments if the TabPagerAdapter class (on the website) has this method implemented the same way
Code:
@Override
public Fragment getItem(int i) {
switch (i) {
case 0:
//Fragement for Android Tab
return new Android();
case 1:
//Fragment for Ios Tab
return new Ios();
case 2:
//Fragment for Windows Tab
return new Windows();
}
return null;
}
In case if the data is always fetched and the asynctask is always executed how can I prevent that ? Maybe adding some onResume() methods to each fragment ?
What is the best solution just to make connection to website, fetch data once and then just keep displaying them while I am swiping between fragments ?
Click to expand...
Click to collapse
Yes, that data is always fetched since onCreateView is always called when a Fragment which hasn't been in layout appears, whether it was already instantiated or not. Have a look at the diagram on the right here for better understanding on how the specific methods are called. The dumb solution would be to put that code into the constructor, but Google suggests not doing anything there and instead using onCreate, which is also only called once.
Instead it might be better to just fire up the tasks for each Fragment (maybe sequentially as well) in the Activity, but that depends on how much data you are loading here, since the user might be switched to the page you are loading at the very end.
Thanks for answear yea I was looking at the fragment google site and I guessed that was the problem, but I am not getting the second part.
Instead it might be better to just fire up the tasks for each Fragment (maybe sequentially as well) in the Activity, but that depends on how much data you are loading here, since the user might be switched to the page you are loading at the very end.
Click to expand...
Click to collapse
I am just getting about 30+ lines of pure text, so it is not that big.
lupajz said:
Thanks for answear yea I was looking at the fragment google site and I guessed that was the problem, but I am not getting the second part.
I am just getting about 30+ lines of pure text, so it is not that big.
Click to expand...
Click to collapse
What I meant was that if you need to load much data over a possibly slow connection and do it in a specific order (meaning start with one category, then when that's finished the next one and so on) it might be that the user is swiping to the last category that will load and has to wait untill all other have been loaded. This is what speaks against loading everything in the activity and then reloading the Fragments with their specific content, but of course this is not the case with just a few lines of text . That would be the the way I would do it (load everything in the onCreate() of the activity and pass it to the Fragments) if there is little to medium data to load since you don't have to open an internet connection everytime the user swipes thus swiping will be much smoother (or it won't show a ProgressBar like that stupid xda-app ).
If it is data that you don't need to reload more than once every day, you can think about cashing it as well!
Hello, I am hoping someone can point out anything I might be doing wrong that is causing my issue. (seems hard to find "best practice" type of advise for newer android features)
My problem is that a ListView which is inside a fragment, controlled by a ViewPager does not get displayed on the screen if I swipe to the next fragment. (but a TextView inside the same fragment shows up fine on all fragment instances),
To set the scene, I have only one fragment class out of which I instantiate all the fragments used by the ViewPager.
Inside the fragments onActivityCreated I create a cursor loader and a custom cursor adapter and on the loaders onLoadFinished I swap the cursor as its supposed to be. Naturally the customCursorAdapter that I created handles the inflation of the listView row in the newView method and binds the data from the cursor in the bindView method.
Does this sound right? I mean I did it this way with the intent of not having tight coupling, having the fragment be independent. So why won't it draw out the listview for subsequent pages? From what I have seen in the logs, all methods get called for all fragments, including getItem, newView, bindView etc.
Final quirk is that the "previous" fragmet's listView does get drawn. Meaning I start on Fragment 1 - listView is there. Move to fragment 2 - listview not there, move to fragment 3 list view not there, move back to fragment 2 list view IS there now, and back to fragment 1 it is NOT there anymore.
I don't have time to post code right now but will as soon as I get a chance.
Thank you all.
Code:
//main activity which creates the view pager and adapter and starts the process
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG,"onCreate begin");
super.onCreate(savedInstanceState);
setContentView(R.layout.pager_task_tracking);
viewPager = (ViewPager) findViewById(R.id.pager);
FragmentManager fragmentManager = getSupportFragmentManager();
MyPagerAdapter myPagerAdapter = new MyPagerAdapter(fragmentManager);
viewPager.setOffscreenPageLimit(0); // trying to force drawing of fragments
viewPager.setAdapter(myPagerAdapter);
Log.d(TAG,"onCreate viewPager done");
//MyPagerAdapter getItem (extends FregmentSatePagerAdapter
public Fragment getItem(int i) {
Log.d(TAG,"getItem called with item:"+i);
return TaskListFragment.newInstance(i);
}
//fragment onActivityCreated , this fragment instanciated in getView of viewPager
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.d(TAG, "onActivityCreated with datePointer:"+datePointer);
TextView textViewFragmentTitle = (TextView) getView().findViewById(R.id.fragmentTitle);
textViewFragmentTitle.setText("DATE:"+datePointer);
loaderManager = getLoaderManager();
list = (ListView) getActivity().findViewById(R.id.TaskListView);
customAdapter = new CustomCursorAdapter(getActivity(), null, 1 , datePointer);
list.setAdapter(customAdapter);
loaderID = datePointer; //attempt to get fragment listview to draw - not working
loaderManager.initLoader(loaderID, null, this);
}
///cursor loader onFinish
public void onLoadFinished(android.support.v4.content.Loader<Cursor> loader,
Cursor newCursor) {
Log.d(TAG, "onLoadFinished:"+datePointer);
if (customAdapter != null && newCursor != null ){
Log.d(TAG, "onLoadFinished swapCursor:"+datePointer);
customAdapter.swapCursor(newCursor);
//doesnt help customAdapter.notifyDataSetChanged();
} else {
Log.v(TAG,"OnLoadFinished: mAdapter is null");
}
}
//MyCustomCursorAdapter (used by the listview inside the fragment above)
public View newView(Context context, Cursor cursor, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(context);
View retView = inflater.inflate(R.layout.task_list_item_layout, null);
RelativeLayout.LayoutParams relativeLayoutParameters;
Log.d(TAG,"newView layout inflated:"+datePointer);
return retView;
}
///bindView of same MyCustomCursorAdapter
public void bindView(View view, Context context, Cursor cursor) {
Log.d(TAG,"bindView:"+datePointer);
TextView tvName = (TextView) view.findViewById(R.id.textviewItemName);
tvName.setText(cursor.getString(cursor.getColumnIndex(DBHelper.C_TASK_NAME))+" ["+datePointer+"]");
}
Also check out the LogCat logs with description of the action in the name corresponding to the log statements in the code above.
I noticed more detail about what happens (if you look at the logcat you will see also)
When the screen is displayed the first fragment has the listview present, but the fragment has the id/datePointer (same thing) of "0" BUT the listview items have an id/datePointer of "1"
If you look at the initial logcat you will see "newView layout inflated:1" but no "newView layout inflated:0" how this is being skipped I have no idea. And how the id's can be mix-matched between fragment instances baffles me.
And lastly, after scrolling right twice (to the 0,1, 2nd fragment) it did not have the listview as reported, but scrolling BACK ONE the listview shows up in the 1st fragment ... but even more baffling with lisvtView items that report being part of the "0" fragment .... whaaaat?
Why are you using a listview inside fragment when you have listfragment available?
EatHeat said:
Why are you using a listview inside fragment when you have listfragment available?
Click to expand...
Click to collapse
EatHeat, I am doing this for the sake of flexibility for adding additional controls/views to the fragment besides just a list view (Example I envision adding several buttons to the bottom of the fragment/screen which will be "outside" the listview.
Does it make more sense what I am doing now? That is exactly one of the questions I have, its so hard to find advise on what makes more sense when it comes to these complex combinations of viewPager+fragments+custom listViews+loaders
So my current flow is:
1--Activity (creates ViewPager with myPagerAdapter)
2----ViewPager in itst getItem method instantiates a fragment from class TaskListFragment
3-------The fragment in it's onActivityCreated instantiates a CustomCursorAdapter (with null cursor) and initializes a loader
4-------The loader swaps the cursor in it's onLoadFinished (and this is supposed to populate the listview)
Items 2-4 repeat for every "swipe" or every fragment instantiated.
-The listView control XML is defined in an XML file which is used by the fragment's setContentView
-The lisView row layout XML is defined in a separate XML file inflated by the customCursorAdapter's newView method.
SOLVED: (after literally one week of headache and rewriting the code in every way I know)
the fragments onCreateView I had it getting the listview with getActivity() .. but I should have done it with getView()
the solution was replacing this line
Code:
list = (ListView) getActivity().findViewById(R.id.TaskListView);
with this line
Code:
list = (ListView) getView().findViewById(R.id.TaskListView);
That was it!!! all the headache!!
I don't particularly understand what getView does as opposed to getActivity .. but right now I am pleased it works.
Hi Can u please provide me the code .. I an working on same thing .. bt my getView Function is not called ..
Email : [email protected]
Make sure you custom pager adapter returns all pages as per count.
Use recyclerview in fragments
Sent from my Lenovo A6000 using Tapatalk
The offscreen page limit is by default 1. It is useless to set the limit lesser equals as 1.
Code:
viewPager.setOffscreenPageLimit(0);