MvxPreferenceFragmentCompat with Up Navigation (back arrow) in ActionBar - android

I am trying to implement a settings view invoked from a drawer menu.
Settings view is implemented using a Fragment inheriting from MvxPreferenceFragmentCompat. Code is below:
[MvxFragmentPresentation(typeof(MainViewModel), Resource.Id.main_content_frame)]
[Register(nameof(SettingsFragment))]
public class SettingsFragment : MvxPreferenceFragmentCompat<SettingsViewModel>
{
public override void OnCreatePreferences(Bundle savedInstanceState, string rootKey)
{
SetPreferencesFromResource(Resource.Xml.preferences, rootKey);
}
}
My preferences.xml is shown below:
<?xml version="1.0" encoding="utf-8" ?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="#string/pref_debug_info_title"
android:key="pref_key_debug_info">
<CheckBoxPreference
android:key="pref_key_provide_debug_info"
android:title="#string/pref_title_provide_debug_info"
android:summary="#string/pref_summary_provide_debug_info"
android:defaultValue="false" />
<CheckBoxPreference
android:key="pref_key_provide_debug_info_over_wifi"
android:title="#string/pref_title_debug_info_over_wifi"
android:summary="#string/pref_summary_debug_info_over_wifi"
android:defaultValue="true" />
</PreferenceCategory>
</PreferenceScreen>
Fragment layout is below:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:id="#+id/fragment_frame"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
layout="#layout/include_toolbar_actionbar_addlistitem" />
<fragment
android:name="SettingsFragment"
android:id="#+id/settings_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<include
layout="#layout/include_floatingactionbutton" />
</android.support.design.widget.CoordinatorLayout>
And finally, main activity XML is here:
<?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">
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/main_content_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true" />
<FrameLayout
android:id="#+id/navigation_frame"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:layout_gravity="left|start" />
</android.support.v4.widget.DrawerLayout>
My preferences show up fine but I don't see the AppBar with a title (that I could set) and there is no arrow to navigate back to the Activity. When I press the back "hardware button" once, nothing happens. When I press it the second time, the app "minimizes" and when I bring it back, I am back at the main activity.
I have done this functionality with "regular" Fragments inheriting from MvxFragment but it seems like I am unable to do so with MvxPreferenceFragmentCompat.
Update 1
Based on input from Trevor, I have update fragment_settings.xml and SettingsFragment.cs as shown below:
fragment_settings.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:id="#+id/fragment_frame"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
layout="#layout/include_toolbar_actionbar" />
<!-- Required ViewGroup for PreferenceFragmentCompat -->
<FrameLayout
android:id="#android:id/list_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
local:layout_behavior="#string/appbar_scrolling_view_behavior"/>
<include
layout="#layout/include_floatingactionbutton" />
</android.support.design.widget.CoordinatorLayout>
SettingsFragment.cs
[MvxFragmentPresentation(typeof(MainViewModel), Resource.Id.main_content_frame)]
[Register(nameof(SettingsFragment))]
public class SettingsFragment : MvxPreferenceFragmentCompat<SettingsViewModel>, View.IOnClickListener
{
#region properties
protected Toolbar Toolbar { get; set; }
protected AppCompatActivity ParentActivity { get; set; }
#endregion
#region Fragment lifecycle overrides
public override void OnCreatePreferences(Bundle savedInstanceState, string rootKey)
{
AddPreferencesFromResource(Resource.Xml.preferences);
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var view = base.OnCreateView(inflater, container, savedInstanceState);
ParentActivity = ((MainActivity)Activity);
Toolbar = view.FindViewById<Toolbar>(Resource.Id.main_tool_bar);
ParentActivity.SetSupportActionBar(Toolbar);
ParentActivity.SupportActionBar.SetDisplayHomeAsUpEnabled(true);
ParentActivity.SupportActionBar.SetDisplayShowHomeEnabled(true);
...
// TODO: Pull it from a resource
Toolbar.Title = Resources.GetText(Resource.String.settings_view_title);
return view;
}
#endregion
#region View.IOnClickListener implementation
public void OnClick(View v)
{
ParentActivity.OnBackPressed();
}
#endregion
}
But I am still getting null on line ParentActivity.SupportActionBar.SetDisplayHomeAsUpEnabled(true) because my SupportActionBar is null (but Toolbar is not). How come?
Update 2
OK, I got a bit further. I got my AppBar and arrow back and the look and feel is like I have expected it. Still have a problem but more about it below.
What helped? This post.
In short, I needed to declare a custom style for my preference and in it, reference the layout of my preference view. Here are the bit:
Styles.xml
<style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
...
<item name="preferenceTheme">#style/AppTheme.Preference</item>
</style>
<!-- Custom Preference Theme -->
<style name="AppTheme.Preference"
parent="#style/PreferenceThemeOverlay.v14.Material">
<item name="preferenceFragmentCompatStyle">
#style/AppPreferenceFragmentCompatStyle
</item>
</style>
<!-- Custom Style for PreferenceFragmentCompat -->
<style name="AppPreferenceFragmentCompatStyle"
parent="#style/PreferenceFragment.Material">
<item name="android:layout">#layout/fragment_settings</item>
</style>
For me, this has been the missing link between the view declaration in the SettingsFragment and the layout resource.
The only problem that remains is ability to go back from this fragment back to main activity. From a "regular" MvxFragment (that is invoked from a drawer), I use OnClick method of the IOnClickListener to call ParentActivity.OnBackPressed().
SettingsFragment.cs
public class SettingsFragment : MvxPreferenceFragmentCompat<SettingsViewModel>, View.IOnClickListener
{
...
public void OnClick(View v)
{
ParentActivity.OnBackPressed();
}
}
But this does not work from this MvxPreferenceFragmentCompat. Still searching for that answer.
Update 3
Last missing piece of the puzzle was the AddToBackStack declaration atop the class as shown below. Once this was in place, OnBackPressed() worked correctly.
SettingsFragment.cs
[MvxFragmentPresentation(typeof(MainViewModel), Resource.Id.main_content_frame, AddToBackStack = true)]
[Register(nameof(SettingsFragment))]
public class SettingsFragment : MvxPreferenceFragmentCompat<SettingsViewModel>, View.IOnClickListener

