I am trying to achieve something like this https://material.google.com/components/bottom-navigation.html#bottom-navigation-behavior
But recycler view is hiding below toolbar and no effect is on BottomNavigationView
Below is my code
activity_main.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:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</android.support.design.widget.AppBarLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.BottomNavigationView
android:id="@+id/nm_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="@color/colorPrimaryDark"
android:foregroundTint="@color/colorAccent"
app:itemIconTint="@android:color/white"
app:itemTextColor="@android:color/white"
app:menu="@menu/nav_menu" />
<android.support.v7.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/nm_bottom"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</RelativeLayout>
</android.support.design.widget.CoordinatorLayout>
item.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:padding="4dp">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardElevation="2dp">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:padding="4dp">
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentTop="true"
android:layout_marginRight="6dip"
android:contentDescription="TODO"
android:src="@drawable/ic_storage" />
<TextView
android:id="@+id/secondLine"
android:layout_width="fill_parent"
android:layout_height="26dip"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_toRightOf="@id/icon"
android:ellipsize="marquee"
android:text="Description"
android:textSize="12sp" />
<TextView
android:id="@+id/firstLine"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_above="@id/secondLine"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_alignWithParentIfMissing="true"
android:layout_toRightOf="@id/icon"
android:gravity="center_vertical"
android:text="Example application"
android:textSize="16sp" />
</RelativeLayout>
</android.support.v7.widget.CardView>
</FrameLayout>
public class MainActivity extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener {
private static final Logger logger = Logger.getLogger(MainActivity.class.getSimpleName());
private BottomNavigationView navigationView;
private RecyclerView mRecyclerView;
private MyAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
}
private void initViews() {
navigationView = (BottomNavigationView) findViewById(R.id.nm_bottom);
navigationView.setOnNavigationItemSelectedListener(this);
mRecyclerView = (RecyclerView) findViewById(R.id.rv);
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
mRecyclerView.setHasFixedSize(true);
// use a linear layout manager
LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
// specify an adapter (see also next example)
List<String> myDataset = new ArrayList<>();
for (int i = 0; i < 100; i++) {
myDataset.add("Index #" + i);
}
mAdapter = new MyAdapter(myDataset);
mRecyclerView.setAdapter(mAdapter);
}
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
return false;
}
}
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private List<String> mDataset;
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public class ViewHolder extends RecyclerView.ViewHolder {
// each data item is just a string in this case
public TextView txtHeader;
public TextView txtFooter;
public ViewHolder(View v) {
super(v);
txtHeader = (TextView) v.findViewById(R.id.firstLine);
txtFooter = (TextView) v.findViewById(R.id.secondLine);
}
}
public void add(int position, String item) {
mDataset.add(position, item);
notifyItemInserted(position);
}
public void remove(String item) {
int position = mDataset.indexOf(item);
mDataset.remove(position);
notifyItemRemoved(position);
}
// Provide a suitable constructor (depends on the kind of dataset)
public MyAdapter(List<String> myDataset) {
mDataset = myDataset;
}
// Create new views (invoked by the layout manager)
@Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
// create a new view
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycler_view, parent, false);
// set the view's size, margins, paddings and layout parameters
ViewHolder vh = new ViewHolder(v);
return vh;
}
// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
final String name = mDataset.get(position);
holder.txtHeader.setText(mDataset.get(position));
holder.txtHeader.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
remove(name);
}
});
holder.txtFooter.setText("Footer: " + mDataset.get(position));
}
// Return the size of your dataset (invoked by the layout manager)
@Override
public int getItemCount() {
return mDataset.size();
}
}
BottomNavigationView makes it easy for users to explore and switch between top-level views with a single tap. There should be a minimum of 3 top-level views and a maximum of 5. If Destinations are more than 5 then use the Navigation Drawer. When the user taps on the icon it will change the top-level view accordingly.
com.google.android.material.bottomnavigation.BottomNavigationView. Represents a standard bottom navigation bar for application. It is an implementation of material design bottom navigation. Bottom navigation bars make it easy for users to explore and switch between top-level views in a single tap.
Create a menu resource with up to 5 navigation targets (BottomNavigationView does not support more than 5 items). Secondly, Having 5 or more items in BottomNavigation is a bad design in terms of User Experience. Even 4 is a stretch. If you need more than 5 items, BottomNavigation is not suitable for you.
Edit: Here is a simple sample showing how to implement scrolling behavior https://github.com/sjthn/BottomNavigationViewBehavior
Change your XML to this:
<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/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:title="@string/app_name" />
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<android.support.design.widget.BottomNavigationView
android:id="@+id/nm_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimaryDark"
android:foregroundTint="@color/colorAccent"
app:itemIconTint="@android:color/white"
app:itemTextColor="@android:color/white"
app:layout_anchor="@+id/rv"
app:layout_anchorGravity="bottom"
app:menu="@menu/nav_menu" />
</android.support.design.widget.CoordinatorLayout>
So with little help from @Srijith i am able to animate the way it is given in link.
Part of this answer is
public class BottomNavigationBehavior<V extends View> extends VerticalScrollingBehavior<V> {
private static final Interpolator INTERPOLATOR = new LinearOutSlowInInterpolator();
private int mBottomNavigationViewId;
private boolean hidden = false;
private ViewPropertyAnimatorCompat mTranslationAnimator;
private BottomNavigationView navigationView;
private View mTabsHolder;
public BottomNavigationBehavior() {
super();
}
public BottomNavigationBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
mBottomNavigationViewId = R.id.nm_bottom;
}
@Override
public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
boolean layoutChild = super.onLayoutChild(parent, child, layoutDirection);
if (navigationView == null && mBottomNavigationViewId != View.NO_ID) {
navigationView = findTabLayout(child);
getTabsHolder();
}
return layoutChild;
}
private BottomNavigationView findTabLayout(View child) {
return (BottomNavigationView) child.findViewById(mBottomNavigationViewId);
}
@Override
public void onNestedVerticalOverScroll(CoordinatorLayout coordinatorLayout, V child, @ScrollDirection int direction, int currentOverScroll, int totalOverScroll) {
}
@Override
public void onDirectionNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, int[] consumed, @ScrollDirection int scrollDirection) {
handleDirection(child, scrollDirection);
}
private void handleDirection(V child, int scrollDirection) {
if (scrollDirection == ScrollDirection.SCROLL_DIRECTION_DOWN && hidden) {
hidden = false;
animateOffset(child, 0);
} else if (scrollDirection == ScrollDirection.SCROLL_DIRECTION_UP && !hidden) {
hidden = true;
animateOffset(child, child.getHeight());
}
}
@Override
protected boolean onNestedDirectionFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY, @ScrollDirection int scrollDirection) {
handleDirection(child, scrollDirection);
return true;
}
private void animateOffset(final V child, final int offset) {
ensureOrCancelAnimator(child);
mTranslationAnimator.translationY(offset).start();
animateTabsHolder(offset);
}
private void animateTabsHolder(int offset) {
if (mTabsHolder != null) {
offset = offset > 0 ? 0 : 1;
ViewCompat.animate(mTabsHolder).alpha(offset).setDuration(200).start();
}
}
private void ensureOrCancelAnimator(V child) {
if (mTranslationAnimator == null) {
mTranslationAnimator = ViewCompat.animate(child);
mTranslationAnimator.setDuration(100);
mTranslationAnimator.setInterpolator(INTERPOLATOR);
} else {
mTranslationAnimator.cancel();
}
}
private void getTabsHolder() {
if (navigationView != null) {
mTabsHolder = navigationView.getChildAt(0);
}
}
public static <V extends View> BottomNavigationBehavior<V> from(V view) {
ViewGroup.LayoutParams params = view.getLayoutParams();
if (!(params instanceof CoordinatorLayout.LayoutParams)) {
throw new IllegalArgumentException("The view is not a child of CoordinatorLayout");
}
CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params)
.getBehavior();
if (!(behavior instanceof BottomNavigationBehavior)) {
throw new IllegalArgumentException(
"The view is not associated with ottomNavigationBehavior");
}
return (BottomNavigationBehavior<V>) behavior;
}
public void setTabLayoutId(@IdRes int tabId) {
this.mBottomNavigationViewId = tabId;
}
}
public abstract class VerticalScrollingBehavior<V extends View> extends CoordinatorLayout.Behavior<V> {
private int mTotalDyUnconsumed = 0;
private int mTotalDy = 0;
@ScrollDirection
private int mOverScrollDirection = ScrollDirection.SCROLL_NONE;
@ScrollDirection
private int mScrollDirection = ScrollDirection.SCROLL_NONE;
public VerticalScrollingBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
public VerticalScrollingBehavior() {
super();
}
@Retention(RetentionPolicy.SOURCE)
@IntDef({ScrollDirection.SCROLL_DIRECTION_UP, ScrollDirection.SCROLL_DIRECTION_DOWN})
public @interface ScrollDirection {
int SCROLL_DIRECTION_UP = 1;
int SCROLL_DIRECTION_DOWN = -1;
int SCROLL_NONE = 0;
}
/*
@return Overscroll direction: SCROLL_DIRECTION_UP, CROLL_DIRECTION_DOWN, SCROLL_NONE
*/
@ScrollDirection
public int getOverScrollDirection() {
return mOverScrollDirection;
}
/**
* @return Scroll direction: SCROLL_DIRECTION_UP, SCROLL_DIRECTION_DOWN, SCROLL_NONE
*/
@ScrollDirection
public int getScrollDirection() {
return mScrollDirection;
}
/**
* @param coordinatorLayout
* @param child
* @param direction Direction of the overscroll: SCROLL_DIRECTION_UP, SCROLL_DIRECTION_DOWN
* @param currentOverScroll Unconsumed value, negative or positive based on the direction;
* @param totalOverScroll Cumulative value for current direction
*/
public abstract void onNestedVerticalOverScroll(CoordinatorLayout coordinatorLayout, V child, @ScrollDirection int direction, int currentOverScroll, int totalOverScroll);
/**
* @param scrollDirection Direction of the overscroll: SCROLL_DIRECTION_UP, SCROLL_DIRECTION_DOWN
*/
public abstract void onDirectionNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, int[] consumed, @ScrollDirection int scrollDirection);
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes) {
return (nestedScrollAxes & View.SCROLL_AXIS_VERTICAL) != 0;
}
@Override
public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes) {
super.onNestedScrollAccepted(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
}
@Override
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {
super.onStopNestedScroll(coordinatorLayout, child, target);
}
@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
if (dyUnconsumed > 0 && mTotalDyUnconsumed < 0) {
mTotalDyUnconsumed = 0;
mOverScrollDirection = ScrollDirection.SCROLL_DIRECTION_UP;
} else if (dyUnconsumed < 0 && mTotalDyUnconsumed > 0) {
mTotalDyUnconsumed = 0;
mOverScrollDirection = ScrollDirection.SCROLL_DIRECTION_DOWN;
}
mTotalDyUnconsumed += dyUnconsumed;
onNestedVerticalOverScroll(coordinatorLayout, child, mOverScrollDirection, dyConsumed, mTotalDyUnconsumed);
}
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, int[] consumed) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
if (dy > 0 && mTotalDy < 0) {
mTotalDy = 0;
mScrollDirection = ScrollDirection.SCROLL_DIRECTION_UP;
} else if (dy < 0 && mTotalDy > 0) {
mTotalDy = 0;
mScrollDirection = ScrollDirection.SCROLL_DIRECTION_DOWN;
}
mTotalDy += dy;
onDirectionNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, mScrollDirection);
}
@Override
public boolean onNestedFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY, boolean consumed) {
super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
mScrollDirection = velocityY > 0 ? ScrollDirection.SCROLL_DIRECTION_UP : ScrollDirection.SCROLL_DIRECTION_DOWN;
return onNestedDirectionFling(coordinatorLayout, child, target, velocityX, velocityY, mScrollDirection);
}
protected abstract boolean onNestedDirectionFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY, @ScrollDirection int scrollDirection);
@Override
public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY) {
return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
}
@Override
public WindowInsetsCompat onApplyWindowInsets(CoordinatorLayout coordinatorLayout, V child, WindowInsetsCompat insets) {
return super.onApplyWindowInsets(coordinatorLayout, child, insets);
}
@Override
public Parcelable onSaveInstanceState(CoordinatorLayout parent, V child) {
return super.onSaveInstanceState(parent, child);
}
}
All this answer came from this guy: https://medium.com/@nullthemall/bottom-navigation-behavior-388b9b206667#.potyetdkb
Entire Project is located here
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With