No such instance field: 'bottomNavigationView' - android

I am using new released Bottom Navigation View from android support library - 25.0.0. And I am trying to setOnNavigationItemSelectedListener and nothing is happened. When I tried to debug the project, I see the error: "No such instance field: 'bottomNavigationView'", but my bottomNavigationView is initialized and the method "getMaxItemCount" works perfectly.
I realized that it is not only the "navigationListener" problem, but also onClickListener. Because this code:
bottomNavigationView = (BottomNavigationView) viewHierarchy.findViewById(R.id.tender_bottom_navigation);
bottomNavigationView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast toast = Toast.makeText(getActivity(), "clicky-clicky", Toast.LENGTH_LONG);
toast.show();
}
});
also provides the same error.
I saw the same mistakes with the boolean values here on site, but the most common answer for this was "restart android studio/clean the project". I have done it. Do you have any thoughts on this?
May be there is no way to implement bottomNavigationView inside a fragment?
EDIT: I tried this code in activity class and it works! So why does he lose the field in the fragment? That is still a riddle to me.
EDIT 2: The whole fragment code.
public class NewTenderFragment extends Fragment {
BottomNavigationView bottomNavigationView;
FragmentManager fragmentManager;
FragmentTransaction fragmentTransaction;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View viewHierarchy = inflater.inflate(R.layout.fragment_new_tender, container, false);
bottomNavigationView = (BottomNavigationView) viewHierarchy.findViewById(R.id.tender_bottom_navigation);
int t = bottomNavigationView.getMaxItemCount(); //this returns "5"
bottomNavigationView.setOnClickListener(new View.OnClickListener() { //this returns such error
#Override
public void onClick(View view) {
Toast toast = Toast.makeText(getActivity(), "clicky-clicky", Toast.LENGTH_LONG);
toast.show();
}
});
return viewHierarchy;
}

Related

Communicating between Fragment and Activity

I have a fragment with multiple toggle buttons. On click of a particular button in my Activity, I'd like to upload the status of all these toggle buttons to the server. I see there is one way of doing it as explained in the below link, where we create a listener interface and on every click of each toggle button, we update some tag/integer in the activity corresponding to each toggle button.
https://developer.android.com/training/basics/fragments/communicating.html
But I would like to know if there is any way to know the checked/unchecked status of all toggle buttons in the fragment from the activity without implementing the interface methods for each time a toggle button is clicked. Hope I'm clear.
Here's a working example of how to achieve that. The activity:
public class MainActivity extends AppCompatActivity {
ToggleFragment toggleFragment;
Button updateStatusButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
updateStatusButton = (Button) findViewById(R.id.updateStatusButton);
toggleFragment = ToggleFragment.newInstance();
getSupportFragmentManager().beginTransaction()
.replace(R.id.main_frame, toggleFragment, "ToggleFragment")
.commit();
updateStatusButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
boolean buttonsChecked = toggleFragment.areToggleButtonsChecked();
Toast.makeText(MainActivity.this, String.format("Toggle buttons checked: %s", buttonsChecked), Toast.LENGTH_LONG).show();
}
});
}
}
And the Fragment:
public class ToggleFragment extends Fragment {
ToggleButton toggleButton1;
ToggleButton toggleButton2;
ToggleButton toggleButton3;
public static ToggleFragment newInstance() {
Bundle args = new Bundle();
ToggleFragment fragment = new ToggleFragment();
fragment.setArguments(args);
return fragment;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.toggle_fragment, container, false);
toggleButton1 = (ToggleButton) view.findViewById(R.id.toggleButton1);
toggleButton2 = (ToggleButton) view.findViewById(R.id.toggleButton2);
toggleButton3 = (ToggleButton) view.findViewById(R.id.toggleButton3);
return view;
}
public boolean areToggleButtonsChecked() {
return toggleButton1.isChecked()
&& toggleButton2.isChecked()
&& toggleButton3.isChecked();
}
}
Honestly, the best way that you could accomplish this is with a ViewModel from the Android Architecture Components library. The ViewModel can have an ObservableField or LiveData value that each of the Fragments can observe, and always have the correct information. This would also make it easier to be lifecycle-aware.

Show dialog above view pager that has nested fragments