You're missing some code to setup the AppCompatActivity.SupportActionBar like you would in any other fragment. Here is how I've solved the problem:
Here is the SettingsFragment Layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
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:id="#+id/coordinator"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:fitsSystemWindows="true"
tools:context=".Activities.MainActivity">
<include
layout="#layout/toolbar_actionbar" />
<!-- Required ViewGroup for PreferenceFragmentCompat -->
<FrameLayout
android:id="#android:id/list_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>
Here is the SettingsFragment implementation:
public class SettingsFragment : MvxPreferenceFragmentCompat<SettingsViewModel>,
View.IOnClickListener
{
private Toolbar _toolbar;
private View _view;
private MainActivity MainActivity => (MainActivity)Activity;
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Since the view is created in PreferenceFragmentCompat's OnCreateView we don't use BindingInflate like a typical MvxFragment.
_view = base.OnCreateView(inflater, container, savedInstanceState);
// TODO: Setup MvvmCross Databinding manually since we didn't use BindingInflate like a typical MvxFragment.
_toolbar = _view.FindViewById<Toolbar>(Resource.Id.toolbar);
if (_toolbar != null)
{
MainActivity.SetSupportActionBar(_toolbar);
MainActivity.SupportActionBar.SetDisplayHomeAsUpEnabled(true);
MainActivity.SupportActionBar.SetDisplayShowHomeEnabled(true);
_toolbar.SetNavigationOnClickListener(this);
// TODO: Bind the Toolbar.Title
}
return _view;
}
public override void OnCreatePreferences(Bundle savedInstanceState, string rootKey)
{
AddPreferencesFromResource(Resource.Xml.preferences);
}
public async void OnClick(View v)
{
// Toolbar was clicked
await ViewModel.CloseCommand.ExecuteAsync().ConfigureAwait(false);
}
}

Related

How do I add a functional edit icon for a profile picture in Android Studio?

So I have a fragment that gives the layout for a profile edit section and I have an image that represent the profile picture of a particular user and I want to efficiently add an edit icon on bottom right corner of the picture for user to select, which allows them to change a new picture.
And I want some help in doing so.
fragment_profile_setting.xml :
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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=".ProfileSettingFragment">
<de.hdodenhof.circleimageview.CircleImageView
android:id="#+id/profile_picture_edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="#drawable/profilepic"
app:civ_border_color="#color/greyblack"
app:civ_border_width="4dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
As for the java code it contains only the methods that inflates the fragment.
ProfileSettingFragment.java :
package com.teamrocket.blooddonationcommunity;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class ProfileSettingFragment extends Fragment {
public ProfileSettingFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_profile_setting, container, false);
}
}
Do flag this if duplicated and drop a link.
You can basically add another ImageView. Add a click listener to that ImageView. So, users will be able to edit profile picture or add new one.
You can update the layout file as below:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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=".ProfileSettingFragment" >
<com.google.android.material.imageview.ShapeableImageView
android:id="#+id/profile_picture"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_margin="10dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:shapeAppearanceOverlay="#style/ImageView.Circle"
app:srcCompat="#drawable/profile_picture" />
<ImageView
android:id="#+id/edit_profile_picture_button"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginEnd="4dp"
android:layout_marginBottom="4dp"
android:background="#drawable/bg_circle"
android:padding="8dp"
android:src="#drawable/ic_edit_24"
app:layout_constraintBottom_toBottomOf="#id/profile_picture"
app:layout_constraintEnd_toEndOf="#id/profile_picture"
app:tint="#color/white" />
</androidx.constraintlayout.widget.ConstraintLayout>
With ShapeableImageView, you can basically build circle image view without any third party also.
And you can add below style to your styles.xml. Thus, your ShapeableImageView will be as circle.
<style name="ImageView.Circle" parent="">
<item name="cornerSize">50%</item>
</style>
I also added bg_circle drawable to make edit_profile_picture_button circle. Because ShapeableImageView doesn't support padding, if you would like to add padding into that edit_profile_picture_button for better UI.
Here is the simple code of that drawable file.
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#android:color/holo_red_dark" />
</shape>
With ViewBinding or DataBinding, you can interact with your views easier. I am giving an example with ViewBinding, and you can find more info about them in here:
ViewBinding: https://developer.android.com/topic/libraries/view-binding
DataBinding: https://developer.android.com/topic/libraries/data-binding
Basically add this to your app(module) level gradle as shown in the developer website.
android {
...
buildFeatures {
viewBinding true
}
}
And update your ProfileSettingFragment:
public class ProfileSettingFragment extends Fragment {
private FragmentProfileSettingBinding binding;
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentProfileSettingBinding.inflate(inflater);
return binding.getRoot();
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.editProfilePictureButton.setOnClickListener(editProfilePictureButton -> {
// Update the profile picture or add new one
});
}
}

Navigation Drawer in bottomappbar android

