Materialio Appbar does not hide when scrolling down - android

I've followed the material documentation for top app bars and implemented a part of it in my app to be able to hide it, when scrolling down my list.
My Layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="#+id/ddd"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".pkgTestforend.DriverListFragment">
<com.example.dochjavatestimplementation.pkgTestforend.CustomLinearLayout
android:layout_width="match_parent"
android:id="#+id/cusLL"
android:layout_height="match_parent"
app:elevation="0dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="#+id/listAllDrivers"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
</com.example.dochjavatestimplementation.pkgTestforend.CustomLinearLayout>
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:liftOnScroll="true" >
<com.google.android.material.appbar.MaterialToolbar
android:id="#+id/topAppBar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:title="PageTitle"
app:menu="#menu/top_app_bar"
app:layout_scrollFlags="scroll|enterAlways|snap"
app:navigationIcon="#drawable/baseline_menu_24"
style="#style/Widget.MaterialComponents.Toolbar.Primary"
/>
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
And the corresponding fragment:
public class DriverListFragment extends Fragment implements View.OnClickListener {
public DriverListFragment() {
}
public static DriverListFragment newInstance(String param1, String param2) {
return new DriverListFragment();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_driver_list, container, false);
setUpToolbar(view);
return view;
}
private void setUpToolbar(View view) {
Toolbar toolbar = view.findViewById(R.id.topAppBar);
AppCompatActivity activity = (AppCompatActivity) getActivity();
if (activity != null) {
activity.setSupportActionBar(toolbar);
}
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
menuInflater.inflate(R.menu.top_app_bar, menu);
super.onCreateOptionsMenu(menu, menuInflater);
}
ListView listViewDriver;
DriverListAdapter adapter;
RoomWithRxJavaViewModel viewModel;
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
listViewDriver = view.findViewById(R.id.listAllDrivers);
viewModel = new RoomWithRxJavaViewModel(getActivity().getApplication());
Disposable d = viewModel.getDrivers()
.subscribe(allusers ->
{
adapter = new DriverListAdapter(getContext(), (ArrayList<Driver>) allusers);
listViewDriver.setAdapter(adapter);
}, e -> {
//show err mes
}
);
}
}
The setup is pretty simple build.
I just have a custom linearlayout which I am planning to modify later. And yes, the relative layout for the listview is on purpose so I can easily modify my future button positions.
The result looks like this:
So the issue is that, altough the app bar at the top is visible, it wont hide when I scroll down my list (see picture above), eventough I am using app:liftOnScroll="true" and app:layout_scrollFlags="scroll|enterAlways|snap".
What am I missing exactly? Is it cause I use my custom linearlayout?
Putting the appbar before the custom linearlayout didn't change the output unfourntatly.

Related

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.

Android Studio Bottom Bar Nav: How do I add to each fragment?

