According to the navigation priciples the first destination in your app should be the screen your users would normally see when they launch the app after signup/login or any other conditional navigation, I called that start destination 'homeFragment'.
Following this principle and after reading the post on conditional navigation by Maria Neumayer I am facing some issues with the Toolbar and the back navigation when going through the conditional navigation flows.
I am architecting the app using one single activity with a ConstraintLayout, a Toolbar and the NavHostFragment:
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.activities.NavigationTestActivity">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="#navigation/nav_graph"
app:defaultNavHost="true"
/>
</android.support.constraint.ConstraintLayout>
The main graph looks like this with the home destination as start destination, connected with an action to a detail fragment (this action is triggered from a button) and a conditional navigation implemented using a nested graph:
Main Graph
I called this nested graph welcomeGraph and it includes the screens for login or signup, you can see it here:
Welcome Nested Graph
In the homeFragment onResume I check if the login/signup has been completed (determined by a dummy boolean stored in sharedPrefs) and if not I launch the welcome nested graph for signup/login.
In the login destination I have a 'Completed' button which sets the dummy boolean in sharedPrefs as true and triggers an action popToWelcomeGraph (inclusive) which should close the whole nested graph and take me back to the homeFragment (this works).
PROBLEM - Toolbar issue in nested graph:
As the Welcome graph is lauched inmediatelly after user lands in the app, the toolbar should not display a back/up arrow in the first destination of that nested graph, instead it should feel as if it was the first screen on the app, and tapping back should quit the app.
QUESTION: Is it possible to alter the Toolbar here to simulate the first screen in the nested graph is the first screen in the app until the login/signup is completed? Would this be a bad practice?
I have recently solved this issue by creating an AppBarConfiguration and setting up the toolbar with that configuration. The AppBarConfiguration takes in the top level destination ids as parameter. With that being said, you can do something like
private lateinit var appBarConfiguration: AppBarConfiguration
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
appBarConfiguration = AppBarConfiguration(setOf(R.id.homeFragment,R.id.welcomeFragment))
findViewById<Toolbar>(R.id.toolbar).setupWithNavController(navController, appBarConfiguration)
}
This way the back button won`t be shown on top level fragments.
You have to implement communicator interface like below
interface ActivityCommunicator {
void alterToolbar();
}
and implement it to your activity class like below
class HomeActivity extends AppCompatActivity implements ActivityCommunicator {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Activity code
}
#Override
public void alterToolbar() {
ActionBar actionBar = getSupportActionBar();
// False to hide back button and true to show it
actionBar.setDisplayHomeAsUpEnabled(false);
}
}
and from your fragment you can call it like below
public class MainFragment extends Fragment {
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
ActivityCommunicator activityCommunicator = (ActivityCommunicator) getActivity();
activityCommunicator.alterToolbar();
// Fragment code
return super.onCreateView(inflater, container, savedInstanceState);
}
}
You can change alterToolbar() implementation as per your need
Related
I have few fragments and one activity. The app contains toolbar which is always visible. There is icon on my toolbar. And i need to hide this icon when user opens 2,4,5 fragments and show this icon when users open 1 and 3 fragment. I don't need all code for this logic, I need advice how can I implement it and where add some logic for this behavior
The following is true assuming you are using jetpack's navigation and single activity:
Add destination change listener to your main nav controller inside your activity (addOnDestinationChangedListener, the interface is NavController.OnDestinationChangedListener). Inside the listener, you could check for destination.id in onDestinationChanged implementation. Actually, you could create two sets like this
private val twoFourFiveDestinations =
setOf(R.id.two, R.id.four, R.id.five)
private val oneThreeDestinations =
setOf(R.id.one, R.id.three)
just to make check like this if(twoFourFiveDestinations.contains(destination.id) ... and manage your icon visibility accordingly, it will make life easier.
Alternative solution would be to hand over icon management to fragments. You could define some interface for comm with activity, and manage toolbar icon when respective fragment is up and running. But you'd need to do that in every fragment of your question.
Step 1 : create an interface FragmentListener with one method :
public interface MyFragmentListener {
void onChangeToolbarImage(boolean show);
}
Step 2 : implement in YourActivity :
ImageView toolarImage= findViewById(R.id.toolbarimage)()///
#Override
public void onChangeToolbarImage(boolean show) {
if()
{ //check your imageView is Visible or not
toolbarImage.setVisibility(show); //change your ImageView's visibility
}
}
Step 3 : in each fragment you need get instance from interface :
private MyFragmentListener fragmentListener;
Step 4 : override onAtach in your Fragment :
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
if (context instanceof MyFragmentListener)
fragmentListener = (MyFragmentListener) context;
fragmentListener.onChangeToolbarTitleImage(true or false);
}
Create a companion object or static variable if you are using java.
class Util {companion object { lateinit var toolbarImg: ImageView }}
Inside your Main Activity onCreate initialize your toolbar and imageview
toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
Util.toolbarImg = toolbar.findViewById(R.id.cartImg)
XML
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay">
<ImageView
android:id="#+id/cartImg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:visibility="visible"
/>
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
Now all you need to do is control the visibility of this ImageView.
On Fragment transaction
To Hide
if(Util.toolbarImg.visibility == View.VISIBLE){
Util.toolbarImg.visibility = View.GONE }
My app works like a Wizard. I have an Activity that serves as a centralization for all my fragments. There is a BottomNavigationView with a "Next" button that appears in each fragment to drive the wizard.
When I want to go next I call "action_next" from BottomNavMenu and it navigate to the next fragment.
But I need to perform some action's when the user presses the Next Button in the context of that fragment (like store the data inputted). Further, I need to cancel the navigation if there is any problem with data inputted by the user.
At first try i did this in my fragment:
val controller = NavHostFragment.findNavController(this)
controller.addOnNavigatedListener { controller, navDestination: NavDestination ->
when (navDestination.id) {
R.id.destination_setup_tournment -> {
proceedNavigation(controller, navDestination)
}
}
}
private fun proceedNavigation(controller: NavController, navDestination: NavDestination) {
val teams = teamAdapter.getItems()
if (validateSubmit(teams)){
presenter.saveTeams(teams)
}else{
//how to cancel navigation and stay on this fragment?
}
}
But it does not look good or even correct for me and I don't know how to cancel the navigation if something is wrong.
Here is the App's related files:
MainActivity:
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?colorPrimary"
android:theme="#style/ToolbarTheme" />
<fragment
android:id="#+id/nav_host_fragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="#navigation/navigation"
app:defaultNavHost="true" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottom_nav_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:menu="#menu/bottom_nav"
style="#style/Widget.MaterialComponents.BottomNavigationView"
>
</com.google.android.material.bottomnavigation.BottomNavigationView>
bottom_nav.xml:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/action_next" android:title="#string/tornment_mode" android:icon="#drawable/ic_navigate_next_black_24dp" />
</menu>
There's no way to 'cancel' a navigation event - you need to put your business logic on your button/listener itself and only call navigate() when you actually want to navigate.
The Creating event callbacks to the activity documentation goes over one approach for connecting your Activity and Fragment together by using onAttach(Context) to get a reference to your Activity and set a property, callback, etc.
For example, you might consider doing something like:
// In your Activity
var onNextClicked: () -> Unit = {}
// In your Activity's onCreate()
button.setOnClickListener = View.OnClickListener {
onNextClicked.invoke()
}
// In your Fragment
override fun onAttach(context: Context) {
(context as YourActivity).onNextClicked = {
val teams = teamAdapter.getItems()
if (validateSubmit(teams)){
presenter.saveTeams(teams)
findNavController().navigate(R.id.action_next)
}
}
}
Each Fragment would set what they want the next button to do, allowing you full control over what the button does.
If you're dead set on the BottomNavigationView instead of just using an actual Button, you'd want to set your own OnNavigationItemSelectedListener and have that listener call the onNextClicked lambda if you're using this example.
I am having trouble wrapping my head around something but let me first describe my setup:
I have an activity that references 3 fragments, each one of them get shown at the correct time. This is how the ChildrenSpecificationFragment looks:
If the user clicks the floating action button the following DialogFragment opens:
I found the following information in the new material design guidelines: https://www.google.com/design/spec/components/dialogs.html#dialogs-full-screen-dialogs
Avoid dialogs that:
Open additional dialogs from within a dialog.
Contain scrolling content, particularly alerts. Instead, consider alternate containers or layouts that are optimized for reading or interacting with significant amounts of content.
Exceptions include:Full-screen dialogs may open additional dialogs, such as pickers, because their design accommodates additional layers of material without significantly increasing the app’s perceived z-depth or visual noise.
This is where my problems begin. The 'add child' dialog has scrollable content (in landscape mode) and when the user clicks 'Birth date' a date picker opens.
I am trying to find a way to implement a full screen dialog (as in the guidelines) that has a callback to the ChildrenSpecificationFragment, so that I can add the child to the RecyclerView .
I hope that my questing is clear and would greatly appreciate any input that would lead me to the solution. Thanks in Advance!
TL;DR - DialogFragment is insufficient for anything other than completely full-screen. Use an Activity instead.
It is possible to make a DialogFragment full-screen (with the ActionBar shown), but it comes with lots of irritations.
A DialogFragment is, as the name suggests, a Dialog and a Fragment rolled into one: it can be treated as both a Dialog, using show() and dismiss(), or as a Fragment, using it with a FragmentManager.
As the official documentation suggests, making a dialog completely full-screen (overlaying everything) is achieved by attaching the dialog to the root view android.R.id.content:
public void showDialog() {
FragmentManager fragmentManager = getSupportFragmentManager();
CustomDialogFragment newFragment = new CustomDialogFragment();
if (mIsLargeLayout) {
// The device is using a large layout, so show the fragment as a dialog
newFragment.show(fragmentManager, "dialog");
} else {
// The device is smaller, so show the fragment fullscreen
FragmentTransaction transaction = fragmentManager.beginTransaction();
// For a little polish, specify a transition animation
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
// To make it fullscreen, use the 'content' root view as the container
// for the fragment, which is always the root view for the activity
transaction.add(android.R.id.content, newFragment)
.addToBackStack(null).commit();
}
}
To get the dialog to appear below the ActionBar, a FrameLayout is required which is used instead of the root layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/drawer_layout"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:fitsSystemWindows="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.CoordinatorLayout
android:id="#+id/toolbar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- Use ThemeOverlay to make the toolbar and tablayout text
white -->
<android.support.design.widget.AppBarLayout
android:id="#+id/abl_top"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:fitsSystemWindows="true"
android:layout_height="wrap_content"
android:layout_width="match_parent"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
app:layout_scrollFlags="scroll|enterAlways"/>
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
<FrameLayout
android:id="#+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="#layout/nav_header"
app:menu="#menu/nav_view"/>
</android.support.v4.widget.DrawerLayout>
Now comes the pain.
Depending on how the app's main navigation is setup, different hoops will need to be jumped through in order to get everything working perfectly.
The above example has a NavigationView. Since the home button android.R.id.home is handled in the main view, some logic is needed there to check if our dialog is shown so that the home button, which is now an X, will close the dialog. Returning false here allow the event to be handled in the dialog.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
FragmentManager fm = getSupportFragmentManager();
Fragment f = fm.findFragmentById(R.id.content);
if (f instanceof MyDialogFragment) {
return false;
}
mDrawerLayout.openDrawer(GravityCompat.START);
return true;
}
return super.onOptionsItemSelected(item);
}
Also, the back button needs similar logic to determine whether the NavigationView needs closing or the ActionBar content resetting.
#Override
public void onBackPressed() {
if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
mDrawerLayout.closeDrawer(GravityCompat.START);
} else {
FragmentManager fm = getSupportFragmentManager();
Fragment f = fm.findFragmentById(R.id.content);
if (f instanceof MyDialogFragment) {
final ActionBar ab = getSupportActionBar();
ab.setHomeAsUpIndicator(R.drawable.ic_menu);
ab.setTitle(R.string.app_name);
}
super.onBackPressed();
}
}
In the DialogFragment itself, the logic for closing the dialog (and abusing the ActionBar) needs to be implemented.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
if (mActionBar != null) {
mActionBar.setHomeAsUpIndicator(R.drawable.ic_menu);
mActionBar.setTitle(R.string.app_name);
}
getActivity().getSupportFragmentManager().popBackStack();
case R.id.action_save:
if (mOnAcceptListener != null) {
mOnAcceptListener.onAccept();
}
if (mActionBar != null) {
mActionBar.setHomeAsUpIndicator(R.drawable.ic_menu);
mActionBar.setTitle(R.string.app_name);
}
getActivity().getSupportFragmentManager().popBackStack();
return true;
}
return super.onOptionsItemSelected(item);
}
This alls feels really kludgy. Of course, if you're using a TabLayout, forget everything I've just said.
With a TabLayout you can just handle everything in the DialogFragment, but if you're using a ViewPager, it'll be impossible to get the dialog to cover the tabs but not the action bar. See Show DialogFragment over TabLayout.
That question (by me) has an answer that suggests the same as #Jdruwe, which is to forget the hopelessness of the DialogFragment and use an Activity instead.
A solution described on my blog using startActivityForResult(...): http://jeroendruwe.be/full-screen-dialogs-in-android/
I don't see code from your post. So I am guessing your code structure as a start.
First build your dialog with a listener and process setPositiveButton() and the onClick event.
Code suggestion:
public class ChildrenSpecificationFragment extends Fragment {
...
public void passData(Object obj) {
}
class SubChildFragment extends Fragment {
AlertDialog.Builder builder = new AlertDialog.Builder(thisContext);
...
// Add the buttons...
builder.setPositiveButton("Save", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
...
passData(Object obj); // pass data to the outer fragment class
Notes:
SubChildFragment, for example, is an inner class derived from Fragment. It can call the public method passData() in the outer class ChildrenSpecificationFragment for passing any data you need.
I am using an inner class because I think this is what you meant in your diagram by
Add child full-screen fragment
This coding technique is easier than starting a new Activity and Intent.
For showing fullscreen dialogs, there is a good Google webpage I think # Dialog - Fullscreen. Search text for "Showing a Dialog Fullscreen or as an Embedded Fragment".
add this line to oncreate in your custom dialog fragment.
setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Black_NoTitleBar_Fullscreen);
On the other hand, you can use content resolvers to store your children datas.
It has observer pattern. So each CursorAdapter attached to that content it refreshes itself without calling notifySetDataChanged();.
I think you are using RecyclerView.Adapter. You can use this class.
Another advice for implementing adding child feature is using startActivityForResult(activity);.
You can send back datas by using getIntent().getExtras().put(key,value); You can search for custom start activity for result.
Good luck
Could anybody give me a clue in the following situation :
I have an Android 3+ app, which consists of a sofisticated set of interchangably operating activities derived from ActionBarActivity. One of them includes a set of fragments derived from Fragment, that can be selected by user by means of
getSupportFragmentManager().beginTransaction()
.replace(R.id.containerBase, FragmentX.getInstance(0))
.commit()
where
FragmentX extends Fragment {
public static FragmentX getInstance() {
FragmentX fragment = new FragmentX();
return fragment;
}
public FragmentX() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_x, container, false);
. . . . . . . .
return rootView;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
. . . . . . . .
}
}
All fragments (X, Y, Z etc) appear properly and I was happy until it became necessary to modify some fragments so that they can have several subpages, which should be swipable horizontally by user. Other fragments should stay as they are.
Which Android classes could be the optimal choice and how could they be intergrated in my app?
Any help would be highly appreciated.
The Fragment that needs to have "horizontally swipable subpages" should probably use a ViewPager of some sort. Your use case isn't completely clear but in my experience a ViewPager with another set of fragments provides a nice and flexible solution. Also, if your "child" fragments (those in the ViewPager) are essentially the same but display different data (e.g. a set of event details fragments), this fragment can also be reused. If not, you can use a switch or something similar in the ViewPager's public Fragment getItem(int position) method.
You should take a look at the FragmentStatePagerAdapter which should fit your needs, the linked page also has a nice and detailed example of how to use it. One thing to note here, since you are using fragments within fragments, the fragment which contains the ViewPager will need to use getSupportChildFragmentManager instead of the regular getSupportFragmentManager to display those fragments.
Certainly I must give some more details about my app.
public class BasePage extends ActionBarActivity implements NavigationBase.NavigationDrawerCallbacks {
private int mPage;
private NavigationBase mNavigationFragment;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
. . . . . . . .
setContentView(R.layout.activity_base);
mNavigationFragment = (NavigationBase) fragmentManager.findFragmentById(R.id.navigation_drawer);
mNavigationFragment.setUp(
R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout));
}
private void setPage() {
switch(mPage) {
case 1:
fragmentManager.beginTransaction()
.replace(R.id.containerBase, FragmentX.getInstance())
.commit();
break;
case 2:
fragmentManager.beginTransaction()
.replace(R.id.containerBase, FragmentY.getInstance())
.commit();
break;
}
}
}
The activity_base layout looks like this:
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="app.BasePage">
<RelativeLayout
android:id="#+id/containerBase"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<fragment
android:id="#+id/navigation_drawer"
android:layout_width="#dimen/navigation_drawer_width"
android:layout_height="match_parent"
android:layout_gravity="start"
android:name="app.NavigationBase" />
</android.support.v4.widget.DrawerLayout>
User navigates through current fragments by setting mPage and calling setPage().
Now I need to modify the fragment X thus it has several subpages, whereas the fragment Y must stay as it is. Programmatically the classes FragmentX and FragmentY are identical, they display solely different user contents.
I comprehend that ViewPager must be a right solution in this scenario. My problem is I do not see where to build the ViewPager in my current code...
I am currently re-coding most of the back end of my android app in order to follow the design guidelines more closely. Currently I am using all activities and zero fragments. I am trying to switch to fragments in order to use the slide out navigation draw and eventually some sliding tabs.
For navigation right now I have this drop down menu which when an item is clicked launches a new activity:
The "Your Statistics" activity is kind of like the home page, where the user will enter the app too. I also want the user to be able to get back to that "page" from anywhere in the app.
My activity that I plan to run the draw from I have a draw layout called fragment_main:
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<FrameLayout
android:id="#+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
<ListView
android:id="#+id/drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#FFF"
android:choiceMode="singleChoice"/>
</android.support.v4.widget.DrawerLayout>
and my activity which loads the drawer layout is:
public class MainDraw extends FragmentActivity {
final String[] data ={"one","two","three"};
final String[] fragments ={
"com.beerportfolio.beerportfoliopro.FragmentOne",
"com.beerportfolio.beerportfoliopro.FragmentTwo",
"com.beerportfolio.beerportfoliopro.FragmentThree"};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_main);
//todo: load statistics
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActionBar().getThemedContext(), android.R.layout.simple_list_item_1, data);
final DrawerLayout drawer = (DrawerLayout)findViewById(R.id.drawer_layout);
final ListView navList = (ListView) findViewById(R.id.drawer);
navList.setAdapter(adapter);
navList.setOnItemClickListener(new AdapterView.OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> parent, View view, final int pos,long id){
drawer.setDrawerListener( new DrawerLayout.SimpleDrawerListener(){
#Override
public void onDrawerClosed(View drawerView){
super.onDrawerClosed(drawerView);
FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
tx.replace(R.id.main, Fragment.instantiate(MainDraw.this, fragments[pos]));
tx.commit();
}
});
drawer.closeDrawer(navList);
}
});
FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
tx.replace(R.id.main,Fragment.instantiate(MainDraw.this, fragments[0]));
tx.commit();
}
}
IN my //todo: comment I should load my first "home" fragment there which is my statistics "page" ? And then all the other fragments will be transitioned in and out based on the draw clicks?
Thanks for your help in advance, I want to make sure I am doing this right, I used to code just to get things working which is why I am now re doing a huge chunk of my code. Please share any other fragment tips to that I might need!
First of all read the well written documentation, it answers to your doubts.
I would share my personal pattern to convert existing Activity to Fragment
Create your on abstract Fragment class from which derive all drawer fragments, this can help to group common attributes
Use a method like selectItem() on docs, it helps to explicit do a call at first run (showing the "home" fragment) and then from onItemClick
move inflating XML layout from Activity.onCreate() code to Fragment.onCreateView() (ie setContentView to inflater.inflate(R.layout.my_layout, container, false), in many cases you can copy all code from onCreate() to onCreateView
move initialization code from Activity.onCreate() to Fragment.onActivityCreated(), this is very useful when both Activity (including fragment) and the direct Fragment exist, for example if your app exposes a "Share with" action you continue to have the Activity that inside the XML includes a <fragment/> and the fragment can be created from the drawer, too
if you need to communicate from Activity to Fragment and viceversa I suggest to create an interface and store it inside the 'onAttach()' (see google example)
Action bar items must be hidden when drawer is open, again take a look at example used in doc, here is very useful the interface to communicate from activity to fragment, the main activity can tell if drawer is open and the fragment can call the interface
public interface FragmentActivityStatus {
public boolean isDrawerOpen();
}
The activity
public class MainActivity extends Activity implements FragmentActivityStatus {
#Override
public boolean isDrawerOpen() {
return drawerLayout.isDrawerOpen(drawerList);
}
}
The fragment
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
fragmentActivityStatus = (FragmentActivityStatus)activity;
}
#Override
public void onPrepareOptionsMenu(Menu menu) {
boolean isMenuVisible = !fragmentActivityStatus.isDrawerOpen();
menu.findItem(R.id.my_menu).setVisible(isMenuVisible);
super.onPrepareOptionsMenu(menu);
}
Not related to fragment, in your code you declare class names as string, consider to create a Class array if you refactor packages the code continue to work, then you can call the Class.getName() to obtain the string to pass to Fragment.instantiate()
final Class<?>[] fragments = {
FragmentOne.class,
FragmentTwo.class,
FragmentThree.class};
Then
FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
tx.replace(R.id.main, Fragment.instantiate(MainDraw.this,
fragments[pos].getName()));
tx.commit();