class BottomNavigationDrawerFragment: BottomSheetDialogFragment(),
NavigationView.OnNavigationItemSelectedListener {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_bottomsheet, container, false)
}
override fun onNavigationItemSelected(item : MenuItem): Boolean {
// Bottom Navigation Drawer menu item clicks
when (item.itemId) {
R.id.nav1 -> context!!.toast("oneeeeee")
R.id.nav2 -> context!!.toast("twoooooo")
R.id.nav3 -> context!!.toast("threeeee")
return true
}
// Add code here to update the UI based on the item selected
// For example, swap
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
navigation_view.setNavigationItemSelectedListener(this)
// Add code here to update the UI based on the item selected
// For example, swap
}
}
// This is an extension method for easy Toast call
fun Context.toast(message: CharSequence) {
val toast = Toast.makeText(this, message, Toast.LENGTH_SHORT)
toast.setGravity(Gravity.BOTTOM, 0, 600)
toast.show()
}
What I want to achieve is something given in image. I want to make a navigation drawer in bottom app bar. Above code doesn't work and it tells unresolved reference type setNavigationItemSelectedListener. What is the error in my code?
you should add a drawer icon in your bottomAppbar, then use a bottomsheet for the drawer.
for your drawer you have two choices:
1- go with google standards and add drawer items in menu folder (seems you dont want this)
2- replace a fragment in your bottom sheet, in this way you can customize your fragment and do whatever you like
------------------- replace a fragment in your bottom sheet -------------
your activity.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:custom="http://schemas.android.com/tools"
android:id="#+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layoutDirection="ltr"
android:background="#color/white"
android:orientation="vertical">
<FrameLayout
android:id="#+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:elevation="6dp"
android:visibility="visible"
app:layout_behavior="#string/bottom_sheet_behavior">
<FrameLayout
android:id="#+id/menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" />
</FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
your Activity.java
public class Activity extends AppCompatActivity implements
FragmentNavigation.OnFragmentInteractionListener {
private CoordinatorLayout coordinatorLayout;
private View bottomSheet;
private BottomSheetBehavior<View> behavior;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
FrameLayout bottomSheetLayout = (FrameLayout)
findViewById(R.id.menu);
FragmentNavigation fragmentNavigation = new FragmentNavigation();
androidx.fragment.app.FragmentTransaction fragmentTransaction =
getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(bottomSheetLayout.getId(),
fragmentNavigation, "k");
fragmentTransaction.commit();
coordinatorLayout = (CoordinatorLayout)
findViewById(R.id.main_content);
bottomSheet = coordinatorLayout.findViewById(R.id.bottom_sheet);
behavior = BottomSheetBehavior.from(bottomSheet);
}
#Override
public void onFragmentInteraction(Uri uri) {
}
}
your fragment navigation
public class FragmentNavigation extends androidx.fragment.app.Fragment {
private String descriptions;
public FragmentNavigation () {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View view = inflater.inflate(R.layout.fragment_navigation, container, false);
return view;
}
}
your fragment_navigation.xml
<?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:custom="http://schemas.android.com/tools"
android:id="#+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white"
android:orientation="vertical">
<TextView
android:id="#+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:elevation="6dp"
android:visibility="visible"
android:text="here is the navigation menu"
app:layout_behavior="#string/bottom_sheet_behavior"/>
</LinearLayout>
See this code it has a navigationIcon attribute but you can use as a bottom app bar. If you need navigation drawer on this click then you have to customize by our own.
<com.google.android.material.bottomappbar.BottomAppBar
android:id="#+id/bottom_app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:elevation="5dp"
android:elevation="5dp"
app:fabAttached="true"
app:fabCradleDiameter="0dp"
app:backgroundTint="#color/colorPrimary"
app:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
app:fabAlignmentMode="center"
app:menu="#menu/bottom_bar_menu"/>
In res>menu>bottom_bar_menu, change showAsAction to always or ifRoom, put an icon for action_settings and remove orderInCategory
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/action_settings"
android:title="#string/action_settings"
android:showAsAction="always"
android:icon="" />
<item
android:title="#string/search"
android:id="#+id/search"
android:icon="#drawable/ic_search_black_24dp"
android:showAsAction="always" />
<item
android:id="#+id/app_bar_archieve"
android:icon="#drawable/ic_bottom_bar_hamburger" // navigation icon
android:title="#string/action_archieve"
app:showAsAction="ifRoom"/>
</menu>
in java :
BottomAppBar bar = (BottomAppBar) findViewById(R.id.bar);
setSupportActionBar(bar);
bar.setNavigationOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// Handle the navigation click by showing a BottomDrawer etc.
}
});
bar.setOnMenuItemClickListener(new OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem item) {
// Handle actions based on the menu item
return true;
}
});
Reference Link : https://material.io/develop/android/components/bottom-app-bar/
bottom_bar.replaceMenu(R.menu.bottomappbar_menu)
bottom_bar.setOnMenuItemClickListener {
when (it.itemId) {
R.id.app_bar_copy -> {
}
R.id.app_bar_fav -> {
}
R.id.app_bar_tra -> {
}
else -> {
}
}
true
}
Just add code in fragment to handle menu item.

How to make DrawerMenu in the versions of MvvmCross? v. 5.4.2

