Communicating between fragments within an activity - android

I am trying to write a test app which contains an activity. There are two fragments inside this activity, which are defined as LeftFragment and RightFragment. I used getFragmentManager().findFragmentById() to get connection from each other fragments. By using that methode I am able to get an LeftFragment object from RightFragment, but not RightFragment object from LeftFragment. It just works only oneway. I am doing this, because I want to call some operations from other fragment, that return some values. I was thinking about using EventBus but I failed too. How can I achive that?
Here is my LeftFragment
public class LeftFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
RightFragment rightFragment = (RightFragment) getFragmentManager().findFragmentById(R.id.rightFragment);
if (rightFragment != null){
makeToast(rightFragment.getMessageFromRight());
}else {
makeToast("does not found rightFragment");
}
return inflater.inflate(R.layout.fragment_left, container, false);
}
public String getMessageFromLeft(){
return "Hi! Im left";
}
private void makeToast(String text){
Toast.makeText(getContext(),text,Toast.LENGTH_SHORT).show();
}
}
And here is my RightFragment
public class RightFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
LeftFragment leftFragment = (LeftFragment) getFragmentManager().findFragmentById(R.id.leftFragment);
if (leftFragment != null){
makeToast(leftFragment.getMessageFromLeft());
}else {
makeToast("does not found leftFragment");
}
return inflater.inflate(R.layout.fragment_right, container, false);
}
public String getMessageFromRight(){
return "Hi! Im right!";
}
private void makeToast(String text){
Toast.makeText(getContext(),text,Toast.LENGTH_SHORT).show();
}
}

There are many ways to communicate between 2 fragments . If 2 fragments loaded at the same time. I usaually use one of 2 ways below to do it.
You can use this link using obserable pattern to communication 2 fragments.
you can use EventBus lib for communication, it 's very simple
Your issue:
By using that methode I am able to get an LeftFragment object from
RightFragment, but not RightFragment object from LeftFragment
I think your problem is LeftFragment is intitialized previous, so you can find it from RightFragment. Your solution is ok, using EventBus. YOu need to review your codes to find the issue. You can test by creating other methods, after 2 fragment was initialized.
For ex: click button in LeftFragment, toast a message in RightFragment.

Probably what is happening is that the Left Fragment is getting the OnCreateView() call first, at which point the Right Fragment has not been inflated yet (therefore it can't be "found" by findFragmentbyId()).
I would suggest moving the code that gets the references to the other fragments into onStart(), and only inflate the fragments in onCreateView().

Related

how to access fragments elements in MainActivity()?

In my project, I want to set visibility of fragments buttons from MainActivity. But the problem is, it gives NullPointerException(). I also maked listBtn & gridBtn as static. I used below code :
FirstFragment fragment = (FirstFragment)getSupportFragmentManager().findFragmentById(R.id. <frameLayout Id>);
main_page_fragment.listBtn.setVisibility(View.GONE);
main_page_fragment.gridBtn.setVisibility(View.GONE);
You cannot access to your fragment view from Activity class because activity uses its own view (ex: R.layout.activity_main). Rather you can set visibility in your corresponding fragment class which will do the same job.
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.details, container, false);
Button listBtn = (Button)view.findviewById(R.id.listBrn);
Button gridBtn = (Button)view.findviewById(R.id.gridBrn);
listBtn.setVisibility(View.GONE);
gridBtn.setVisibility(View.GONE);
return view;
}
Fragment onCreateView callback is called after onCreate method of activity, so i think you have tried to get access from it. That views will be accessible only after onResumeFragments callback is called, you should perform your actions with fragments there.
Another tip is that you strongly should not call views of fragments directly like you did or via static reference to views that's the worst. You should avoid such dependencies on fragments inner implementation. Instead of it, better is create some method like setInitialState (the name depends on your business logic) and just call it from activity.
So result code:
In activity:
private FirstFragment fragment;
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//init fragment here
}
#Override
protected void onResumeFragments() {
super.onResumeFragments();
fragment.setInitialState();
}
In fragment:
//this will be called on fragment #onResume step, so views will be ready here.
public void setInitialState() {
listBtn.setVisibility(View.GONE);
gridBtn.setVisibility(View.GONE);
}
If you add your fragments dynamically from MainActivity like so:
YourFragment fragment = new YourFragment();
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.fragmentContainer, fragment, YOUR_TAG)
.commit();
Then you can define method in your fragment like so:
public void hideButtons()
{
yourBtn.setVisibility(View.GONE);
}
And call it from activity:
fragment.hideButtons();
I struggle with this for several hours and I found a much simpler solution.
Inside the fragment, simply make a public function (outside the on create view method) with the behavior that you want.
fun hideElement() {
binding.button.visibility = View.GONE
}
And then in main activity access to the fragment and call the function.
binding.bottomNavigation.setOnNavigationItemSelectedListener {
when (it.itemId){
R.id.someFragment -> someFragment.hideElement()
}
}