I followed a tutorial online to create a Bottom Bar Navigation, but the guy didn't explain clearly how to add stuff to each fragment and swap between the fragments. I want to add an EditText to the home page as an example on how to add items to the fragments. Here's my code:
Main Activity -
public class MainActivity extends AppCompatActivity {
private static final String TAG_FRAGMENT_CATA = "tag_frag_cata";
private static final String TAG_FRAGMENT_HOME = "tag_frag_home";
private static final String TAG_FRAGMENT_SETTINGS = "tag_frag_settings";
private BottomNavigationView bottomNavigationView;
private List<BottomBarFragment> fragments = new ArrayList<>(3);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_nav);
bottomNavigationView.setOnNavigationItemSelectedListener(
new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.bottombaritem_cata:
switchFragment(0, TAG_FRAGMENT_CATA);
return true;
case R.id.bottombaritem_home:
switchFragment(1, TAG_FRAGMENT_HOME);
return true;
case R.id.bottombaritem_settings:
switchFragment(2, TAG_FRAGMENT_SETTINGS);
return true;
}
return false;
}
});
buildFragmentsList();
switchFragment(1, TAG_FRAGMENT_HOME);
}
private void buildFragmentsList() {
BottomBarFragment cataFragment = buildFragment("Categories");
BottomBarFragment homeFragment = buildFragment("Home");
BottomBarFragment settingsFragment = buildFragment("Settings");
fragments.add(cataFragment);
fragments.add(homeFragment);
fragments.add(settingsFragment);
}
private BottomBarFragment buildFragment(String title) {
BottomBarFragment fragment = new BottomBarFragment();
Bundle bundle = new Bundle();
bundle.putString(BottomBarFragment.ARG_TITLE, title);
fragment.setArguments(bundle);
return fragment;
}
private void switchFragment(int pos, String tag) {
getSupportFragmentManager().beginTransaction().replace(R.id.frame_fragmentholder, fragments.get(pos), tag).commit();
}
}
Bottom Bar Activity -
public class BottomBarFragment extends Fragment {
public static final String ARG_TITLE = "arg_title";
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.bottom_bar_fragment, container, false);
return rootView;
}
}
XML for the Main Activity
<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=".MainActivity">
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.BottomNavigationView
android:id="#+id/bottom_nav"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="?android:attr/windowBackground"
app:itemBackground="#android:color/white"
app:itemIconTint="#android:color/black"
app:itemTextColor="#android:color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="#menu/bottombar_menu" />
<FrameLayout
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="#+id/bottom_nav"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<FrameLayout
android:id="#+id/frame_fragmentholder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="?attr/actionBarSize">
</FrameLayout>
<android.support.design.widget.BottomNavigationView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</android.support.constraint.ConstraintLayout>
XML Layout file -
<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=".BottomBarFragment">
</android.support.constraint.ConstraintLayout>
If someone could explain how to use this code, I would really appreciate it!
You have three fragements in your bottomNavigationBar. I guess these would be cata, home and settings. For each one you need to create new class.
E.g. for cata:
public class CataFragment extends Fragment {
public CataFragment() {
// Required constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//here you would do the fragment specific coding
return inflater.inflate(R.layout.fragment_cata, container, false);
}
}
As you can already see, they are in touch with a layout file, here the fragment_cata file for what you have to create an xml layout file with its name. The layout file can be an autocreated blank fragment layout that you can find in the list when you create new files. That would look like this:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ChatFragment">
<!-- TODO: Update blank fragment layout -->
</RelativeLayout>
This layout file you can handle like any other layout file

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.

Viewpager with different menu and common toolbar not working

