I have a Fragment which contains viewpager.
inside ViewPager I have 3 more Fragments, Fragment-A,Fragment-B, and Fragment-C.
Inside Fragment A I have a Button, in Button onClick() I want to change the current item to Fragment C.
How can I achieve it? Please help
If button is not in main fragment, you'll have to define an interface to communicate between your main fragment and sub fragments i.e fragment A B C.
public interface GoToNextFragment {
void onGoToFragment(int index);
}
Your MainFragmet will have to implement this interface and the method that goes with it :
public class MainFragment extends Fragment implements GoToNextFragment {
//Your regular methods...
#Override
public void onGoToFragment(index) {
if (myAdapter != null) {
myAdapter.setCurrentItem(index);
}
}
Still in your MainFragment, set the interface on your fragments :
fragment1.setInterface(this);
fragment2.setInterface(this);
In your Fragments, add the setInterface method :
private GoToNextFragment mInterface;
public void setInterface(GoToNextFragment i) {
mInterface = i;
}
And finally in fragment a, trigger the effect with :
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mInterface != null) {
mInterface.onGoToFragment(2);
}
});
In your clickHandler add this method viewpager.setCurrentItem(int index)
viewpager.postDelayed(new Runnable() {
#Override
public void run() {
viewpager.setCurrentItem(2);
}
}, 100);
Here 2 is your Fragment C index.
Update
No need to create object of viewpager in Fragment A.
Just create one interface that extend in your Activity or Fragment which contain ViewPager and implement it into Fragment A.
Related
I'm pretty new to Android.
This is my scenario: I have a simple app with 3 tabs. In each tab i want to use one or more fragments. This is the situation:
Tab 1:
Fragment A
Tab 2:
Fragment B
Fragment C
Fragment D
Tab 3:
Fragment E
Fragment F
In "Tab 1" I have no issue. All works pretty good. Issues arise when I need to move in "Tab 2" and "Tab 3".
In Tab 2 I have to propagate some parameters from "Fragment B" to "Fragment C" and from "Fragment C" to "Fragment D".
Then it can happen that when user clicks on some button in "Fragment D" I have to pass to "Tab 3" and I have to propagate some parameters from "Fragment D" to "Fragment E".
In my main Activity for Tab handling I'm using these components:
android.support.design.widget.TabLayout
android.support.v4.view.ViewPager
android.support.v4.app.FragmentStatePagerAdapter (I created a custom
class)
android.support.design.widget.TabLayout.OnTabSelectedListener (I created a custom class)
My very simple FragmentStatePagerAdapter extension is:
public class MyOwnPageAdapter extends FragmentStatePagerAdapter {
private int numeroTab;
public MyOwnPageAdapter(FragmentManager fm, int numeroTab) {
super(fm);
this.numeroTab = numeroTab;
}
#Override
public Fragment getItem(int position) {
switch (position){
case 0:
return new FragmentA() ;
case 1:
return new FragmentB() ;
case 2:
return new FragmentC() ;
default:
return null;
}
}
#Override
public int getCount() {
return numeroTab;
}
}
My very simple TabLayout.OnTabSelectedListener extension is:
public class TabSelectedListener implements TabLayout.OnTabSelectedListener {
private ViewPager viewPager;
public TabSelectedListener(ViewPager viewPager){
this.viewPager = viewPager;
}
#Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
}
I'm able in switching fragments inside tabs that is in Tab 2 i can switch from Fragment B to Fragment C and so on. I'm having issues in passing parameters between fragments and above all from Fragment D in Tab 2 to Fragment E in Tab 3
In my Fragments implementation byt using the android.support.v4.app.FragmentManager I can add and remove views (e.g. fragments) by doing something like this:
mFragmentManager.beginTransaction().add(rootView.getId(),mListaEdificiFragment, "BUILDS").addToBackStack(null).commit();
The problem is the param propagation that since the FragmentStatePagerAdapter seems to cache views it happens that the fragment constructor is called but the onCreate and onCreateView are no more called so I can't handle the propagated parameters.
Is there any solution to this? Or am I simply wrong in my navigation pattern? I would like to avoid to collapse Fragment B,Fragment C and Fragment D in one "big view" where to hide some section (the same for Fragment E e Fragment F)
Any suggestion is more then welcome
Angelo
One simple solution to transfer a variable value from one fragment to another is shared preferences (can also be used to transfer values from one activity to another too). Shared preference will save data against variables that will persist across all the activities and fragments in an android app.
Now in your case, lets assume you want to transfer a value name = angelo from your fragment A to fragment B. In your fragment A, write this code:
Button updateName = findViewById(R.id.btnupdateTeamName);
updateTeamName .setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString("name", "angelo");
editor.commit();
}
});
When executed, the above code will update a value name with angelo in shared preferences. This will be available throughout your app.
For more info about shared preference, check out this official document.
I write my Fragment like this for passing data to it.
public class MyFragment extends Fragment {
private static String ARG_PARAM1 = "data";
private String data;
public MyFragment() {
// Required empty public constructor
}
public static MyFragment newInstance(String data) {
MyFragment fragment = new MyFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, data);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
data = getArguments().getString(ARG_PARAM1);
}
}
}
Now data can be passed to the Fragment by calling MyFragment.newInstance("Hello"). I hope this helps.
I have faced a similar issue in my project.
In my case, I have viewpager and each tab has multiple fragment.
So one of the simple solutions is to use LiveData and ViewModel.
In your Tab2:
Fragment B
Fragment C
Fragment D
TabTwoViewModel (with live data)
In mutable Live data observer this live data to Fragment B, C, and D.
When you update live data object, Live data notify automatically all fragment.
Finally I got a solution.
Since the main problem is the fragments communication, I followed the official documentation
Let's suppose I have Fragment A with list of articles and Fragment B where to see the selected article detail, in my Fragment A i wrote:
public class FragmentA extends Fragment {
private OnArticleSelectionListener mCallback;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
}
}
#Override
public void onStart() {
super.onStart();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.activityedifici, container, false);
return rootView;
}
public interface OnArticleSelectionListener {
void onArticleSelection(String articleId);
}
#Override
public void onDetach() {
super.onDetach();
mCallback = null;
}
public void setOnArticleSelectionListener(OnArticleSelectionListener mCallback) {
this.mCallback = mCallback;
}
}
As you can see I declared the following interface
public interface OnArticleSelectionListener {
void onArticleSelection(String articleId);
}
This is the article selection listener.
In my Main Activity I wrote the following:
public class MainActivity implements FragmentA.OnArticleSelectionListener{
//All my own stuffs
#Override
public void onAttachFragment(Fragment fragment) {
if (fragment instanceof FragmentA){
FragmentA ef = (FragmentA)fragment;
ef.setOnArticleSelectionListener(this);
}
}
#Override
public void onArticleSelection(String articleId) {
if( getSupportFragmentManager().findFragmentByTag(TAG_ARTICLE_DETAIL) != null ){
//FragmentB is the article detail and it has already been created and cached
FragmentB dcf = (FragmentB)getSupportFragmentManager().findFragmentByTag(TAG_ARTICLE_DETAIL);
dcf.updateArticleDetail( articleId );
}else{
//FragmentB is the article detail and it has never been created I create and replace the container with this new fragment
FragmentB dcf = new FragmentB();
//Parameter propagation
Bundle args = new Bundle();
args.putString(FragmentB.ARG_ARTICLE_ID, articleId);
dcf.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.container_articles, dcf, TAG_ARTICLE_DETAIL);
transaction.addToBackStack(null);
transaction.commit();
}
}
}
In this way I'm able in intercepting events in FragmentA and propagate them to the FragmentB; when I need to open a Tab all remains the same and finally (after transaction.commit() or the dcf.updateArticleDetail( articleId )) I do the following tabLayout.getTabAt(2).select(); and the third tab (tab index starts from 0) is open and the Detail is showed.
I hope this can be useful
Angelo
I am using Tabbed Activity of Android Studio.
I'm moving between the pages swiping but i added 2 views to move next and previous by onclick method but it doesn't work to go back only to go next.
nextAbitudini.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//it works
mViewPager.setCurrentItem(getArguments().getInt(ARG_SECTION_NUMBER));
}
});
backAbitudini.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//it doesn't work
mViewPager.setCurrentItem(getArguments().getInt(ARG_SECTION_NUMBER)-1);
}
});
I'm using FragmentPagerAdapter.
It works swiping back.
I put onClick methods inside onCreateview.
Refering to the Tabbed Activity, take a look at the section number as the fragment is initialized. (sectionNumber = position+1).
#Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a PlaceholderFragment (defined as a static inner class below).
return PlaceholderFragment.newInstance(position + 1);
}
Therefore, ARG_SECTION_NUMBER-1 refers to the position of the current fragment, ARG_SECTION_NUMBER-2 to the previous, and ARG_SECTION_NUMBER to the next.
Consequently, your code should be like this:
nextAbitudini.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//it works
mViewPager.setCurrentItem(getArguments().getInt(ARG_SECTION_NUMBER));
}
});
backAbitudini.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//it doesn't work
mViewPager.setCurrentItem(getArguments().getInt(ARG_SECTION_NUMBER)-2);
}
});
I have a Fragment FR1 that contains several Nested Fragments; FRa, FRb, FRc. These Nested Fragments are changed by pressing Buttons on FR1's layout. Each of the Nested Fragments have several input fields within them; which include things like EditTexts, NumberPickers, and Spinners. When my user goes through and fills in all the values for the Nested Fragments, FR1 (the parent fragment) has a submit button.
How can I then, retrieve my values from my Nested Fragments and bring them into FR1.
All Views are declared and programmatically handled within each Nested Fragment.
The parent Fragment, FR1 handles the transaction of the Nested Fragments.
I hope this question is clear enough and I am not sure if code is necessary to post but if someone feels otherwise I can do so.
EDIT 1:
Here is how I add my Nested Fragments:
tempRangeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getChildFragmentManager().beginTransaction()
.add(R.id.settings_fragment_tertiary_nest, tempFrag)
.commit();
}
});
scheduleButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getChildFragmentManager().beginTransaction()
.add(R.id.settings_fragment_tertiary_nest, scheduleFrag)
.commit();
}
});
alertsButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getChildFragmentManager().beginTransaction()
.add(R.id.settings_fragment_tertiary_nest, alertsFrag)
.commit();
}
});
submitProfile.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
constructNewProfile();
}
});
where my constructNewProfile() method needs the values from my Nested Fragments.
public Fragment tempFrag = fragment_profile_settings_temperature
.newInstance();
public Fragment scheduleFrag= fragment_profile_settings_schedules
.newInstance();
public Fragment alertsFrag = fragment_profile_settings_alerts
.newInstance();
The above refers to the fields of the parent fragment; and how they are initially instantiated.
The best way is use an interface:
Declare an interface in the nest fragment
// Container Activity or Fragment must implement this interface
public interface OnPlayerSelectionSetListener
{
public void onPlayerSelectionSet(List<Player> players_ist);
}
Attach the interface to parent fragment
// In the child fragment.
public void onAttachToParentFragment(Fragment fragment)
{
try
{
mOnPlayerSelectionSetListener = (OnPlayerSelectionSetListener)fragment;
}
catch (ClassCastException e)
{
throw new ClassCastException(
fragment.toString() + " must implement OnPlayerSelectionSetListener");
}
}
#Override
public void onCreate(Bundle savedInstanceState)
{
Log.i(TAG, "onCreate");
super.onCreate(savedInstanceState);
onAttachToParentFragment(getParentFragment());
// ...
}
Call the listener on button click.
// In the child fragment.
#Override
public void onClick(View v)
{
switch (v.getId())
{
case R.id.tv_submit:
if (mOnPlayerSelectionSetListener != null)
{
mOnPlayerSelectionSetListener.onPlayerSelectionSet(selectedPlayers);
}
break;
}
}
Have your parent fragment implement the interface.
public class Fragment_Parent extends Fragment implements Nested_Fragment.OnPlayerSelectionSetListener
{
// ...
#Override
public void onPlayerSelectionSet(final List<Player> players_list)
{
FragmentManager fragmentManager = getChildFragmentManager();
SomeOtherNestFrag someOtherNestFrag = (SomeOtherNestFrag)fragmentManager.findFragmentByTag("Some fragment tag");
//Tag of your fragment which you should use when you add
if(someOtherNestFrag != null)
{
// your some other frag need to provide some data back based on views.
SomeData somedata = someOtherNestFrag.getSomeData();
// it can be a string, or int, or some custom java object.
}
}
}
Add Tag when you do fragment transaction so you can look it up afterward to call its method. FragmentTransaction
This is the proper way to handle communication between fragment and nest fragment, it's almost the same for activity and fragment.
http://developer.android.com/guide/components/fragments.html#EventCallbacks
There is actually another official way, it's using activity result, but this one is good enough and common.
Instead of using interface, you can call the child fragment through below:
( (YourFragmentName) getParentFragment() ).yourMethodName();
The best way to pass data between fragments is using Interface. Here's what you need to do:
In you nested fragment:
public interface OnDataPass {
public void OnDataPass(int i);
}
OnDataPass dataPasser;
#Override
public void onAttach(Activity a) {
super.onAttach(a);
dataPasser = (OnDataPass) a;
}
public void passData(int i) {
dataPasser.OnDataPass(i);
}
In your parent fragment:
public class Fragment_Parent extends Fragment implements OnDataPass {
...
#Override
public void OnDataPass(int i) {
this.input = i;
}
btnOk.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Fragment fragment = getSupportFragmentManager().findFragmentByTag("0");
((Fragment_Fr1) fragment).passData();
}
}
}
You can use share data between fragments.
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, item -> {
// Update the UI.
});
}
}
More Info ViewModel Architecture
You can use getChildFragmentManager() and find nested fragments, get them and run some methods to retrieve input values
Check for instanceOf before getting parent fragment which is better:
if (getParentFragment() instanceof ParentFragmentName) {
getParentFragment().Your_parent_fragment_method();
}
Passing data between fragments can be done with FragmentManager. Starting with Fragment 1.3.0-alpha04, we can use setFragmentResultListener() and setFragmentResult() API to share data between fragments.
Official Documentation
Too late to ans bt i can suggest create EditText object in child fragment
EditText tx;
in Oncreateview Initialize it. then create another class for bridge like
public class bridge{
public static EditText text = null;
}
Now in parent fragment get its refrence.
EditText childedtx = bridge.text;
now on click method get value
onclick(view v){
childedtx.getText().tostring();
}
Tested in my project and its work like charm.
In my app, I have an activity that has two fragments in actionbar tabs navigation mode, just like the android developer site example.
in my first fragment I have a listview (which has it's own adapter ) and each item of the listview has a button called +1. I want to refresh the second fragment that shows the items in listview in first fragment that their +1 button's clicked.
I know i have to use interfaces. but I cant figure how to use them. where do I have to define the interface? how to use it? and how to access it from the activity to refresh the second fragment?
a quick help would be great. thanks.
If you want it on List Item Click
Fragment A:
public class FragmentA extends ListFragment {
OnItemSelectedListener mListener;
...
// Container Activity must implement this interface
public interface OnItemSelectedListener {
public void onItemSelected(int position);
}
...
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnItemSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnItemSelectedListener");
}
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
mCallback.onItemSelected(position);
}
}
ContainerActivity:
public class ContainerActivity extends FragmentActivity
implements FragmentA.OnItemSelectedListener
{
//...
public void onItemSelected(int Position/*pass anything which u want*/)
{
SecondFragment second_fragment = (SecondFragment) getSupportFragmentManager().findFragmentById(R.id.fragmentB);
if(second_fragment !=null)
{
second_fragment.UpdateUI(Position);
}
}
}
Second Fragment:
public class SecondFragment extends Fragment {
...
public void UpdateUI(Position)
{
}
}
Hope this helps. On click of a Button inside each listitem might be bit difficult, but try the same approach. May be you have to write the interface declaration and call in your custom adapter.
I am using sliding menu/drawer pattern in my app. So the main activity has a leftView which is a ListFragment named topicsFragment() which loads set of topic items. When an item/topic is clicked it replaces the fragment on main view by calling the FeedsFragment(tag). FeedsFragment uses arraylist adapter to load the feeds which has various clickable items in each list item. I want to fetch another instance on the feedsFragment(tag) when an item is clicked within a list item.
holder.contextView= (TextView) newsView.findViewById(R.id.arcHeader);
if (item.hasArc()) {
holder.contextView.setVisibility(View.VISIBLE);
String arc;
try {
arc=item.getarc();
holder.contextView.setText(arc);
holder.contextView.setOnClickListener(new View.OnClickListener() {
//currently it loads a class
#Override
public void onClick(View v) {
Intent i = new Intent(context, SomeClass.class);
i.putExtra("tag", arc);
context.startActivity(i);
}
});
} catch (JSONException e) {
e.printStackTrace();
}
} else {
holder.contextView.setVisibility(View.GONE);
}
Currently it loads a new class. I want to define a fragment and then pass to main activity to replace with the current view but I cant use getSupportFragmentManager() inside an adapter class but only in a fragment or fragment activity. What should be an alternative to sweeping in a fragment from an adapter?
What I did was create this method in my main activity and just called it from other classes to change the fragment:
public void switchContent(Fragment fragment) {
mContent = fragment;
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, fragment).commit();
slidemenu.showContent();
}
Solved it by using the context passed in the list adapter:
#Override
public void onClick(View v) {
Fragment newFragment = new ListFragmentClass(tag);
if (newFragment != null)
switchFragment(newFragment);
}
private void switchFragment(Fragment newFragment) {
if (context == null)
return;
if (context instanceof MainActivity) {
MainActivity feeds = (MainActivity) context;
feeds.switchContent(newFragment);
}
}
Here switchContent is method defined in your main activity for switching/replacing fragment as given in answer by Justin V.
Pass getFragmentManager() as a parameter in constructor of your adapter and use that.
Use an Interface to connect your side drawer ListFragment to the main activity. For example:
public class LeftDrawer extends ListFragment{
private DrawerCallback mCallback;
public interface DrawerCallback{
public void onListClick(String tag);
}
public void setCallback(DrawerCallback callback){
mCallback = callback;
}
}
As Fragments should have an empty constructor, use a public method within your Fragment to set the callback before completing the FragmentTransaction adding it to your drawer. At this point all that is left is notifying your Fragment that a click has occurred. What you should do is actually catch the click in your ListFragment directly rather than adding on onClickListener to every view in your adapter.
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
/*
* Get item at the clicked position from your adapter and
* get its string tag before triggering interface
*/
mCallback.onListClick(tag);
}
Use the onListItemClick method to do this. You will get the list position that was clicked and can easily then get that item from your adapter and get its tag value to pass back to your host activity.