I have setup a very simple test project https://github.com/ArtworkAD/ViewPagerDialogTest to evaluate following situation: the main activity has a view pager which hosts a single fragment using support fragment manager:
public class MainActivity extends AppCompatActivity {
// ...
#Override
protected void onCreate(Bundle savedInstanceState) {
// ...
viewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager()));
// ...
tabLayout.setupWithViewPager(viewPager);
}
#Override
protected void onResume() {
super.onResume();
MainActivity.CustomDialog dialog = (MainActivity.CustomDialog) getSupportFragmentManager().findFragmentByTag(MainActivity.CustomDialog.TAG);
if (dialog == null) {
new MainActivity.CustomDialog().show(getSupportFragmentManager().beginTransaction(), MainActivity.CustomDialog.TAG);
}
}
// ...
}
When the activity is resumed a dialog fragment is shown inside the main activity.
The single fragment inside the view pager is defined like this:
public class RootFragment extends Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.root_fragment, container, false);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction().add(R.id.root_frame, new FirstLevelFragment(), "ROOT").commit();
}
return root;
}
}
This root fragment allows us to stack other fragments on the "root_frame". So we stack another and another:
public class FirstLevelFragment extends Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
setRetainInstance(true);
View root = inflater.inflate(R.layout.first_level_fragment, container, false);
root.findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
SecondLevelFragment f = (SecondLevelFragment) getActivity().getSupportFragmentManager().findFragmentByTag("NESTED");
if (f == null) {
getActivity().getSupportFragmentManager().beginTransaction().add(R.id.root_frame, new SecondLevelFragment(), "NESTED").addToBackStack(null).commit();
}
}
});
return root;
}
public static class SecondLevelFragment extends Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
setRetainInstance(true);
return inflater.inflate(R.layout.second_level_fragment, container, false);
}
}
}
This works great! The stacking idea is taken from https://stackoverflow.com/a/21453571/401025 . However when dialog is shown and the users goes to the second level fragment and rotates the screen I get following exception:
E/AndroidRuntime: java.lang.RuntimeException: Unable to start activity
ComponentInfo{de.azzoft.viewpagerdialogtest/de.azzoft.viewpagerdialogtest.MainActivity}:
java.lang.IllegalArgumentException: No view found for id 0x7f0c0083
(de.azzoft.viewpagerdialogtest:id/root_frame) for fragment
SecondLevelFragment{15c0db38 #0 id=0x7f0c0083 NESTED}
E/AndroidRuntime: Caused by: java.lang.IllegalArgumentException: No
view found for id 0x7f0c0083
(de.azzoft.viewpagerdialogtest:id/root_frame) for fragment
SecondLevelFragment{15c0db38 #0 id=0x7f0c0083 NESTED}
Full stack trace: https://github.com/ArtworkAD/ViewPagerDialogTest/blob/master/README.md
Without the dialog appearing everything works great. You can test it by downloading the test project.
It seems that the dialog, which is actually a fragment, messes up fragment hierarchy when it is added to the activity. Any ideas how to fix this?
It is important that the second fragment is retained.
No view found for id 0x7f0c0083 (de.azzoft.viewpagerdialogtest:id/root_frame) for fragment SecondLevelFragment
When Activity recreates on rotate, the Activity FragmentManger tries to add the SecondLevelFragment into R.id.root_frame . But the root_frame view is not in Activity layout, its in FirstLevelFragment layout. Thats why the app crashes.
You have to make two changes to fix this issue.
Add the FirstLevelFragment into the RootFragment using the getChildFragmentManager
getChildFragmentManager().beginTransaction().add(R.id.root_frame, new FirstLevelFragment(), "ROOT").commit();
Add the SecondLevelFragment using FragmentManager
getFragmentManager().beginTransaction().add(R.id.root_frame, new SecondLevelFragment(), "NESTED").addToBackStack(null).commit();
Finally remove the setRetainInstance from FirstLevelFragment and SecondLevelFragment as nested fragments doesn't required to set retain.
If you need to pop back the SecondLevelFragment on back press you need to pass the back press the event to RootFragment and pop from back stack.
Override the back press on activity
#Override
public void onBackPressed() {
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.viewpager);
if(fragment instanceof RootFragment){
boolean handled = ((RootFragment)fragment).onBackPressed();
if(handled){
return;
}
}
super.onBackPressed();
}
And handle the back press on RootFragment
public boolean onBackPressed() {
int count = getChildFragmentManager().getBackStackEntryCount();
if(count > 0){
getChildFragmentManager().popBackStackImmediate();
return true;
}
return false;
}
I created a Pull request to your repository . please check
https://github.com/ArtworkAD/ViewPagerDialogTest/pull/1
Let me know if any questions.
If you override onDismiss so resolved crash. enjoy it.
#Override
protected void onResume() {
super.onResume();
DialogFragment dialog = (DialogFragment) getSupportFragmentManager().findFragmentByTag(TAG);
if(dialog == null){
CustomDialog.newInstance().show(getSupportFragmentManager(), TAG);
}
}
public static class CustomDialog extends DialogFragment {
public static CustomDialog newInstance() {
CustomDialog d = new CustomDialog();
return d;
}
#Override
public void onDismiss(DialogInterface dialog) {
// super.onDismiss(dialog);
Toast.makeText(getActivity(), "onDismiss", Toast.LENGTH_LONG).show();
}
#Override
public void onCancel(DialogInterface dialog) {
// super.onCancel(dialog);
Toast.makeText(getActivity(), "onCancel", Toast.LENGTH_LONG).show();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("Dialog");
builder.setMessage("This is a message!");
builder.setPositiveButton("Okay", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
Toast.makeText(getActivity(), "onClick", Toast.LENGTH_LONG).show();
}
});
builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
Toast.makeText(getActivity(), "onClick", Toast.LENGTH_LONG).show();
}
});
return builder.show();
}
}
If you want to keep the state of your Fragments you should use a FragmentStatePagerAdapter.
From the docs:
Implementation of PagerAdapter that uses a Fragment to manage each
page. This class also handles saving and restoring of fragment's
state.
If you use this you can also remove the setRetainInstance(true) calls.
Well, I had downloaded your Test app and it seems that I have fixed the problem.
In your FirstLevelFragment class, comment the following line
//if (nestedNestedFragment == null) {
getActivity().getSupportFragmentManager().beginTransaction().add(R.id.root_frame, new SecondLevelFragment(), "NESTED").addToBackStack(null).commit();
//}
And
Comment setRetainInstance(true); in SecondLevelFragment
I think you missed setContentView() in onCreate() of your Activity. See your Fragment can not be added without a View hierarchy. Your Fragment is hosted by an activity. So you need to set the content to the activity first.
Hope this Helps,
Thanks.

