Via a checkbox in my preferences, the view of a fragment must be updated. The preferences are within a PreferenceFragment:
public class SettingsFragment extends PreferenceFragment {...}
This fragment is instantiated in an Activity using:
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment())
.commit();
My app has some other fragments, which extend android.support.v4.app.Fragment. Upon the checkbox change in the preferences, I have to update the view in this fragment:
public class OfferingsFragment extends android.support.v4.app.Fragment {
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState)
{
// This view needs to be updated
View view = inflater.inflate(R.layout.offering_tiles, null);
...
}
}
My question is: How can I update this view from within the PreferencesFragment? Apparently, I cannot use getFragmentManager to get an instance of OfferingsFragment because OfferingsFragment is a fragment from support library. Is there a way to access OfferingsFragment from within PreferenceFragment?
You have a few options based on how your app is setup. If both fragments are not active at the same time, the Offerings fragment can just read the value from the SharedPreferences when it creates the view.
If both fragments are active and the view needs to be refreshed, you can either create a callback through the parent activity using an interface, example here. Or an easier route is to use an event bus like Otto or Green Robot
Related
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()
}
}
The main page of my application has a FrameLayout.
I'm instantiating two fragments when the activity starts, and I'm trying to use a menu button to swap between the fragment.
scanHistoryFrag = new HistoryFragment();
scanFrag = new ScanFragment();
I never replace these objects - I use the same ones throughout the lifecycle of the application. However, when I swap them in my FrameLayout...
private void ChangeFragment(Android.Support.V4.App.Fragment fragment)
{
Android.Support.V4.App.FragmentTransaction ft = SupportFragmentManager.BeginTransaction();
ft.Replace(Resource.Id.fragmentContainer, fragment);
ft.Commit();
}
OnCreate and OnCreateView are called on the Fragment again... which means any adjustments I made post creation on that fragment are overwritten with initial values again. I can't seem to find any explanation for why this is happening or how I might avoid it.
The ChangeFragment method is being called by OnOptionsItemSelected, as I'm using a menu button to toggle them.
I never replace these objects - I use the same ones throughout the lifecycle of the application.
Initialization of a subclass of Fragment just create a instance of this class object, the constructor of this class will be called, but it will not go through the lifecycle of Fragment unless this Fragment is added, for more information, you can refer to Fragments. To understand it easier, I personal think the instance saves the data state of this Fragment class, but the events of lifecycle handle the view state of this Fragment.
which means any adjustments I made post creation on that fragment are overwritten with initial values again.
Yes, you're right. To avoid overwritting with initial values again, we can cache the fragment's view in OnCreateView for example like this:
private View rootView;
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Use this to return your custom view for this Fragment
// return inflater.Inflate(Resource.Layout.YourFragment, container, false);
if (rootView == null)
{
//first time creating this fragment view
rootView = inflater.Inflate(Resource.Layout.fragmentlayout1, container, false);
//Initialization
//TODO:
}
else
{
//not first time creating this fragment view
ViewGroup parent = (ViewGroup)rootView.Parent;
if (parent != null)
{
parent.RemoveView(rootView);
}
}
return rootView;
}
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;
}
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.
I am creating a fragment and adding it to a layout using java code. To do this, i created 2 classes and 2 layouts. One of the classes extends Fragment and other extends FragmentActivity. One of the xml files is the container and other is the fragment. Here is my code:
public class FragmentClass extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_layout, container,false);
return v;
}
}
And here is how i add the fragment to the layout:
public class Fragment_Activity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.container_layout);
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.container_layout_eklenecek_yer);
if(fragment==null){
fragment=new FragmentClass();
fm.beginTransaction().add(R.id.container_layout_eklenecek_yer, fragment).commit();
}
}
}
This code works as i expect, but here is my question: I have a piece of code in Fragment_Activity class that says:
fragment=new FragmentClass();
and FragmentClass has no constructors. Is a default, empty constructor is called here, or onCreateView works as a constructor? I am confused here.
Thanks
No.
You need a default constructor with fragments, a constructor that takes no arguments is a "default constructor" (this might be c++ terminology) because it allows you to construct an object, for certain.
Because Android might need RAM or something it can kill your fragments, it might also bring them back.
If you pass stuff to the constructor how will the Android OS know that stuff to pass it to you when it needs to re-create the fragment? It doesn't - this question has no answer.
Hence the default constructor.
When re-creating your fragment Android will attach an activity, and you can use onActivityAttached (or something to that tune, look up fragment life-cycles) to get the activity. If you know the activity implements/extends a whatever you can cast that activity to a whatever and store it, whatever you need to do.
The onCreateview method is what the name says: the thing that is called that returns a view. It is called when Android wants the view that represents your fragment, it is NOT a constructor, neither to be thought of, or in actual fact.