NavigationDrawerFragment not working with AppCompat Toolbar - java

I'm using the NavigationDrawer created by the template (when starting a new project).
I'm trying to get Material Design compatibility so I'm using AppCompat v7.
I followed these instructions to set a Toolbar as my ActionBar (i.e. using setSupportActionBar on my toolbar) and I get a NPE in my NavigationDrawerFragment at (inside onCreateView)
mDrawerListView.setAdapter(new ArrayAdapter<String>(
getActionBar().getThemedContext(),
android.R.layout.simple_list_item_activated_1,
android.R.id.text1, ...
now I suspect the issues is with the getActionBar() method inside fragment:
private ActionBar getActionBar() {
return ((ActionBarActivity) getActivity()).getSupportActionBar();
}
but I have no idea why - I called setSupportActionBar(toolbar) in Activity's onCreate before calling setUp() on the NavigationDrawerFragment...
If anyone has a clue why this is happening please help!

It seems like the problem was that fragment's onCreateView was called before activity's onCreate and thus there was no ActionBar set. I just moved the problematic code into fragment's onActivityCreated and it works like a charm.

just replace getActionBar().getThemedContext() with getActivity()

Related

Does findFragmentById take in a FrameLayout rather than a Fragment?

I'm new to the concept of Fragments
In the video I was watching, they used this piece of code:
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentById(R.id.my_container);
if(fragment == null){
fragment = new FragmentMain();
fragmentManager.beginTransaction()
.add(R.id.my_container, fragment)
.commit();
}
To create a Fragment via java.
In findFragmentById they passed in a FrameLayout(my_container) rather than a Fragment. I read in the docs that you can pass in a container id, but it confuses me how it will initialize it as a Fragment. How does this work?
Should I use FragmentManager? I read in the docs that it's deprecated.
Thanks!
In findFragmentById they passed in a FrameLayout(my_container) rather than a Fragment. I read in the docs that you can pass in a container id, but it confuses me how it will initialize it as a Fragment. How does this work?
You can think of the container as of a placeholder that can hold a fragment at a time; initially it has no fragment and in this case the if(fragment == null) will be met, so you can do a fragment transaction (So, the container layout now is replaced by a fragment view, which is another xml layout assigned to this particular fragment and returned by its onCreateView() callback.onCreateView() is one of fragment's lifecycle callbacks that get called by the system until the fragment is fully visible to the user in the placeholder.
Later on when you want this placeholder to hold another fragment, you can do the transaction again where it repeats the same thing with the new fragment, the placeholder will show up the layout of the new fragment, and the lifecycle methods of this fragment will get called
Should I use FragmentManager? I read in the docs that it's deprecated.
FragmentManger (from android.app package) and its getFragmentManager() are deprecated, and you should use FragmentManager (from androidx.fragment.app package) and getSupportFragmentManager() instead like you already did.
For more info you can have a look to FragmentManager doc and Fragment lifecycle.
It will look into the container with the given ID and give you the Fragment that is currently inside. At no point does findFragmentById initialize a Fragment, it will only look through existing ones, as the name suggests.
You should not use the native FragmentManager on new versions of Android, as it has been deprecated, but the FragmentManager from the AndroidX Fragment library, providing the same functionality.
See also: https://developer.android.com/guide/fragments/fragmentmanager

How to get onNewIntent() in a Navigation Fragment

I want to use Navigation component in my android project, in order to achieve this, I have refactored my app to be fragment-based, but it raises a problem that I don't know how to pass onNewIntent() in my MainActivity to my Navigation Fragments.
So MainActivity is a NavHost, to make it simple, saying that I have a fragment as the start destination, how can I get onNewIntent() inside this fragment from MainActivity?
UPDATE
Also, is there a way to get the reference of the current presented fragment, like the one currently held by NavHost?
On new intent , you can do something like
val navigationController = nav_host_fragment.findNavController()
if (navigationController.currentDestination?.id == R.id.fragmentA) {
(nav_host_fragment.childFragmentManager.fragments[0] as FragmentA).doSomething()
}
Here you can pass any values to the function doSomething()
You can use in MainAcitivy class this:
private Fragment fragment;
fragment = new FragmentTry();
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragmentid, fragment)
.commit();

What is the best way to maintain the backstack of my Activity after Activity.Recreate() is called?

I have an Activity that handles many Fragments, and, for backstack management, I have a custom stack, where I manage show / hide for Fragments. My code and navigation work perfectly.
Right now, I am implementing the application theme change by a Button in the Configuration Fragment. For this I am using the method Activity.Recreate (); for the change of the theme and, the data of the Configuration Fragment is retained with the same data and the theme of the application changes perfectly, but the BackStack of Fragments disappears, reason why, when pressing the back button, it leaves the application, instead of sending me back to the Fragment or Previous Activity, from where I accessed the Configuration Fragment.
What is the best way to maintain the backstack of my Activity? This is possible?
Important: only when Activity.Recreate(); is called, because if the Activity is destroyed by any other way, I do not want the BackStack back, I want my Activity clean.
Additional:
The orientation setting of my application is in portrait mode.
The launchMode of my Activity is singleTask, it must be so for the type of application I am doing.
From onCreate documentation and this answer.
Add the following logic to your code:
public void onCreate(Bundle savedInstanceState) {
if (savedInstanceState == null) {
// savedInstanceState will be null only when creating the activity for the first time
backstack = new BackStack(); //init your backstack
} else {
// there is a chance that your backstack will be already exists at this point
// if not:
// retrieve the backstack with savedInstanceState.getSerializable("stack")
}
}
And just clear the stack when changing theme, before calling recreate()
// changing theme detected
bacstack.clear();
backstack = null;
recreate();
To save the stack between destruction (onDestroy) and recreation (onCreate) of your activity, Use this method:
#Override
protected void onSaveInstanceState(#NonNull Bundle outState) {
super.onSaveInstanceState(outState);
if (backstack != null) // the check isn't necessary, you can just put a null in the bundle
outState.putSerializable("stack", backstack);
}
the official guide
for saving UI state
The onSaveInstanceState method helps your activity to Survive
Configuration change and System-initiated process death.
link

Access the ActionBar from within a Fragment

How do you access the ActionBar from within a Fragment (android.support.v4.app.Fragment). I am using the Android Support Library v4 and v7. I'm not using the Sherlock library's.
The activity hosting the fragment is an ActionBarActivity.
I have read the Android help section Fragment which led me too getActivity() but there is no getSupportActionBar() method.
ActionBar actionBar = getActivity().getSupportActionBar()
I have read the Android help section Fragment which led me too getActivity() but there is no getSupportActionBar() method.
You will need to cast the result of getActivity() to be an ActionBarActivity, on which you will find getSupportActionBar().
If you are using AppCompatActivity then do it like this.
((AppCompatActivity )getActivity()).getSupportActionBar();
Also it is advised to shift to Toolbar with AppCompatActivity instead of action bar(app bar). For more details refer this http://android-developers.blogspot.in/2014/10/appcompat-v21-material-design-for-pre.html

ActionBar in PreferenceActivity

In my application I am using the new Action Bar Compatibility sample from Google (located at <sdk>/samples/android-<version>/ActionBarCompat) which works great. The only problem I have is applying this to my PreferenceActivity in order to get a screen like the settings in the Android Market (see picture).
To fill the ActionBar with icons, each Activity must extend the ActionBarActivity class. The problem is that my Activity already extends PreferenceActivity and in Java classes can not extend more than one class.
There must be a way to get the ActionBar together with a PreferenceScreen. I would be glad if anybody could provide a solution for this common issue.
P.S.: A solution like in How to add a button to PreferenceScreen does not fit because the ActionBar is actually the title bar and so this is more a Java than a layout thing.
Edit: My answer below is rather hacky and it seems like it is now outdated (for pre Android 3.0) Have a look at the other answers for less hacky and more current solutions ~pyko 2014-09-01
I managed to get it working - not sure if this is the nicest/cleanest solution, but it works.
Had to make the following changes:
Make a copy of ActionBarActivity and have the new class extend PreferenceActivity
public abstract class ActionBarPreferenceActivity extends PreferenceActivity {
// contents exactly the same as 'ActionBarActivity'
}
Modify onCreate() in ActionBarHelperBase.java slightly - make a special case for PreferenceActivity classes
#Override
public void onCreate(Bundle savedInstanceState) {
// If the activity is a PreferenceActivity, don't make the request
if (!(mActivity instanceof PreferenceActivity)) {
mActivity.requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
}
Have your PreferenceActivity extend this class and add request for FEATURE_CUSTOM_TITLE before you call super.onCreate()
public class MyPreferenceActivity extends ActionBarPreferenceActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); // add this line
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
// etc etc
}
// etc etc
}
As far as I can tell, changes 2 and 3 are needed because for PreferenceActivity:
"As soon as you call super.onCreate(), the ViewGroup will be set up and so, you are not allowed to change the Window's parameters." (see Oliver's comment to the answer)
I guess the order of how components in PreferenceActivity activities are created is different to plain Activity activities .
If you want to try a PreferenceFragment implementation based on support-v4 Fragment:
https://github.com/kolavar/android-support-v4-preferencefragment
I´m using it by myself and it isnt much work turning PreferenceActivity into PreferenceFragment.
Can you just clone the code for ActionBarActivity, and change "extends Activity" to "extends PreferenceActivity"? Then extend your new class instead of ActionBarActivity.
From all the Google apps I've seen, though, it seems unusual to put buttons in the action bar of a PreferenceActivity. If you're not putting buttons on it, you could just use a values-v11 alternate style resource to show the holo theme, and set that style in the manifest for your PreferenceActivity.
I used in my application this actionbar
https://github.com/johannilsson/android-actionbar and it's work great with this thread How to add a button to PreferenceScreen
I'd like to thank to #pyko providing a great answer, but it has problem that it won't work well on HoneyComb and above. well you can have a hack way to get it around like #AndroidDev said;
But #pyko is gonna pollute the ActionBarHelperBase class, and #AndroidDev isn't very transparent.The best way is to create ActionBarActivityPreferences who extends from PreferenceActivity; and in onCreate method, change the order of calling parent method:
#Override
protected void onCreate(Bundle savedInstanceState) {
//IMPORTATNT: MAKE SURE actionBarHelper called before super;
//as super oncreate of prefenceactivity is actuallying setting the content view
mActionBarHelper.onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
}
why calls 'mActionBarHelper.onCreate(savedInstanceState);' before 'super.onCreate(savedInstanceState);' , that is because super (i.e. PreferenceActivity) is actually setting the content view in its onCreate method, which would cause crash ("requestFeature() must be called before adding content'). SO what you need do is to swap the order, make sure ' mActionBarHelper.onCreate(savedInstanceState);' is called before super.
In this way, we don't need to pollute the 'ActionBarHelperBase' yet we keep SettingActivity very clean because we encapsulate the tricky detail to 'ActionBarActivityPreferences' and bang!
You can easily add action bar in preference activity by the following changes:
In AndroidManifest.xml :
<activity
android:name=".activity.SettingsActivity"
android:theme="#style/SettingsTheme"
android:label="Settings"/>
In v21/styles.xml
<style name="SettingsTheme" parent="#android:style/Theme.Material.Light.DarkActionBar">
<item name="android:colorPrimary">#color/colorPrimary</item>
<item name="android:colorPrimaryDark">#color/colorPrimaryDark</item>
</style>
In v14/styles.xml for Back API support:
<style name="SettingsTheme" parent="#android:style/Theme.Holo.Light.DarkActionBar">
<item name="android:actionBarStyle">#style/ActionBar.V14.Movie.NoTitle</item>
</style>
Thanks, just an update, you need to add an if statement before the Custom Title line to support HoneyComb and above.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
You can get a ActionBarSherlock lib and let you code extends SherlockPreference;

Categories

Resources