I have tabs in my app. Each tab is of different fragments and has different menu. Below is the layout which I am using
<?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_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/background">
<include
layout="#layout/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|enterAlways" />
</android.support.design.widget.AppBarLayout>
<com.CustomViewPager
android:id="#+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
<android.support.design.widget.TabLayout
android:id="#+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:background="#color/background"
app:layout_anchor="#id/view_pager"
app:layout_anchorGravity="bottom|center_horizontal"
app:layout_behavior="com.widget.ScrollTabBehavior"
app:tabBackground="#color/background"
app:tabIndicatorColor="#color/toolbar"
app:tabIndicatorHeight="0dp"
app:tabMode="fixed"
app:tabPaddingEnd="0dp"
app:tabPaddingStart="0dp" />
</android.support.design.widget.CoordinatorLayout>
<RelativeLayout
android:id="#+id/left_drawer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
android:layout_marginLeft="-64dp"
android:layout_marginStart="-64dp"
android:background="#color/toolbar"
android:choiceMode="singleChoice"
android:divider="#android:color/transparent"
android:dividerHeight="0dp">
<ImageButton
android:id="#+id/close_btn"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:background="#android:color/transparent"
android:padding="5dp"
android:src="#drawable/close_icon" />
<view
android:id="#+id/drawerlist"
class="android.support.v7.widget.RecyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/close_btn" />
</RelativeLayout>
</android.support.v4.widget.DrawerLayout>
Now with every Fragment in onCreate() I have mentioned setHasOptionsMenu(true); further more. I have overriden the function onCreateOptionsMenu() in each fragment which has menu.clear(); at first then calling it's super constructor then inflating fragment's own menu xml. But result I am getting is something like this -
Suppose there are 5 tabs. 2nd and 3rd tab has viewpager within each consist of two more fragments within
1st tab has no menu
2nd tab has menu_2 (only for 2nd child fragment)
3rd tab again has no menu
4th tab has menu_4 (only for 1st child fragment)
5th tab has menu_5
Initially, there should be no menu for tab 1 which is ok. Then moving to 3rd tab directly it shows menu_4 which by default should show no menu. Then sliding to tab 4, it will show proper menu_4 then sliding back to 3rd tab it will show no menu (which is as required).
This same thing happens for tab 5. If I am switching to 2nd child fragment within 2nd tab then the same behavior is observed with 1st tab.
In short, according to my observation it is showing the menu of adjacent tabs which is actually getting executed after the current fragment and therefore such behavior is occurring.
So how to avoid this?
I have written small test-application to check the behaviour.
Let's go through the sample and see, if something wrong with your fragments (as you see above, ViewPager with different menus works like a charm)
Activity's XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.TabLayout
android:id="#+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<android.support.v4.view.ViewPager
android:id="#+id/viewPager"
android:layout_below="#+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
Activity class. Important part is invalidateOptionsMenu() every time ViewPager has PageSelected event. Then, we setting setHasOptionsMenu to all fragments and subfragments(from the nested ViewPagers) to false if they out of screen.
public class MainActivity extends AppCompatActivity {
PagerAdapter pagerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Fragment[] fragments = {
Fragment.instantiate(this, FragmentNoMenu.class.getName()),
Fragment.instantiate(this, FragmentA.class.getName()),
Fragment.instantiate(this, FragmentNoMenu.class.getName()),
Fragment.instantiate(this, FragmentB.class.getName()),
};
TabLayout tabLayout = (TabLayout)findViewById(R.id.tabLayout);
ViewPager viewPager = (ViewPager)findViewById(R.id.viewPager);
pagerAdapter = new PagerAdapter(getSupportFragmentManager(), fragments);
viewPager.setAdapter(pagerAdapter);
viewPager.setOffscreenPageLimit(0);
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
#Override
public void onPageSelected(int position) {
invalidateOptionsMenu(position);
}
#Override
public void onPageScrollStateChanged(int state) {}
});
invalidateOptionsMenu(0);
tabLayout.setupWithViewPager(viewPager);
}
private void invalidateOptionsMenu(int position) {
for(int i = 0; i < pagerAdapter.getCount(); i++) {
Fragment fragment = pagerAdapter.getItem(i);
fragment.setHasOptionsMenu(i == position);
if (fragment instanceof FragmentWithViewPager) {
FragmentWithViewPager fragmentWithViewPager = (FragmentWithViewPager)fragment;
if (fragmentWithViewPager.pagerAdapter != null) {
for (int j = 0; j < fragmentWithViewPager.pagerAdapter.getCount(); j++) {
fragmentWithViewPager.pagerAdapter.getItem(j).setHasOptionsMenu(i == position);
}
}
}
}
invalidateOptionsMenu();
}
}
PagerAdapter class:
public class PagerAdapter extends FragmentPagerAdapter {
private final Fragment[] fragments;
public PagerAdapter(FragmentManager fragmentManager, Fragment[] fragments) {
super(fragmentManager);
this.fragments = fragments;
}
#Override
public CharSequence getPageTitle(int position) {
return fragments[position].getClass().getSimpleName();
}
#Override
public Fragment getItem(int position) {
return fragments[position];
}
#Override
public int getCount() {
return fragments.length;
}
}
Here're my test fragments I used:
FragmentNoMenu class:
public class FragmentNoMenu extends android.support.v4.app.Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_no_menu, container, false);
}
}
FragmentNoMenu layout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#0F0F50"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
FragmentA class is a Fragment with nested ViewPager:
public class FragmentA extends FragmentWithViewPager {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_a, container, false);
Fragment[] fragments = {
Fragment.instantiate(getContext(), SubFragmentA.class.getName()),
Fragment.instantiate(getContext(), SubFragmentB.class.getName()),
Fragment.instantiate(getContext(), SubFragmentC.class.getName()),
};
if (pagerAdapter == null) {
pagerAdapter = new PagerAdapter(getChildFragmentManager(), fragments);
}
viewPager = (ViewPager)rootView.findViewById(R.id.viewPager);
viewPager.setAdapter(pagerAdapter);
return rootView;
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.fragment_a, menu);
super.onCreateOptionsMenu(menu, inflater);
}
}
FragmentA's layout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#B0B0B0"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:id="#+id/viewPager"
android:layout_marginTop="80dp"
android:layout_width="match_parent"
android:layout_height="200dp"/>
</FrameLayout>
FragmentA's 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/action_item_1"
android:title="Item 1"
android:orderInCategory="250"
app:showAsAction="never" />
<item
android:id="#+id/action_item_2"
android:title="Item 2"
android:orderInCategory="300"
app:showAsAction="never" />
</menu>
NB! FragmentA extends FragmentWithViewPager - it's a small extension of Fragment to make it easier to distinguish Fragments with nested fragments in MainActivity:
public class FragmentWithViewPager extends Fragment {
PagerAdapter pagerAdapter;
ViewPager viewPager;
}
FragmentB:
public class FragmentB extends Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_b, container, false);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.fragment_b, menu);
super.onCreateOptionsMenu(menu, inflater);
}
}
It's layout & menu:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#999999"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
.....
<?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_item3"
android:title="Item 3"
android:icon="#drawable/ic_triage_star"
android:orderInCategory="250"
app:showAsAction="always" />
</menu>
Subfragments (they are all look the same from the code perspective):
Layouts:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#AA0000"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:text="SubFragment C (with icon Menu)"
android:textSize="24sp"
android:textColor="#00BB00"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
Code:
public class SubFragmentB extends Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.subfragment_b, container, false);
}
}
That's it! I've uploaded the project to my dropbox - feel free to check it out!
I hope, it helps