Android retaining information across fragments

I have five fragments a user can switch between. One of these fragments loads a list of users from the server to populate the UI list on the fragment. I need the list information to persist if a user swipes to a different fragment and then swipes back to the original. I do not want the fragment to reload the users every time a user leaves the fragment and goes back.
I am looking at setRetainInsance(true) and was wondering if this is possible solution? What would be the best way for the fragment to retain the information without being created from scratch each time.
I am using this to switch between fragements -getSupportFragmentManager().beginTransaction().replace(R.id.searchLayout, ratingFragment).commit();
A Fragment is Just like any other object.
on Fragment transaction , the Fragment does not call OnCreate() method instead it starts from onCreateView , therefore , load your users and save it an instance variable and assign it in onCreate()
Example
class MyFragment extends Fragment{
List<users> userList;
void onCreate(){
userList = getUserList();}
//the list is loaded during Oncreate();
now imagine you have replaced the Fragment
now According to Andorid Framework , onCreate() is not Called again
instead onCreateView() is called
void onCreateView(){
//you can check whether instance Variable is initialised or not
if(userList != null) {
listview.setAdapter(new Myadapter(this,userList);
Replace the fragment by adding it to backstack.
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.addToBackStack(tag);
fragmentTransaction.replace(container, fragment, tag);
fragmentTransaction.commit();
Also create object of View and return it if it's not null.
private void View view ;
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
if (view != null)
return view;
view = inflater.inflate(R.layout.fragment_browse_recipe, container, false);
//initialize layout views
return view;
}

Different Activity each tab fragment

I have 3 tabs, and I'm trying to show a different activity and layout each tab.
I am using the tabs as my app navigation, so when a has changed, the whole layout and activity need to be changed.
Actually only the layout is changing.
Since the MainActivity.java + layout has the PageAdapter in it (the PageAdapter is loading the tab's content),
I have moved my first page activity from the MainActivity.java to a new java activity called showUpdates.java. Now I'm trying to access this activity as my Tab #1 content.
If you still didn't understand what I'm asking for, The following will make it clear:
My 3 tabs are:
Java: TabFragment1.java, XML: activity_show_updates.xml
Java: TabFragment2.java, XML: tab_fragment_2.xml
Java: TabFragment3.java, XML: tab_fragment_3.xml
The TabFragment1.java:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
/**
* Was trying:
* Intent intent = new Intent(...)
* startActivity(intent);
*/
return inflater.inflate(R.layout.activity_show_updates, container, false);
}
As you can see, it's moving to a new layout, activity_show_updates.xml.
What I'm trying to do is - using my showUpdates.java as the Activity of the tab_fragment_1.
I've been trying to use Intent but my app crashed when I created it at TabFragement1.java.
Anyone knows how I can make showUpdates.java as the activity of this layout?
You cannot nest activities. There are fragments for that. Just create a different fragment for each tab and use an Activity to hold all of them. If you need it, you can also create child fragments (although the support is not so good and you might have some problems, but nothing really hard to handle).
If you have viewpager working, you can do the following:
public static class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return NUM_ITEMS;
}
#Override
public Fragment getItem(int position) {
if(position == 0){
//return first fragment
}
else if(position == 1){
//return second fragment
}
...
}
and you can do your onCreate logic in onCreateView:
public override View OnCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View yourFragment = inflater.inflate (Resource.Layout.fragment_name, container, false);
... //do stuff
return yourFragment;
}
All you need to do is define which fragments to output in your adapter

How can I maintain a child view's state when switching fragments?

I am having a hard time understanding how the fragment lifecycle relates to switching between fragments in the back stack. Please bear with me if my question exposes more than one misconception.
Here is my code:
public class SomeFragment extends Fragment {
private SomeCustomView customView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.some_fragment, container, false);
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Create the child view
customView = (SomeCustomView) getView().findViewById(R.id.some_fragment_child_view);
customView.initializeMyCustomView();
}
}
As you can see, my fragment has a child view. The child view is a custom one. Here's code:
public class SomeCustomView extends SurfaceView implements SurfaceHolder.Callback {
private boolean aVariableWhichMustPersistForLifetimeOfApplication;
}
Whenever this fragment is added to the back stack and then later restored, the variable customView is recreated, and so I loose the value of aVariableWhichMustPersistForLifetimeOfApplication. This is creating all sorts of problems for me.
The application started out using an Activity that only displayed SomeCustomView and there were no fragments. Now I have to add functionality and so I have turned the custom view into a fragment, and thus I arrive at this problem.
I found an answer which works for me. The FragmentTransaction class has a number of methods which allow you to switch fragments in/out. (Android documentation for FragmentTransaction is here and a great StackOverflow explanation is here.)
In my case, I wanted SomeFragment to never loose the data contained in its view. To do this, use this code:
SomeFragment fragment = new SomeFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.add(R.id.activity_fragment_placeholder, fragment, "some_fragment");
transaction.commit();
and then later:
getFragmentManager().beginTransaction().hide(fragment).commit();
You can now add/attach a different fragment to R.id.activity_fragment_placeholder. Notice that I'm using hide() rather than replace(), that's the key difference that keeps the view from being destroyed. When you want the fragment back, you can use show() or Android will do this automatically when the user clicks "Back" if you use addToBackStack() when adding/attaching your other fragment.

