I have a pager that contains three fragments
adapter.addFragment (new PlainColorFragment (Color.red));
adapter.addFragment (new PlainColorFragment (Color.green));
adapter.addFragment (new PlainColorFragment (Color.blue));
My question is whether it is possible to detect that fragmentation has focus or is being displayed to the user.
For example, when the green fragment is the one on screen or has focus, show a "toast" on the screen
I hope I have explained my question correctly.
thanks
Simple:
greenFragment.isVisible();
If you're looking for some kind of event, you would have to manage that manually wherever your fragment switching happens, or in your fragment class, you could execute your code in the fragment's OnHiddenChanged event (double checking, of course, that it is currently visible)
You could set an OnPageChangedListener to your ViewPager and show a different toast depending on the position.
You can create an Interface, implementing it in your Fragment and then, on parent activity, you can implement BackStackChangedListener as in example below:
public interface MyFragmentOnScreen {
public void onActiveFragment();
}
public class MyFragment extends Fragment implements MyFragmentOnScreen{
[...]
#Override
public void onActiveFragment() {
//Things you should do when your fragment becomes active
}
}
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
[...]
getSupportFragmentManager().addOnBackStackChangedListener(
new FragmentManager.OnBackStackChangedListener() {
public void onBackStackChanged() {
// Update your UI here.
Log.v(MainActivity.TAG, "Backstack changed");
if (getSupportFragmentManager().findFragmentById(R.id.frMain) instanceof MyFragmentOnScreen) {
((MyFragmentOnScreen) getSupportFragmentManager().findFragmentById(R.id.frMain)).onActiveFragment();
}
}
}
});
}
}
where frMain is the holder in MainActivity layout for your Fragment.
Related
Short Story:
How to detect a ViewPager page scrolling/changing before fragment lifecycle execution for the new page?
Long Story:
I have a ViewPager with assigned fragments to be displayed using ViewPagerAdapter, one of these fragments is meant to be displayed with different data according to current page selected in the pager.
for example, if current page selected is 2 it would display A data, and if the current page selected is 4 it would display B data.
the straight forward solution is to set the data according to the current page using OnPageChangeListener or SimpleOnPageChangeListener, but both are not applicable as the fragment WHOLE life cycle is being called before any of these listeners methods being called, so the data would be set after fragment creation here.
the second straight forward solution is to make the changes after receiving the call from the listeners which is so bad regarding user experience and design wise.
So would be the best way to set fragment credentials when changing the current page of the ViewPager before onResume() method of the fragment to be executed?
What I'm doing:
in MyFragment.java:
// it goes here first
#Override
public void onResume() {
super.onResume();
// check the Data Applied
if(dataA)
doSomething();
else
doSomethingElse();
}
in MainActivity.java:
pager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
// then it goes here
// setting the Data of the fragment
if (position == 2)
setDataA();
else (position == 4)
setDataB();
}
});
Why don't you use callback Interface? If you set interface, you can even get the call back on the fragment onAttach() or where you want.
Example Implementation:
Activity:
public class MyActivity extends AppCompatActivity implements FragmentListener {
#Override
public void onFragmentSelected(String value) {
// set data here.
}
public interface FragmentListener {
void onFragmentSelected(String value);
}
}
In your viewPager Fragments:
public class MyFragment extends Fragment{
#Override
public void onAttach(Context context) {
super.onAttach(context);
if(context instanceof MyActivity){
((MyActivity)context).onFragmentSelected("Your_Identification");
}
}
}
Do this in all your viewPager fragments so you will get which fragment attached from the frgment onAttach() itself. Or choose when it should be called.
Hope it Helps:)
I have two static fragments in same activity, in "fragmentA" i have a customized list, when an item is clicked must to appear a detail in "fragmentB", detail appear only when i change screen orientation, no automatically. I use this code in main activity for refresh but application restart(detail appear).
finish();
startActivity(getIntent());
Someone knows a better way to make appear detail automatically in "fragmentB" when i clicked some item from "fragmentA", always using two static fragments in same activity.
Don't use static references to hold a Fragment, it's a really bad practice.
Don't store the Context in a static reference. Or you could will leak memory.
Instead, implement an Interface:
//FragmentActivityTest
public class FragmentActivityTest extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentB fragmentB = new FragmentB();
FragmentA fragmentA = new FragmentA();
fragmentA.setFragmentBHandler(fragmentB);
//Perform transactions etc
}
}
//FragmentA
public class FragmentA extends Fragment {
private FragmentBHandler _handler;
public void setFragmentBHandler(FragmentBHandler handler) {
_handler = handler;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
((ListView) getView().findViewById(R.id.list_view)).setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
_handler.updateDetail();
}
});
}
}
//FragmentB
public class FragmentB extends Fragment implements FragmentBHandler {
#Override
public void updateDetail() {
//do your work
}
}
You should use an event bus like greenrobot or otto. FragmentB subscribe to an event, and FragmentA post that event. When you click on an item, you'll send an event, and the subscriber will execute your action (show details).
Without showing code, I can only guess your current implementation.
The proper way to communicate between fragments is
Pass data to the parent activity from Fragment A on item click,
Activity passes this data to fragment B by finding the fragment in fragment manager and call a method in fragment B,
That method in fragment B should determine if it should populate the detail.
How can I communicate a listview of a ListFragment after an event of a Fragment inside another Fragment?
In the ListFragment (fragment A) I have a method to refresh the ListView. But also, I need to refresh it after a click of a Button inside a Fragment, wich is child of another Fragment (fragment b)
Its like Fragment A (listFragment) | Fragment B (detailview)
(fragment C - child fragment of B)
How can I do it?
You can access another Fragment by its tag:
// find your fragment
YourFragment f = (YourFragment) getSupportFragmentManager().findFragmentByTag("yourFragTag");
// update the list view
f.updateListView();
The tag of your Fragment is set when it is attached to a container layout:
FragmentManager fm = getSupportFragmentManager();
fm.beginTransaction().replace(R.id.frameBuy, YourFragment.newInstance(), "yourFragTag").commit();
So when you click your Button, find the Fragment you want to refresh by its tag, and then call your refresh method.
IF you are using a ViewPager, this is how to get the Fragments tag:
/**
* Gets the fragment tag of a fragment at a specific position in the viewpager.
*
* #param pos the pos
* #return the fragment tag
*/
public String getFragmentTag(int pos){
return "android:switcher:"+R.id.yourViewPagerId+":"+pos;
}
You can do it with a few simple steps:
Create a listener interface for component to listen to the button click event from FragmentC. For example:
public interface FragmentCButtonListener {
public void onButtonClicked();
}
Add a method in FragmentC to allow listener registration. FragmentC will keep track of the listener, and call the listener callback as soon as the button is clicked. For example:
public class FragmentC extends Fragment {
FragmentCButtonListener myListener;
public void registerListener (FragmentCButtonListener listener) {
myListener = listener;
}
}
FragmentA should implement FragmentCButtonListener interface, register itself as a listener to FragmentC, and refresh the list view when it receives the callback from FragmentC. For example:
public class FragmentC extends Fragment implements FragementCButtonListener {
FragmentC fragmentC;
public void onCreate() {
fragment = new FragmentC();
fragment.registerListener (this);
}
public void onButtonClicked() {
//refresh the list view
}
}
Please note, I assume the FragmentA has a reference to FragmentC in the sample. If not, just make sure the container class of all fragments registers itself as the listener of FragmentC. The container class can as FragmentA to update listview once it receives callback from FragmentC.
follow these steps
We have two fragments called AddFragmentand ListFragment, and upon adding an item on first fragment you want the updated list be shown on list fragment (what sort of sorcery is this!!!).
Step 1 create the listener interface on class level of AddFragment with a method that is going to be implemented by the other guy (ListFragment ) and create Interface type variable
public class AddFragment extends Fragment{
//listener varriable
//listener
public interface OnCategoryAddedListener{
public void refreshList();
}
private static OnCategoryAddedListener meAddListener;
}
Step 2 create register method on class level of the same AddFragment class and set listenter variable
public class AddFragment extends Fragment{
public void registerListener(OnCategoryAddedListener listener)
{
meAddListener = listener;
}
}
Step 3 upon any event cud be button click or yelling at ur application(that is considered rude event in android :-) ) check for listener object meAddListener variable and call the interface,
in a Shakespeare’s nutshell it means “for thou who implement ye interface and brought the method within ur class shelter, I shall give u my utmost privilege and blessing to …. ”
Step 4 On ListFragment implement the AddFragment’s interface,no turning back just go implement its method. Within that method u just call abracadabra to repopulate ur list or any sort of updatable android view object… and ur done
public class ListFragment extends Fragment implements AddFragment.OnCattegoryAddedListener{
//refer to AddFragment
AddFragment addFragment;
//once the fragment is within the memory scope u instantiate AddFragment
//and register listener with AddFragment context which inherently implement OnCategoryAddedListener
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
categoryAddFragment = new CategoryAddFragment();
categoryAddFragment.registerListener(this);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
fillList();
}
public void fillList() {
ArrayAdapter<String> catArrayAdapter = new ArrayAdapter<>(context,android.R.layout.simple_list_item_1,getItems());
setListAdapter(catArrayAdapter);
}
//le interface method u can count on to refresh ur list content
public void refreshList(){
fillList();
}
check this out for a little more
enjoy the java magic
I have two fragments sitting side by side in the same activity. When I touch a button in the right fragment (fragment B), I need a TextView in the left fragment to update (fragment A). I have looked all over for the best way to do this, but nothing seems to work for my needs. Could someone possibly give me an example of how I would code this? Fragment A is set through the XML layout, and fragment B gets loaded programmatically into a container. I have tried accomplishing this by using a method in fragment A to update the text, and calling on that method from a method in the parent activity. I then call on the method in the parent activity from fragment B.
This is the code in fragment B that declares the interface and calls a method in the interface
AttackCards attackCards;
public interface AttackCards {
public void deckSize();
}
public void onAttach(DeckBuilder deckBuilder) {
super.onAttach(deckBuilder);
attackCards = (AttackCards) deckBuilder;
}
attackCards.deckSize(); //this is in my onclick methods
This is the code in the activity that implements the interface and calls the method in fragment A
public class DeckBuilder extends Activity implements AttackCards{
public void deckSize() {
DeckBuilderFragment deckBuilderFragment = (DeckBuilderFragment)getFragmentManager().
findFragmentById(R.id.deckbuilder_fragment);
deckBuilderFragment.deckSize();
}
This is the method in fragment A that appends the textview with the contents of a shared preferences value
public void deckSize() {
deckSize = (TextView) getView().findViewById(R.id.decksize);
final SharedPreferences defaultDeck = getActivity()
.getSharedPreferences("defaultDeck", Context.MODE_PRIVATE);
deckSize.setText(String.valueOf(defaultDeck.getInt("decksize", 0)));
}
Sadly this attempt simply brings me a nullpointer when touching a button. I am getting a null pointer at
attackCards.deckSize(); //this is in my onclick methods
Could someone please help me out with an example of how to do this correctly?
One fragment should not communicate to another fragment directly. It should do so through attached activity. The detail explanation with code example is available here
Android Developer site
Declare an interface in Fragment B, and implement the interface in the activity. Call the interface through callback in Fragment B when button is clicked. You can have a public function in Fragment A to update the TextView, so activity directly call the function to update the text.
You can define an interface in Fragment B and implement it on the MainActivity. Then on the callback method (onClickOnB in this case) set the text on the TextView. You should obtain a reference of the TextView in the Activity's onCreate() after setContentView(). This works because Fragment A is static. Otherwise, you can create a public method inside Fragment A so you can set the text from inside the callback by getting a reference of Fragment A and calling such method.
Fragment B
public class FragmentB extends Fragment implements onClickListener{
ClickOnB listener;
public void setOnFragmentBClickListener(ClickOnB listener){
this.listener = listener;
}
#Override
public void onClick(View v){
//stringMessage is a `String` you will pass to `Fragment` A to update its `TextView`
listener.onClickOnB(stringMessage);
}
interface ClickOnB{
public void onClickOnB(String message);
}
}
MainActivity
public class MainActivity extends Activity implements ClickOnB{
#Override
protected onCreate(Bundle savedInstanceState){
//Get a reference of `Fragment` B somewhere in your code after you added it dynamically and set the listener.
((FragmentB)getFragmentManager().findFragmentByTag("FragmentB")).setOnFragmentBClickListener(this);
}
#Override
public void onClickOnB(String message){
//Set the text to the `TextView` here (I am assuming you get a reference of the `TextView` in onCreate() after inflating your layout.
mTextView.setText(message);
}
}
As viewpager preload +1 and -1 of the fragment that is used in it. I've a loading of data using asyntask that i wish to execute only when i am on the page itself.
Where should i execute the function? oncreate does not seems to work for me
You can implement ViewPager.OnPageChangeListener and run your AsyncTask in onPageSelected().
For example:
public class MyActivity implements ViewPager.OnPageChangeListener {
#Override
public void onPageSelected(int position) {
new MyAsyncTask().execute();
}
}
However, as Tyczj pointed out in the comments, this defeats the purpose of a ViewPager trying to keep Views loaded. This feature is designed to make your app look smooth, and without it your Views will look empty (or take on their default appearance) while you load your data.
The solution provided by Tanis should work, there's however one think that should be taken in consideration. Since the AsyncTask is started from Activity, you may encounter some issues when dealing with configuration changes. Perhaps starting the AsyncTask dirrectly from the fragment will make more sense.
The solution then would be to make the currently displayed fragment aware that he is the fragment displayed now.
Firstly, you should have a method in your Activity that will return the position of current fragment from ViewPager:
public class MainActivity extends Activity{
//....
public int getViewPagerCurrentIndex() {
return pager.getCurrentItem();
}
}
Secondly, in your PagerAdapter in getItem() method pass the position of current item as an argument to the fragment:
public class MyPagerAdapter extends FragmentStatePagerAdapter {
//....
#Override
public Fragment getItem(int position) {
return MyFragment.newInstance(position);
}
}
Lastly, check in MyFragment that both position returned from MainActivity and position received when the fragment was instantiated match. If they match then this instance of fragment is visible:
public static MyFragment newInstance(int position) {
MyFragment fragment = new MyFragment ();
Bundle args = new Bundle();
args.putInt(KEY_POSITION, position);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
if (getArguments() != null) {
fragmentPosition = getArguments().getInt(KEY_POSITION);
}
}
// It does not matter where this method is called, the AsyncTask will be started only for the currently visible fragment.
private void executeAsyncTask(){
MainActivity mainActivity = (MainActivity)getActivity();
if (mainActivity.getViewPagerCurrentIndex() == fragmentPosition ) {
new MyAsyncTask().execute();
}
}