Hide toolbar on scroll when the using a recyclerview inside a Fragment instead of an Activity

I'm trying to adapt the strategy for hiding / showing a toolbar (or any visual element) from the well explained and great article:
http://mzgreen.github.io/2015/02/15/How-to-hideshow-Toolbar-when-list-is-scroling%28part1%29/
But in my case I'm using a Fragment to hold the recycleview instead of the activity. My problem is that the padding is not being applied so the first element is under the toolbar, and I have also another strange behavior, as the toolbar is also under the statusbar. I don't know what is happening here.
The following are my "moving pieces":
BasicActivity.java: based on the one given on the previous post, but moving away the recycleview part as is going to be on the Fragment piece. Also it exposes the show and hide methods to allow the fragment to access it:
public class BasicActivity extends ActionBarActivity {
private Toolbar mToolbar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_basic);
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.container,new RecycleFragment())
.commit();
overridePendingTransition(0, 0);
initToolbar();
}
private void initToolbar() {
mToolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(mToolbar);
setTitle(getString(R.string.app_name));
mToolbar.setTitleTextColor(getResources().getColor(android.R.color.white));
}
public void hideViews() {
mToolbar.animate().translationY(-mToolbar.getHeight()).setInterpolator(new AccelerateInterpolator(2));
}
public void showViews() {
mToolbar.animate().translationY(0).setInterpolator(new DecelerateInterpolator(2));
}
}
My activiy_basic.xml is the following:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<include layout="#layout/toolbar_actionbar" />
</FrameLayout>
The layout toolbar_actionbar.xml
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:clipToPadding="false"/>
The Fragment RecycleFragment.java:
public class RecycleFragment extends Fragment {
#Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_recycler, container, false);
return view;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initRecyclerView(view);
}
private void initRecyclerView(View view) {
RecyclerView recyclerView = (RecyclerView)view.findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
RecyclerAdapter recyclerAdapter = new RecyclerAdapter(createItemList());
recyclerView.setAdapter(recyclerAdapter);
recyclerView.setOnScrollListener(new HidingScrollListener() {
#Override
public void onHide() {
((BasicActivity)getActivity()).hideViews();
}
#Override
public void onShow() {
((BasicActivity)getActivity()).showViews();
}
});
}
private List<String> createItemList() {
List<String> itemList = new ArrayList<>();
for(int i=0;i<20;i++) {
itemList.add("Item "+i);
}
return itemList;
}
}
And the layout for the fragment is just a recyclerview fragment_recycler.xml:
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
The adapter and the viewholder for the recycler are the same as the article, and they doesn't affect the behavior.
What is wrong with the code?
UPDATE:
A MichaƂ Z. below pointed out. What was missing is the paddingTop and clipptoPadding on the Recyclerview view
So the final xml should be:
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="?attr/actionBarSize"
android:clipToPadding="false"/>
And to solve the statusbar overlapping problem, it is needed to add a "fitsystemwindows" = "true" element on the activity layout. So it must be as the following:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<FrameLayout android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<include layout="#layout/toolbar_actionbar" />
</FrameLayout>
UPDATE2
The fitSystemWindows is only needed if the theme is setting the statusbar as translucent
Your fragment_recycler.xml file is missing paddingTop and clipToPadding attributes.
It should look like this:
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="?attr/actionBarSize"
android:clipToPadding="false"/>
And also remove clipToPadding from your toolbar_actionbar.xml.

Categories

Resources