I'm using StarWarsEaxmple from MvvmCross repository and I can't make it work. The output of MvvmCross ,The problem in my opinion is either in different versions of MvvmCross or in Presenter. In Samples version is 5.1.1 and in my project is 5.4.2. And demonstrates weird behavior.
I can see empty drawer when I don't involve MvvmCross NavigationService. However, when I navigating to both ViewModels sequently (as in the example) I can see only Menu Page without drawer and other page frame is even doesn't invoked.
Reference to MvvmCross Sample
Main Activity
[Activity(Icon = "#drawable/icon",
Theme = "#style/AppTheme", LaunchMode = LaunchMode.SingleTop,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : MvxCachingFragmentCompatActivity<MainViewModel>
{
public DrawerLayout DrawerLayout;
//This method is invoked
protected override void OnCreate(Bundle bundle)
{ base.OnCreate(bundle);
SetContentView(Resource.Layout.activity_main);
DrawerLayout = FindViewById<DrawerLayout>(Resource.Id.drawerLayout);
ViewModel.ShowDefaultMenuItem();
}
....
Menu Fragment
[MvxFragment(typeof(MainViewModel), Resource.Id.navigationFrame)]
[Register("VacationManager.Droid.Activities.MenuFragment")]
public class MenuFragment : MvxFragment<MenuViewModel>, NavigationView.IOnNavigationItemSelectedListener
{
private NavigationView _navigationView;
private IMenuItem _previousMenuItem;
//This method is invoked too
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
base.OnCreateView(inflater, container, savedInstanceState);
var view = this.BindingInflate(Resource.Layout.menu_view, null);
_navigationView = view.FindViewById<NavigationView>(Resource.Id.navigation_view);
_navigationView.SetNavigationItemSelectedListener(this);
return view;
}
}
Main Part of Page
[MvxFragment(typeof(MainViewModel), Resource.Id.bodyFrame, false)]
[Register("VacationManager.Droid.Activities.VacationRequestListFragment")]
public class VacationRequestListFragment : BaseFragment<VacationRequestListViewModel> // You can find BaseFragment in sample
{
protected override int FragmentId => Resource.Layout.fragment_list;
//It is never invoked
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
base.OnCreateView(inflater, container, savedInstanceState);
return this.BindingInflate(Resource.Layout.fragment_list, container, false);
}
}
MainPage Layout
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:id="#+id/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/warning">
<!-- Center Side -->
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/mainFrame">
<include layout="#layout/toolbar" />
<FrameLayout
android:id="#+id/bodyFrame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"/>
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_margin="16dp"
android:src="#drawable/bullseye"
local:layout_anchor="#id/bodyFrame"
local:layout_anchorGravity="bottom|right|end" />
</android.support.design.widget.CoordinatorLayout>
<!-- Left Side -->
<FrameLayout
android:id="#+id/navigationFrame"
android:layout_height="match_parent"
android:layout_width="240dp"
android:layout_gravity="left|start"
android:clickable="true" />
</android.support.v4.widget.DrawerLayout>
MainPage Layout
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.NavigationView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:id="#+id/navigation_view"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:layout_gravity="start"
android:fitsSystemWindows="true"
android:theme="#style/ThemeToolbarNavigationView"
android:background="#color/colorPrimary"
local:itemTextColor="#color/light_gray"
local:itemIconTint="#color/light_gray"
local:headerLayout="#layout/navigation_header"
local:menu="#menu/navigation_drawer" />
Main ViewModel
public void ShowDefaultMenuItem()
{
NavigationService.Navigate<VacationRequestListViewModel>();
NavigationService.Navigate<MenuViewModel>();
}
Seems I'm losing small detail... Any help would be appreciated.
The problem was first of all in namespaces of attributes over the activities. They should be MvvmCross.Droid.Views.Fragments . And also instead of MvxFragmentAttribute we need to use MvxFragmentPresentationAttribute. Then it works.

ActionBar and Toolbar in a fragment

I am struggling to get my fragment child views to take ActionBar/Toolbar into account when displaying.
I have followed a sample from here which has very nicely partitioned the layout into activity layout and fragments with include tags for AppBar/Toolbar and, in my case, floating action button (fab). I have refactored my already working code from having the AppBar/Toolbar and fab on the activity layout with just the fragment being in a separate layout. But the approach of including the AppBar/Toolbar and fab on the fragment gives me the ability to have a clean activity which can accomodate any fragment, with or without AppBar/Toolbar or fab (or any other UI elements). Below is my basic layout setup. The problem I am straggling with is that my RecyclerView is obscured on the the by the Appbar/Toolbar.
activity_main.axml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/main_coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<!-- Actual content of the screen -->
<FrameLayout
android:id="#+id/main_content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true" />
</android.support.design.widget.CoordinatorLayout>
fragment_list.axml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/list_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<include
layout="#layout/include_toolbar_actionbar" />
<MvvmCross.Droid.Support.V4.MvxSwipeRefreshLayout
android:id="#+id/listsRefresher"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:MvxBind="Refreshing IsLoading; RefreshCommand ReloadCommand">
<MvvmCross.Droid.Support.V7.RecyclerView.MvxRecyclerView
android:id="#+id/listsRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:MvxItemTemplate="#layout/item_list"
app:MvxBind="ItemsSource Lists; ItemClick ShowListItemsCommand" />
</MvvmCross.Droid.Support.V4.MvxSwipeRefreshLayout>
<include
layout="#layout/include_floatingactionbutton" />
</FrameLayout>
include_toolbar_actionbar.axml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.AppBarLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/main_app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.ActionBar">
<android.support.v7.widget.Toolbar
android:id="#+id/main_tool_bar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.ToolBar"
app:layout_scrollFlags="scroll|enterAlways" />
</android.support.design.widget.AppBarLayout>
include_floatingactionbutton.axml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.FloatingActionButton
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_margin="#dimen/margin_medium"
android:src="#drawable/ic_add_white_24dp"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
app:layout_anchor="#id/main_content_frame"
app:layout_anchorGravity="bottom|right|end" />
MainActivity.cs
namespace List.Mobile.Droid.Activities
{
[Activity(
Label = "#string/applicationName",
Icon = "#drawable/ic_icon",
Theme = "#style/AppTheme.Default",
LaunchMode = LaunchMode.SingleTop,
ScreenOrientation = ScreenOrientation.Portrait,
Name = "list.droid.mobile.activities.MainActivity")]
public class MainActivity : MvxAppCompatActivity<MainViewModel>
{
...
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.activity_main);
ViewModel.ShowLists();
}
}
}
ListsFragment.cs
namespace List.Mobile.Droid.Views
{
[MvxFragmentPresentation(typeof(MainViewModel), Resource.Id.main_content_frame, true)]
[Register("list.mobile.droid.views.ListsFragment")]
public class ListsFragment : BaseFragment<ListsViewModel>, ActionMode.ICallback
{
...
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
base.OnCreateView(inflater, container, savedInstanceState);
var swipeToRefresh = FragmentView.FindViewById<MvxSwipeRefreshLayout>(Resource.Id.refresher);
if (AppBar != null)
AppBar.OffsetChanged += (sender, args) => swipeToRefresh.Enabled = args.VerticalOffset == 0;
var listsRecyclerView = FragmentView.FindViewById<MvxRecyclerView>(Resource.Id.listsRecyclerView);
...
return FragmentView;
}
}
}
BaseFragment.cs
namespace List.Mobile.Droid.Views
{
public abstract class BaseFragment<T> : MvxFragment<T> where T : MvxViewModel
{
protected abstract int FragmentResourceId { get; }
protected View FragmentView { get; set; }
protected AppBarLayout AppBar { get; set; }
protected FloatingActionButton Fab { get; set; }
protected Toolbar Toolbar { get; set; }
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var view = base.OnCreateView(inflater, container, savedInstanceState);
FragmentView = this.BindingInflate(FragmentResourceId, null);
AppBar = FragmentView.FindViewById<AppBarLayout>(Resource.Id.main_app_bar);
Fab = FragmentView.FindViewById<FloatingActionButton>(Resource.Id.fab);
Toolbar = FragmentView.FindViewById<Toolbar>(Resource.Id.main_tool_bar);
AppCompatActivity parentActivity = ((AppCompatActivity)Activity);
parentActivity.SetSupportActionBar(Toolbar);
parentActivity.SupportActionBar.SetDisplayHomeAsUpEnabled(false);
parentActivity.SupportActionBar.SetHomeButtonEnabled(false);
return view;
}
}
}
If you combine the layout structure of activity+fragment+include you'll see something like this:
CoordinatorLayout
--FrameLayout -> id=main_content_frame
----FrameLayout -> id=list_frame
------AppBarLayout
--------Toolbar
------SwipeRefresh
--------Recycler
------Fab
That means, you have the toolbar and swipe/recycler in a FrameLayout, and this one on top of the other is exactly the expected behavior.
To fix you should make the AppBar+Swipe+Fab as children of the CoordinatorLayout (which is the layout that properly handles interactions between Toolbar/Fab/Scrolling Content. So change your activity to be just the FrameLayout and re-order the fragment to be:
CoordinatorLayout
--AppBarLayout
----Toolbar
--SwipeRefresh
----RecyclerView
--Fab
and don't forget to add app:layout_behavior="#string/appbar_scrolling_view_behavior" to the SwipeRefreshLayout, that's for the Coordinator to properly position it.

Android new Bottom Navigation bar or BottomNavigationView

Saw the new guideline came out, and used in google photos latest app.
Have no idea how to use the new Bottom Navigation Bar.
See through the new support lib, didn't find any lead.
Can not find any official sample.
How to use the new Bottom bar? Don't want to do any customize.
I think you might looking for this.
Here's a quick snippet to get started:
public class MainActivity extends AppCompatActivity {
private BottomBar mBottomBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Notice how you don't use the setContentView method here! Just
// pass your layout to bottom bar, it will be taken care of.
// Everything will be just like you're used to.
mBottomBar = BottomBar.bind(this, R.layout.activity_main,
savedInstanceState);
mBottomBar.setItems(
new BottomBarTab(R.drawable.ic_recents, "Recents"),
new BottomBarTab(R.drawable.ic_favorites, "Favorites"),
new BottomBarTab(R.drawable.ic_nearby, "Nearby"),
new BottomBarTab(R.drawable.ic_friends, "Friends")
);
mBottomBar.setOnItemSelectedListener(new OnTabSelectedListener() {
#Override
public void onItemSelected(final int position) {
// the user selected a new tab
}
});
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mBottomBar.onSaveInstanceState(outState);
}
}
Here is reference link.
https://github.com/roughike/BottomBar
EDIT New Releases.
The Bottom Navigation View has been in the material design guidelines for some time, but it hasn’t been easy for us to implement it into our apps. Some applications have built their own solutions, whilst others have relied on third-party open-source libraries to get the job done. Now the design support library is seeing the addition of this bottom navigation bar, let’s take a dive into how we can use it!
How to use ?
To begin with we need to update our dependency!
compile ‘com.android.support:design:25.0.0’
Design xml.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Content Container -->
<android.support.design.widget.BottomNavigationView
android:id="#+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:itemBackground="#color/colorPrimary"
app:itemIconTint="#color/white"
app:itemTextColor="#color/white"
app:menu="#menu/bottom_navigation_main" />
</RelativeLayout>
Create menu as per your requirement.
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/action_favorites"
android:enabled="true"
android:icon="#drawable/ic_favorite_white_24dp"
android:title="#string/text_favorites"
app:showAsAction="ifRoom" />
<item
android:id="#+id/action_schedules"
android:enabled="true"
android:icon="#drawable/ic_access_time_white_24dp"
android:title="#string/text_schedules"
app:showAsAction="ifRoom" />
<item
android:id="#+id/action_music"
android:enabled="true"
android:icon="#drawable/ic_audiotrack_white_24dp"
android:title="#string/text_music"
app:showAsAction="ifRoom" />
</menu>
Handling Enabled / Disabled states. Make selector file.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_checked="true"
android:color="#color/colorPrimary" />
<item
android:state_checked="false"
android:color="#color/grey" />
</selector>
Handle click events.
BottomNavigationView bottomNavigationView = (BottomNavigationView)
findViewById(R.id.bottom_navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(
new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.action_favorites:
break;
case R.id.action_schedules:
break;
case R.id.action_music:
break;
}
return false;
}
});
Edit : Using Androidx you just need to add below dependencies.
implementation 'com.google.android.material:material:1.2.0-alpha01'
Layout
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:layout_gravity="bottom"
app:menu="#menu/bottom_navigation_menu"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</FrameLayout>
If you want to read more about it's methods and how it works read this.
Surely it will help you.
You should use BottomNavigationView from v25 Android Support Library.
It represents a standard bottom navigation bar for application.
Here is a post on Medium that has a step by step guide:
https://medium.com/#hitherejoe/exploring-the-android-design-support-library-bottom-navigation-drawer-548de699e8e0#.9vmiekxze
My original answer dealt with the BottomNavigationView, but now there is a BottomAppBar. I added a section at the top for that with an implementation link.
Bottom App Bar
The BottomAppBar supports a Floating Action Button.
Image from here. See the documentation and this tutorial for help setting up the BottomAppBar.
Bottom Navigation View
The following full example shows how to make a Bottom Navigation View similar to the image in the question. See also Bottom Navigation in the documentation.
Add the design support library
Add this line to your app's build.grade file next to the other support library things.
implementation 'com.android.support:design:28.0.0'
Replace the version number with whatever is current.
Create the Activity layout
The only special thing we have added to the layout is the BottomNavigationView. To change the color of the icon and text when it is clicked, you can use a selector instead of specifying the color directly. This is omitted for simplicity here.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.BottomNavigationView
android:id="#+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:menu="#menu/bottom_nav_menu"
app:itemBackground="#color/colorPrimary"
app:itemIconTint="#android:color/white"
app:itemTextColor="#android:color/white" />
</RelativeLayout>
Notice that we used layout_alignParentBottom to actually put it at the bottom.
Define the menu items
The xml above for Bottom Navigation View referred to bottom_nav_menu. This is what defines each item in our view. We will make it now. All you have to do is add a menu resource just like you would for an Action Bar or Toolbar.
bottom_nav_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/action_recents"
android:enabled="true"
android:icon="#drawable/ic_action_recents"
android:title="Recents"
app:showAsAction="ifRoom" />
<item
android:id="#+id/action_favorites"
android:enabled="true"
android:icon="#drawable/ic_action_favorites"
android:title="Favorites"
app:showAsAction="ifRoom" />
<item
android:id="#+id/action_nearby"
android:enabled="true"
android:icon="#drawable/ic_action_nearby"
android:title="Nearby"
app:showAsAction="ifRoom" />
</menu>
You will need to add the appropriate icons to your project. This is not very difficult if you go to File > New > Image Asset and choose Action Bar and Tab Icons as the Icon Type.
Add an item selected listener
There is nothing special happening here. We just add a listener to the Bottom Navigation Bar in our Activity's onCreate method.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BottomNavigationView bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.action_recents:
Toast.makeText(MainActivity.this, "Recents", Toast.LENGTH_SHORT).show();
break;
case R.id.action_favorites:
Toast.makeText(MainActivity.this, "Favorites", Toast.LENGTH_SHORT).show();
break;
case R.id.action_nearby:
Toast.makeText(MainActivity.this, "Nearby", Toast.LENGTH_SHORT).show();
break;
}
return true;
}
});
}
}
Need more help?
I learned how to do this watching the following YouTube video. The computer voice is a little strange, but the demonstration is very clear.
Android Studio Tutorial - Bottom Navigation View
You can also use Tab Layout with custom tab view to achieve this.
custom_tab.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:gravity="center"
android:orientation="vertical"
android:paddingBottom="10dp"
android:paddingTop="8dp">
<ImageView
android:id="#+id/icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:scaleType="centerInside"
android:src="#drawable/ic_recents_selector" />
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textAllCaps="false"
android:textColor="#color/tab_color"
android:textSize="12sp"/>
</LinearLayout>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v4.view.ViewPager
android:id="#+id/view_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<android.support.design.widget.TabLayout
android:id="#+id/tab_layout"
style="#style/AppTabLayout"
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="?attr/colorPrimary" />
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
private TabLayout mTabLayout;
private int[] mTabsIcons = {
R.drawable.ic_recents_selector,
R.drawable.ic_favorite_selector,
R.drawable.ic_place_selector};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Setup the viewPager
ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager);
MyPagerAdapter pagerAdapter = new MyPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(pagerAdapter);
mTabLayout = (TabLayout) findViewById(R.id.tab_layout);
mTabLayout.setupWithViewPager(viewPager);
for (int i = 0; i < mTabLayout.getTabCount(); i++) {
TabLayout.Tab tab = mTabLayout.getTabAt(i);
tab.setCustomView(pagerAdapter.getTabView(i));
}
mTabLayout.getTabAt(0).getCustomView().setSelected(true);
}
private class MyPagerAdapter extends FragmentPagerAdapter {
public final int PAGE_COUNT = 3;
private final String[] mTabsTitle = {"Recents", "Favorites", "Nearby"};
public MyPagerAdapter(FragmentManager fm) {
super(fm);
}
public View getTabView(int position) {
// Given you have a custom layout in `res/layout/custom_tab.xml` with a TextView and ImageView
View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.custom_tab, null);
TextView title = (TextView) view.findViewById(R.id.title);
title.setText(mTabsTitle[position]);
ImageView icon = (ImageView) view.findViewById(R.id.icon);
icon.setImageResource(mTabsIcons[position]);
return view;
}
#Override
public Fragment getItem(int pos) {
switch (pos) {
case 0:
return PageFragment.newInstance(1);
case 1:
return PageFragment.newInstance(2);
case 2:
return PageFragment.newInstance(3);
}
return null;
}
#Override
public int getCount() {
return PAGE_COUNT;
}
#Override
public CharSequence getPageTitle(int position) {
return mTabsTitle[position];
}
}
}
Download Complete Sample Project
Google has launched the BottomNavigationView after the version 25.0.0 of the design support library. But it came with the following limitations:
You can't remove titles and center icon.
You cant't change titles text size.
Y̶o̶u̶ ̶c̶a̶n̶'̶t̶ ̶c̶h̶a̶n̶g̶e̶ ̶t̶h̶e̶ ̶b̶a̶c̶k̶g̶r̶o̶u̶n̶d̶ ̶c̶o̶l̶o̶r̶ ̶i̶t̶ ̶i̶s̶ ̶a̶l̶w̶a̶y̶s̶ ̶t̶h̶e̶ ̶c̶o̶l̶o̶r̶P̶r̶i̶m̶a̶r̶y̶.̶
It doesn't have a BottomNavigationBehavior: so no integration with FAB or SnackBar through CordinatorLayout.
Every menuItem is a pure extension of FrameLayout so it doesn't have any nice circle reveal effect
So the max you can do with this fist version of BottomNavigationView is: (without any reflection or implementing the lib by yourself).
So, If you want any of these. You can use a third part library like roughike/BottomBar or implement the lib by yourself.
As Sanf0rd mentioned, Google launched the BottomNavigationView as part of the Design Support Library version 25.0.0. The limitations he mentioned are mostly true, except that you CAN change the background color of the view and even the text color and icon tint color. It also has an animation when you add more than 4 items (sadly it cannot be enabled or disabled manually).
I wrote a detailed tutorial about it with examples and an accompanying repository, which you can read here:
https://blog.autsoft.hu/now-you-can-use-the-bottom-navigation-view-in-the-design-support-library/
The gist of it
You have to add these in your app level build.gradle:
compile 'com.android.support:appcompat-v7:25.0.0'
compile 'com.android.support:design:25.0.0'
You can include it in your layout like this:
<android.support.design.widget.BottomNavigationView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/bottom_navigation_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:itemBackground="#color/darkGrey"
app:itemIconTint="#color/bottom_navigation_item_background_colors"
app:itemTextColor="#color/bottom_navigation_item_background_colors"
app:menu="#menu/menu_bottom_navigation" />
You can specify the items via a menu resource like this:
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/action_one"
android:icon="#android:drawable/ic_dialog_map"
android:title="One"/>
<item
android:id="#+id/action_two"
android:icon="#android:drawable/ic_dialog_info"
android:title="Two"/>
<item
android:id="#+id/action_three"
android:icon="#android:drawable/ic_dialog_email"
android:title="Three"/>
<item
android:id="#+id/action_four"
android:icon="#android:drawable/ic_popup_reminder"
android:title="Four"/>
</menu>
And you can set the tint and text color as a color list, so the currently selected item is highlighted:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:color="#color/colorAccent"
android:state_checked="false"/>
<item
android:color="#android:color/white"
android:state_checked="true"/>
</selector>
Finally, you can handle the selection of the items with an OnNavigationItemSelectedListener:
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment fragment = null;
switch (item.getItemId()) {
case R.id.action_one:
// Switch to page one
break;
case R.id.action_two:
// Switch to page two
break;
case R.id.action_three:
// Switch to page three
break;
}
return true;
}
});
Other alternate library you can try :- https://github.com/Ashok-Varma/BottomNavigation
<com.ashokvarma.bottomnavigation.BottomNavigationBar
android:layout_gravity="bottom"
android:id="#+id/bottom_navigation_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
BottomNavigationBar bottomNavigationBar = (BottomNavigationBar) findViewById(R.id.bottom_navigation_bar);
bottomNavigationBar
.addItem(new BottomNavigationItem(R.drawable.ic_home_white_24dp, "Home"))
.addItem(new BottomNavigationItem(R.drawable.ic_book_white_24dp, "Books"))
.addItem(new BottomNavigationItem(R.drawable.ic_music_note_white_24dp, "Music"))
.addItem(new BottomNavigationItem(R.drawable.ic_tv_white_24dp, "Movies & TV"))
.addItem(new BottomNavigationItem(R.drawable.ic_videogame_asset_white_24dp, "Games"))
.initialise();
i've made a private class which uses a gridview and a menu resource:
private class BottomBar {
private GridView mGridView;
private Menu mMenu;
private BottomBarAdapter mBottomBarAdapter;
private View.OnClickListener mOnClickListener;
public BottomBar (#IdRes int gridviewId, #MenuRes int menuRes,View.OnClickListener onClickListener) {
this.mGridView = (GridView) findViewById(gridviewId);
this.mMenu = getMenu(menuRes);
this.mOnClickListener = onClickListener;
this.mBottomBarAdapter = new BottomBarAdapter();
this.mGridView.setAdapter(mBottomBarAdapter);
}
private Menu getMenu(#MenuRes int menuId) {
PopupMenu p = new PopupMenu(MainActivity.this,null);
Menu menu = p.getMenu();
getMenuInflater().inflate(menuId,menu);
return menu;
}
public GridView getGridView(){
return mGridView;
}
public void show() {
mGridView.setVisibility(View.VISIBLE);
mGridView.animate().translationY(0);
}
public void hide() {
mGridView.animate().translationY(mGridView.getHeight());
}
private class BottomBarAdapter extends BaseAdapter {
private LayoutInflater mInflater;
public BottomBarAdapter(){
this.mInflater = LayoutInflater.from(MainActivity.this);
}
#Override
public int getCount() {
return mMenu.size();
}
#Override
public Object getItem(int i) {
return mMenu.getItem(i);
}
#Override
public long getItemId(int i) {
return 0;
}
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
MenuItem item = (MenuItem) getItem(i);
if (view==null){
view = mInflater.inflate(R.layout.your_item_layout,null);
view.setId(item.getItemId());
}
view.setOnClickListener(mOnClickListener);
view.findViewById(R.id.bottomnav_icon).setBackground(item.getIcon());
((TextView) view.findViewById(R.id.bottomnav_label)).setText(item.getTitle());
return view;
}
}
your_menu.xml:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/item1_id"
android:icon="#drawable/ic_item1"
android:title="#string/title_item1"/>
<item android:id="#+id/item2_id"
android:icon="#drawable/ic_item2"
android:title="#string/title_item2"/>
...
</menu>
and a custom layout item your_item_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_margin="16dp">
<ImageButton android:id="#+id/bottomnav_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="top|center_horizontal"
android:layout_marginTop="8dp"
android:layout_marginBottom="4dp"/>
<TextView android:id="#+id/bottomnav_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="8dp"
android:layout_marginTop="4dp"
style="#style/mystyle_label" />
</LinearLayout>
usage inside your mainactivity:
BottomBar bottomBar = new BottomBar(R.id.YourGridView,R.menu.your_menu, mOnClickListener);
and
private View.OnClickListener mOnClickListener = new View.OnClickListener() {
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.item1_id:
//todo item1
break;
case R.id.item2_id:
//todo item2
break;
...
}
}
}
and in layout_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
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:fitsSystemWindows="true">
...
<FrameLayout android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
<GridView android:id="#+id/bottomNav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/your_background_color"
android:verticalSpacing="0dp"
android:horizontalSpacing="0dp"
android:numColumns="4"
android:stretchMode="columnWidth"
app:layout_anchor="#id/fragment_container"
app:layout_anchorGravity="bottom"/>
</android.support.design.widget.CoordinatorLayout>
I think this is also be useful.
Snippet
public class MainActivity : AppCompatActivity, BottomNavigationBar.Listeners.IOnTabSelectedListener
{
private BottomBar _bottomBar;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.MainActivity);
_bottomBar = BottomBar.Attach(this, bundle);
_bottomBar.SetItems(
new BottomBarTab(Resource.Drawable.ic_recents, "Recents"),
new BottomBarTab(Resource.Drawable.ic_favorites, "Favorites"),
new BottomBarTab(Resource.Drawable.ic_nearby, "Nearby")
);
_bottomBar.SetOnItemSelectedListener(this);
_bottomBar.HideShadow();
_bottomBar.UseDarkTheme(true);
_bottomBar.SetTypeFace("Roboto-Regular.ttf");
var badge = _bottomBar.MakeBadgeForTabAt(1, Color.ParseColor("#f02d4c"), 1);
badge.AutoShowAfterUnSelection = true;
}
public void OnItemSelected(int position)
{
}
protected override void OnSaveInstanceState(Bundle outState)
{
base.OnSaveInstanceState(outState);
// Necessary to restore the BottomBar's state, otherwise we would
// lose the current tab on orientation change.
_bottomBar.OnSaveInstanceState(outState);
}
}
Links
https://github.com/pocheshire/BottomNavigationBar
It's https://github.com/roughike/BottomBar ported to C# for Xamarin developers
There is a new official BottomNavigationView in version 25 of the Design Support Library
https://developer.android.com/reference/android/support/design/widget/BottomNavigationView.html
add in gradle
compile 'com.android.support:design:25.0.0'
XML
<android.support.design.widget.BottomNavigationView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:design="http://schema.android.com/apk/res/android.support.design"
android:id="#+id/navigation"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
design:menu="#menu/my_navigation_items" />
This library, BottomNavigationViewEx, extends Google's BottomNavigationView. You can easily customise Google's library to have bottom navigation bar the way you want it to be. You can disable the shifting mode, change visibility of the icons and texts and so much more. Definitely try it out.
I have referred this github post and I have set the three layouts for three fragment pages in bottom tab bar.
FourButtonsActivity.java:
bottomBar.setFragmentItems(getSupportFragmentManager(), R.id.fragmentContainer,
new BottomBarFragment(LibraryFragment.newInstance(R.layout.library_fragment_layout), R.drawable.ic_update_white_24dp, "Recents"),
new BottomBarFragment(PhotoEffectFragment.newInstance(R.layout.photo_effect_fragment), R.drawable.ic_local_dining_white_24dp, "Food"),
new BottomBarFragment(VideoFragment.newInstance(R.layout.video_layout), R.drawable.ic_favorite_white_24dp, "Favorites")
);
To set the badge count :
BottomBarBadge unreadMessages = bottomBar.makeBadgeForTabAt(1, "#E91E63", 4);
unreadMessages.show();
unreadMessages.setCount(4);
unreadMessages.setAnimationDuration(200);
unreadMessages.setAutoShowAfterUnSelection(true);
LibraryFragment.java:
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class LibraryFragment extends Fragment {
private static final String STARTING_TEXT = "Four Buttons Bottom Navigation";
public LibraryFragment() {
}
public static LibraryFragment newInstance(int resource) {
Bundle args = new Bundle();
args.putInt(STARTING_TEXT, resource);
LibraryFragment sampleFragment = new LibraryFragment();
sampleFragment.setArguments(args);
return sampleFragment;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = LayoutInflater.from(getActivity()).inflate(
getArguments().getInt(STARTING_TEXT), null);
return view;
}
<android.support.design.widget.BottomNavigationView
android:id="#+id/navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="?android:attr/windowBackground"
app:menu="#menu/navigation" />
navigation.xml(inside menu)
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/navigation_home"
android:icon="#drawable/ic_home_black_24dp"
android:title="#string/title_home"
app:showAsAction="always|withText"
android:enabled="true"/>
Inside onCreate() method,
BottomNavigationView navigation = (BottomNavigationView)findViewById(R.id.navigation);
//Dont forgot this line
BottomNavigationViewHelper.disableShiftMode(navigation);
And Create class as below.
public class BottomNavigationViewHelper {
public static void disableShiftMode(BottomNavigationView view) {
BottomNavigationMenuView menuView = (BottomNavigationMenuView) view.getChildAt(0);
try {
Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode");
shiftingMode.setAccessible(true);
shiftingMode.setBoolean(menuView, false);
shiftingMode.setAccessible(false);
for (int i = 0; i < menuView.getChildCount(); i++) {
BottomNavigationItemView item = (BottomNavigationItemView) menuView.getChildAt(i);
//noinspection RestrictedApi
item.setShiftingMode(false);
// set once again checked value, so view will be updated
//noinspection RestrictedApi
item.setChecked(item.getItemData().isChecked());
}
} catch (NoSuchFieldException e) {
Log.e("BNVHelper", "Unable to get shift mode field", e);
} catch (IllegalAccessException e) {
Log.e("BNVHelper", "Unable to change value of shift mode", e);
}
}
}
You can create the layouts according to the above-mentioned answers
If anyone wants to use this in kotlin:-
private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.images -> {
// do your work....
return#OnNavigationItemSelectedListener true
}
R.id.videos ->
{
// do your work....
return#OnNavigationItemSelectedListener true
}
}
false
}
then in oncreate you can set the above listener to your view
mDataBinding?.navigation?.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)

Categories

Resources