Android Fragment onCreateView - inflating every time it's called?

I have been putting some debugging into my app to try to work out what is happening with the lifecycle.
I have some tabs and each tab content is a different fragment, each time a tab is changed onCreateView is called in the corresponding fragment.
In some of my onCreateViews I am currently mocking up some data and injecting table rows etc and then inflating the view every time. Like in the example below:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d(LOG_TAG, "******************* onCreateView() is being called in the Container Fragment *********************");
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_patient, container, false);
mTabHost = (FragmentTabHost)rootView.findViewById(android.R.id.tabhost);
mTabHost.setup(getActivity(), getChildFragmentManager(), android.R.id.tabcontent);
//Set up tabs
mTabHost.addTab(mTabHost.newTabSpec("home_addresses").setIndicator("Home Addresses"), HomeAddressesFragment.class, null);
mTabHost.addTab(mTabHost.newTabSpec("postal_addresses").setIndicator("Postal Addresses"), PostalAddressesFragment.class, null);
mTabHost.addTab(mTabHost.newTabSpec("temp_addresses").setIndicator("Temporary Addresses"), TemporaryAddressesFragment.class, null);
mTabHost.addTab(mTabHost.newTabSpec("email_other").setIndicator("Email / Other"), TemporaryAddressesFragment.class, null);
mTabHost.addTab(mTabHost.newTabSpec("tel_fax").setIndicator("Telephone & Fax"), TemporaryAddressesFragment.class, null);
//Set to home addresses tab
mTabHost.setCurrentTab(0);
tempAddressBtn = (Button)rootView.findViewById(R.id.temp_addr_tab_btn);
tempAddressBtn.setTransformationMethod(null);
tempAddressBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
tabHandler(v);
}
});
postalAddressBtn = (Button)rootView.findViewById(R.id.postal_addr_tab_btn);
postalAddressBtn.setTransformationMethod(null);
postalAddressBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
tabHandler(v);
}
});
homeAddressBtn = (Button)rootView.findViewById(R.id.home_addr_tab_btn);
homeAddressBtn.setTransformationMethod(null);
homeAddressBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
tabHandler(v);
}
});
telFaxBtn = (Button)rootView.findViewById(R.id.tel_fax_tab_btn);
telFaxBtn.setTransformationMethod(null);
telFaxBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
tabHandler(v);
}
});
emailOtherBtn = (Button)rootView.findViewById(R.id.email_other_tab_btn);
emailOtherBtn.setTransformationMethod(null);
emailOtherBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
tabHandler(v);
}
});
return rootView;
}
My question is should I be doing this each time the onCreateView method is called? Is the view already cached somewhere?
I am seeing code in tutorials that looks a bit like:
#Override
protected void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
setContentView(R.layout.pager_activity);
if (savedInstance == null) {
PagerFragment frag = PagerFragment.newInstance(buildPagerData());
FragmentManager fm = getSupportFragmentManager();
fm.beginTransaction().add(R.id.layout_fragments, frag, PAGER_TAG).commit();
}
findViewById(R.id.btnFragments).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
changeFragmentVisibility();
}
});
}
I know this Fragment in the example above extends from FragmentActivity and mine just extend from Fragment but it is using the fragment manager and checking if the state is null before deciding to instantiate a new fragment.
Edit: I am guessing as well that as this happens in onCreate and not onCreateView it happens less frequently?
I am wondering if this is something that I should be doing or if its ok to continue with the way I am going?
In my opinion you should manage your tabs from the parent Activity. Each Fragment should be responsible for one part of the UI.
Another advantage of that approach is that each Fragment layout will be a bit easier. And it will be easier to apply different containers if needed (e.g ViewPager).

