I have a requirement for my app that a custom action bar view be shown from one fragment only (the landing page fragment). The problem is that this action bar is appearing when the user navigates to other fragments. Is there a way to do this without disabling custom view on every fragment?
Thanks
For this issue, only show actionbar for one fragment without show on all other fragments, solution could be very easy:
In your activity that hosts your fragments:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
... ...
// for API >= 11
getActionBar.hide();
// for API < 11
getSupportActionBar().hide();
... ...
}
Then in the fragment that you want to show actionbar:
#Override
public void onAttach(Activity activity){
activity.getActionBar().show(); //getSupportActionBar() for API<11
}
#Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
if (hidden) {
getActivity().getActionBar().hide(); //getSupportActionBar() for API<11
} else {
getActivity().getActionBar().show(); //getSupportActionBar() for API<11
}
}
Well, I'm just trying to suggest. Why not removing the action bar to all the fragments and just creating a single fragment (the landing page) with a custom action bar thru layout?
To remove ActionBar to all fragments, add this code to tag:
android:theme="#android:style/Theme.Light.NoTitleBar"
Create a Layout with action bar for your landing page through xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/rlMain"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true" >
<RelativeLayout
android:id="#+id/rlHeaderMain"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_alignParentTop="true"
android:background="#000000"
android:clipChildren="false"
android:clipToPadding="false"
android:padding="5dp" >
</RelativeLayout>
Related
My android app menu item is not clickable I Add app:showAsAction="always" and android:enabled="true" to the menu item and The Item Button is shown but it's not clickable, What Im missing here ?
App Theme : parent="Theme.MaterialComponents.DayNight.DarkActionBar"
Layout :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:theme="#style/ThemeOverlay.AppCompat.ActionBar"
android:orientation="vertical"
tools:context=".Settings.ReportTable">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/colorPrimary"
android:id="#+id/toolbar"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/balance_table_SV"/>
</LinearLayout>
Activity :
public class ReportTable extends AppCompatActivity {
ScrollView balance_table_SV;
TableLayout tableLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_report_table);
Toolbar toolbar = findViewById(R.id.toolbar);
toolbar.inflateMenu(R.menu.excel);
balance_table_SV = findViewById(R.id.balance_table_SV);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.excel, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId() == R.menu.excel) {
export_to_excel();
}
return super.onOptionsItemSelected(item);
}
}
First of all, it looks like you're comparing the item ID to the resource ID of the entire menu (R.menu.excel). You should have an ID for each menu item to use for click handling:
<item android:id="#+id/excel" />
if(item.getItemId() == R.id.excel) { ... }
Secondly there seems to be quite a bit of confusion here between the Toolbar and the Android action bar. You are you using a mixture of both in the code you have provided which is causing the inconsistency.
Firstly, if you're using a Toolbar in your layout, you don't need the action bar at all, therefore you should be inheriting from a NoActionBar Material Components theme (such as Theme.MaterialComponents.DayNight.NoActionBar). Then in order to setup your Toolbar you have two options:
Set the Toolbar as the action bar
With this approach you can register the Toolbar to act as the action bar for an activity, which means it correctly triggers the onOptionsItemSelected callback etc. So to do this we need to register it in onCreate:
setSupportActionBar(toolbar);
Then you should probably be using onCreateOptionsMenu to inflate the menu instead of Toolbar.inflateMenu just as you've already done in your code:
getMenuInflater().inflate(R.menu.excel, menu);
And finally to listen for menu item clicks, you can override onOptionsItemSelected and check the item ID as I outlined above.
Setup the Toolbar independently
Instead of registering the Toolbar as the action bar, you can instead just use the Toolbar like any regular view and set it up directly. Firstly to inflate the menu you can call:
toolbar.inflateMenu(R.menu.excel);
Then in order to listen for click events you can attach an OnMenuItemClickListener as follows:
toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem item) {
...
}
});
Both approaches are valid but I personally prefer the second. It seems strange to try and work around the activity lifecycle when you could just treat the Toolbar normally.
I have MainActivity as the host of my Navigation Controller, it has toolbar and bottom navigation view
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:theme="?attr/actionBarTheme"
android:minHeight="?attr/actionBarSize"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<fragment
android:id="#+id/nav_host_fragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/bottom_nav"
app:layout_constraintTop_toBottomOf="#+id/toolbar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="#navigation/navigation_graph"
app:defaultNavHost="true"
/>
<android.support.design.widget.BottomNavigationView
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:background="#color/colorPrimary"
app:itemIconTint="#color/color_bottom_view_navigation"
app:itemTextColor="#color/color_bottom_view_navigation"
app:menu="#menu/menu_bottom_view"
app:labelVisibilityMode="labeled"
android:id="#+id/bottom_nav"/>
</android.support.constraint.ConstraintLayout>
it will host some fragments as the menu for bottom navigation view like HomeFragment, OrderFragment, FavouriteFragment, CartFragment, ProfileFragment.
like this :
let say there is logOut button in the HomeFragment, and if it is clicked then it will move to Login screen. as usual, the login screen or sign up screen doesn't have bottom navigation view and also doesn't have toolbar.
so what is the best way to remove that bottom navigation view and also the toolbar if using navigation controller ?
I have tried to use <Include> tag in my navigation controller graph,
so I make two navigation graph, then I make 2 activity to place fragment as the host. the first activity has bottom navigation view and toolbar (MainActivity, like the xml I share above) and the other activity doesn't have bottom navigation view and toolbar
the navigation graph are like the image below:
MainActivity as nav host fragment
AuthActivity as nav host fragment
but when I move from HomeFragment (that has logout button) to LoginFragment using this code:
logout_button.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.action_toAuthActivity)
}
but in login screen the bottom navigation view and the toolbar is still there
I assume the auth_graph (AuthActivity as the host) can be used to host some screen that doesn't have bottom navigation view and toolbar like login screen, sign up screen or forgot password screen.
but....I can't remove that bottom navigation view and the toolbar using this way
so how to remove bottom navigation view and toolbar in some fragments if using navigation controller ?
More concise is to use a navigationlistener. This way you only need 1 function in your MainActivity and no code in all the fragments where you want to hide the bottomnavigation or any other UI element (like toolbar). Place this function inside your onCreate from your MainActivity.
My function looks like this:
private fun visibilityNavElements(navController: NavController) {
navController.addOnDestinationChangedListener { _, destination, _ ->
when (destination.id) {
R.id.about_fragment,
R.id.settings_fragment,
R.id.detail_fragment,
R.id.missionPhotoFragment -> bottom_nav?.visibility = View.GONE
else -> bottom_nav?.visibility = View.VISIBLE
}
}
}
I use Kotlin Android Extensions to directly access views by there id (no findViewById needed).
It seems that there's no simple solution currently implemented in NavigationUI.
What I ended up doing is adding a hideBottomBar method in MainActivity like so:
#Override
protected void onCreate(Bundle savedInstanceState) {
// ...
hideBottomBar(false); // to have it visible by default
}
public void hideBottomBar(boolean isHidden){
bottomBar.setVisibility(isHidden ? View.GONE : View.VISIBLE);
}
And then, in the fragments where the bottom bar needs to be hidden:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// layout inflating and stuff...
MainActivity activity = (MainActivity) getActivity();
if (activity != null)
activity.hideBottomBar(true);
return view;
}
#Override
public void onDestroyView() {
super.onDestroyView();
MainActivity activity = (MainActivity) getActivity();
if (activity != null)
activity.hideBottomBar(false); // to show the bottom bar when
// we destroy this fragment
}
You can access parent view's id and hide the bottom navigation by setting its visibility to gone and again have to make it visible in On Destroy view
BottomNavigationView navView;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View root = inflater.inflate(R.layout.fragment_add_standard, container, false);
navView = container.getRootView().findViewById(R.id.nav_view);
navView.setVisibility(View.GONE);
return root;
}
#Override
public void onDestroyView() {
super.onDestroyView();
navView.setVisibility(View.VISIBLE);
}
I'll be honest, I just read the title to this question but.. Can't you just toggle the visibility? Put this in your MainActivity.
fun toggleBottomNavigation(visible: Boolean) {
bottomNavigationView.visibility = if (visible) {
View.VISIBLE
} else {
View.GONE
}
}
and do the same for the toolbar.
I have a DrawerLayout connected to the ActionBar with an ActionBarDrawerToggle that stops working. I know the question about why it doesn't open has been asked 100 times but my question is a little different. My drawer works! Yep, you click on the hamburger and the drawer opens and closes like a champ...
The problem is that it stops working when I dismiss any fragment that was added to the activity. Here is the activity layout
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/renderer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="#+id/activity_root_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
...header stuff...
<ViewStub
android:id="#+id/view_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"/>
...footer stuff...
</RelativeLayout>
<FrameLayout
android:id="#+id/left_drawer"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"/>
</android.support.v4.widget.DrawerLayout>
Then the activity contains this...
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
mRendererLayout = (DrawerLayout) findViewById(R.id.renderer_layout);
mDrawerToggle = new ActionBarDrawerToggle(
this,
mRendererLayout,
R.string.alert_ok,
R.string.cancel_all
) {
#Override
public void onDrawerOpened(View drawerView) {
Log.e("===", "RA.Drawer Opened ");
}
#Override
public void onDrawerClosed(View drawerView) {
Log.e("===", "RA.Drawer Closed ");
}
};
mRendererLayout.addDrawerListener( mDrawerToggle );
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
Log.e("===", "RA.onOptionsItemSelected(): ");
if (mDrawerToggle.onOptionsItemSelected(item)) {
return true;
} else {
return super.onOptionsItemSelected(item);
}
}
At a few points I add various fragments to the activity with
addFragment( new MyFragment() );
Each fragment has a view with an onClick() that calls
getActivity().getSupportFragmentManager().popBackStack();
It is at this point that the drawer stops working. The fragment disappears as expected and it does log "RA.onOptionsItemSelected()" when the hamburger is clicked but "RA.Drawer Opened" is no longer logged and the drawer does not open. This continues until the activity is relaunched.
To make things more complicated the activity uses a ViewStub to swap out two layouts: either a FrameLayout or a FrameLayout containing a ViewPager. Only the latter has the problem. I swap out the viewStub like this...
ViewStub viewStub = findViewById(R.id.view_stub);
viewStub.setLayoutResource(R.layout.either_layout_here);
viewStub.inflate();
Swapping in this layout has no problems with the drawer. It gets a fragment added to it via addFragment():
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
Swapping in this layout, on the other hand, has the problem...
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ViewPager
android:id="#+id/view_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</FrameLayout>
This one does not get a fragment added to its content_frame (yet), since the ViewPager handles its fragments. That's really the only difference. For some reason popping the fragment back stack breaks the drawer button on the ActionBar.
I'm stumped. Help!
If I dynamically add a Fragment (using the FragmentManager) into a container defined in a landscape XML then switch to portrait, that dynamically injected Fragment still exists. It is contributing to the Action Bar even though it is not visible. What is a good way / design to prevent this from happening?
I have tried using isVisible in onCreateOptionsMenu of the Fragment but that causes issues on some Android versions because onCreateOptionsMenu is called before onCreateView which results in false even if the fragment is going to be visible with the current configuration.
Note: I am not handling the configuration myself. I haven't specified configChanges in the manifest and I am not overriding onConfigurationChanged.
Activity:
// inject detail fragment
Fragment detailFragment = getSupportFragmentManager().findFragmentById(R.id.detail_container);
if(detailFragment == null)
getSupportFragmentManager().beginTransaction().replace(R.id.detail_container, DetailFragment.newInstance(id)).commit();
// inject master fragment
if(findViewById(R.id.master_container) != null) {
masterDetail = true;
Fragment listFragment = getSupportFragmentManager().findFragmentById(R.id.master_container);
if(listFragment == null)
getSupportFragmentManager().beginTransaction().replace(R.id.master_container, ListFragment.newInstance(position)).commit();
}
Activity portrait XML
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Activity landscape XML
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:baselineAligned="false">
<FrameLayout
android:id="#+id/master_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
<FrameLayout
android:id="#+id/detail_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"/>
</LinearLayout>
The model I'd follow based on your explanation.
public class MyActivity extends Activity {
boolean mMultiPane = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FrameLayout masterContainer = (FrameLayout) findViewById(R.id.master_container);
if (masterContainer != null)
mMultiPane = true;
...
}
}
From then on in your Activity you can use the mMultiPane variable to change the behaviour including changing how the options menu is set up (just have two different menu.xml files or add / remove menu items depending on which mode you're in).
The question says it all.
I dont know if fragments can be included inside Navigation Drawer or not.
I am more interested to make contextual layout.
The Navigation drawer in Google play and YouTube app is not just a simple drawer.
I dont know if fragments can be included inside Navigation Drawer or not
Yes, they can be integrated just as in any valid part of the layout. Just put in the drawer a ViewGroup (FrameLayout, LinearLayout, etc) and instruct the FragmentManager to put a fragment in that view group by giving the layout id in add or replace method.
I am not sure what you mean by Contextual Layout, but I've checked the Google Play and Youtube apps and in their drawer layout it doesn't look impossible to have fragments inside.
EDIT: Below is some basic example with a drawer having a fragment. I guess you know about this developer article with how to customize the drawer in conjunction with the action bar.
drawer_main.xml:
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!-- The main content view -->
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- The navigation drawer -->
<FrameLayout
android:id="#+id/left_drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start" />
</android.support.v4.widget.DrawerLayout>
MainActivity.java:
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.drawer_main);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.left_drawer, getDrawerFragment()).commit();
}
}
private Fragment getDrawerFragment() {
return new DrawerFragment();
}
}
DrawerFragment.java:
public class DrawerFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.drawer_fragment_layout, container, false);
}
public void onViewCreated(View view, Bundle savedInstanceState) {
ListView listView = (ListView) view.findViewById(R.id.main_list_view);
listView.setAdapter(new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_1, new String[] {
"Adam", "Diana", "John"
}));
}
}
fragment's layout drawer_fragment_layout.xml:
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/main_list_view"
android:background="#ffffff"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Makes sense?
You can use fragment or just a layout. Navigation drawer has to child layout. First one is main, second is drawer. These two child layout can be just a layout or a fragment like youtube ext. You can find all basic information in here.