Check if fragment displayed as dialog to change click listener behavior

I am creating an app in which the user should be able add people for meetings.
The structure consists of several fragments managed in the same activity (list_people, person_detail, create_meeting).
I would like to reuse the fragment showing the list of people as a dialog in the create_meeting fragment. And add a person to a meeting by clicking on the person item.
When the list_people fragment is embedded in the view, a click on a person item replace the list_people fragment with a person_detail fragment. This behavior is already implemented with an interface for the main activity.
I am looking for a solution to change the behavior of the click listener whether the list_people fragment is displayed as an embedded fragment or as a dialog. Any ideas of how I could do that?
Any help would be greatly appreciated.Thanks.
Ok I have found a solution. It is to use a constructor (newInstance) for the fragment in which you can pass variables.
public class ListPeopleFragment extends Fragment {
public static ListPeopleFragment newInstance(boolean nested){
ListPeopleFragment f = new ListPeopleFragment();
Bundle args = new Bundle();
args.putBoolean("nested", nested);
f.setArguments(args);
return f;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View view = inflater.inflate(R.layout.fragment_list_people, container, false);
boolean nested = false;
Bundle arguments = getArguments();
if (arguments != null)
{
nested = getArguments().getBoolean("nested");
}
displayListViewPeople(view, nested);
return view;
}
}
The displayListViewPeople set the click listener depending on the value of nested.
You instantiate the fragment this way:
ListPeopleFragment nestedFrag = ListPeopleFragment.newInstance(true);

Categories

Resources