Is it possible to convert Blank Activities to Fragment Activities

I currently have an Unit Converter app that I'm working in.
Here I've used multiple Blank Activities. Where each Unit's Activity can be opened using MainActivity. But now I want to make it tablet friendly.
Hence I want to use FragmentActivity now. Is it possible to convert the Blank Activities to Fragment Activities.?
All you need to do is take all View-specific logic from the Activity to a Fragment, then load the Fragment in your Activity.
For example,
public class MainActivity extends Activity {
#InjectView(R.id.button)
public Button button;
#OnClick(R.id.button)
public void onButtonClick(View view) {
Toast.makeText(this, "Hello!", Toast.LENGTH_SHORT).show();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.inject(this);
}
}
This type of logic goes in
public class MainFragment extends Fragment {
#InjectView(R.id.button)
public Button button;
#OnClick(R.id.button)
public void onButtonClick(View view) {
Toast.makeText(this, "Hello!", Toast.LENGTH_SHORT).show();
}
#Override
public void onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, container, false);
ButterKnife.inject(this, view);
return view;
}
}
And your Activity needs to display this fragment either statically, or dynamically. If you go dynamical, you'll need the following lines in your Activity:
public class MainActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = getSupportFragmentManager();
if(savedInstanceState == null) {
fm.beginTransaction()
.add(R.id.container, new MainFragment())
.commit();
}
fm.addOnBackStackChangedListener(new OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if(getSupportFragmentManager().getBackStackEntryCount() == 0) finish();
}
});
}
}
If you go static, then you need to specify the fragments in your layout XML for the activity.
http://developer.android.com/guide/components/fragments.html#Adding
I would visit the Android website as they give a fairly good explanation on how fragments work.
You can learn how to add them to your existing application by another Android link here.

Fragment setRetainInstance not works (Android support lib) [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Android fragments setRetainInstance(true) not works (Android support library)
I wrote a simple test project, but I cant understand why I always receive savedInstanceState = null in lifecycle methods onCreate, onCreateView and onActivityCreated. I change the screen orientation, see the log, but state not saved.
Tell me please where is my mistake.
Thanks.
The code of fragment class is:
public class TestFragment extends Fragment {
private String state = "1";
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (savedInstanceState != null) {
//never works
state = savedInstanceState.getString("state");
}
//always prints 1
Toast.makeText(getActivity(), state, Toast.LENGTH_SHORT).show();
return inflater.inflate(R.layout.fragment_layout, container, false);
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("state", "2");
Log.e("", "saved 2");
}
}
EDIT
When i try to use setRetainInstance i have no result again((( I change a state to 2 using btn1, but after changing orientation i see 1 when pressing on btn2. Help please(
public class TestFragment extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
setRetainInstance(true);
super.onCreate(savedInstanceState);
}
private String state = "1";
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_layout, container, false);
//button for changing state
((Button)view.findViewById(R.id.button1)).setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
state = "2";
}
});
//button for showing state
((Button)view.findViewById(R.id.button2)).setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
Toast.makeText(getActivity(), state, Toast.LENGTH_SHORT).show();
}
});
return view;
}
}
I have seen the same issue. But you can save the instate state in an other way, thanks to the method setRetainInstance(true), all your class data are saved automatically.
#Override
public void onCreate(Bundle savedInstanceState) {
setRetainInstance(true);
super.onCreate(savedInstanceState);
}
EDIT
Try to also add these params when you declare your activities :
<activity
android:name="MyActivity"
android:configChanges="orientation|keyboardHidden|screenSize" />

Categories

Resources