This is what I'm trying to achieve:
I've noticed the layoutDependsOn() and onDependentViewChanged() are never called at all.
For the sake of testing, I tried using "scaling" instead of "set XY" for the custom behavior:
public class CustomBehavior extends CoordinatorLayout.Behavior<LinearLayout>
{
public CustomBehavior(Context context, AttributeSet attrs)
{
super(context, attrs);
}
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, LinearLayout child, View dependency)
{
return dependency instanceof LinearLayout;
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, LinearLayout child, View dependency)
{
float translationY = getTranslationY(parent, child);
float percentComplete = -translationY / dependency.getHeight();
float scaleFactor = 1 - percentComplete;
child.setScaleX(scaleFactor);
child.setScaleY(scaleFactor);
return false;
}
private float getTranslationY(CoordinatorLayout parent, LinearLayout child)
{
float minOffset = 0;
final List<View> dependencies = parent.getDependencies(child);
for (int i = 0, z = dependencies.size(); i < z; i++)
{
final View view = dependencies.get(i);
if (view instanceof LinearLayout && parent.doViewsOverlap(child, view))
{
minOffset = Math.min(minOffset, ViewCompat.getTranslationY(view) - view.getHeight());
}
}
return minOffset;
}
}
And this is the fragment's xml:
<android.support.design.widget.CoordinatorLayout 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">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:clickable="true"
android:orientation="vertical">
<LinearLayout
android:id="#+id/myLinearLayout"
android:layout_width="330dp"
android:layout_marginTop="30dp"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:orientation="vertical"
app:layout_behavior="com.test.CustomBehavior">
...
</LinearLayout>
</RelativeLayout>
<LinearLayout
android:id="#+id/bottomSheetLayout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:behavior_peekHeight="100dp"
app:layout_behavior="android.support.design.widget.BottomSheetBehavior">
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
Your LinearLayout is a child of RelativeLayout and thus app:layout_behavior doesn't have any effect.
You can only assign app:layout_behavior to direct children of the CoordinatorLayout.
Related
I am trying to implement behaviour which is shown below (on pictures), so I need to stretch all content below AppBarLayout. I've already achieved it somehow by implementing custom CoordinatorLayout Behaviour but that solution have some issues with views recycling inside RecyclerView and overall performance. Is there easier solution to achieve what i want?
public class TestBehaviour48Margin extends CoordinatorLayout.Behavior {
public TestBehaviour48Margin() {
}
public TestBehaviour48Margin(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean layoutDependsOn(#NonNull CoordinatorLayout parent, #NonNull View child, #NonNull View dependency) {
return dependency instanceof AppBarLayout;
}
#Override
public boolean onDependentViewChanged(#NonNull CoordinatorLayout parent, #NonNull View child, #NonNull View dependency) {
AppBarLayout appBarLayout = (AppBarLayout) dependency;
val toolbar = appBarLayout.findViewById(R.id.toolbar);
int range = appBarLayout.getTotalScrollRange();
int totalHeight = parent.getHeight();
int appBarHeight = appBarLayout.getHeight();
int toolbarHeight = toolbar.getHeight();
int initialHeight = totalHeight - appBarHeight;
int finalHeight = totalHeight - toolbarHeight;
int differenceHeight = finalHeight - initialHeight;
float factor = -appBarLayout.getY() / range;
val layoutParams = child.getLayoutParams();
int lastHeight = layoutParams.height;
layoutParams.height = (int) (initialHeight + (differenceHeight * factor)) + LayoutUtils.dpToPx(parent.getContext(), 48);
if(lastHeight != layoutParams.height){
child.setLayoutParams(layoutParams);
}
return true;
}
}
<CoordinatorLayout ... (some content)
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="48dp"
android:layout_gravity="bottom"
app:layout_behavior=".custom_views.TestBehaviour48Margin">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/custom_pink"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:nestedScrollingEnabled="false"
android:id="#+id/rv_strength_items"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#color/grey_B" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#color/custom_blue_light" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_gravity="bottom"
android:background="#color/custom_green"
bind:layout_anchor="#id/scroll"
bind:layout_anchorGravity="bottom" />
</LinearLayout>
</FrameLayout>
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/colorSurfaceNight"
android:visibility="#{viewmodel.views.mainLayoutVisibility}"
app:elevation="0dp"
app:layout_behavior=".fragments.training.main_training.view_fold.BlockableAppBarLayoutBehaviour">
... rest of code
iam new to android,please help me.
i want to hide bottom navigation bar so i am using bottom navigation behavior,
i have a scrollview when i scroll the bottomnavigationbar scrolls, i dosent hide please help me
morethan a week am trying to solve this issue please help me
MainActivity.java
mBottomNavigationView=findViewById(R.id.bottom_nav);
mBottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_nav);
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) mBottomNavigationView.getLayoutParams();
layoutParams.setBehavior(new BottomNavigationViewBehavior());
bottomNavigationView.setOnNavigationItemSelectedListener(
new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.bottombaritem_map:
........
......
return true;
}
return false;
}
});
}
BottomNavigationViewBehavior
public class BottomNavigationViewBehavior extends CoordinatorLayout.Behavior<BottomNavigationView> {
private int height;
#Override
public boolean onLayoutChild(CoordinatorLayout parent, BottomNavigationView child, int layoutDirection) {
height = child.getHeight();
return super.onLayoutChild(parent, child, layoutDirection);
}
#Override
public boolean onStartNestedScroll(#NonNull CoordinatorLayout coordinatorLayout,
BottomNavigationView child, #NonNull
View directTargetChild, #NonNull View target,
int axes, int type)
{
return axes == ViewCompat.SCROLL_AXIS_VERTICAL;
}
#Override
public void onNestedScroll(#NonNull CoordinatorLayout coordinatorLayout, #NonNull BottomNavigationView child,
#NonNull View target, int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed,
#ViewCompat.NestedScrollType int type)
{
if (dyConsumed > 0) {
slideDown(child);
} else if (dyConsumed < 0) {
slideUp(child);
}
}
private void slideUp(BottomNavigationView child) {
child.clearAnimation();
child.animate().translationY(0).setDuration(200);
}
private void slideDown(BottomNavigationView child) {
child.clearAnimation();
child.animate().translationY(height).setDuration(200);
}
}
Layout xml file
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.design.widget.CoordinatorLayout
android:id="#+id/coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView 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/main_book_scrol1"
android:layout_width="fill_parent"
android:visibility="visible"
android:layout_height="fill_parent"
android:fillViewport="true">
...........
..........
</ScrollView>
<!---your RecyclerView/Fragment Container Layout-->
<FrameLayout
android:id="#+id/container"
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/bottom_nav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:itemBackground="#color/white"
app:menu="#menu/bottombar_menu" />
</android.support.design.widget.CoordinatorLayout>
<!---NavigationView-->
</android.support.v4.widget.DrawerLayout>
i have a BottomNavigationView activity . in that i placed a webview as fragment , but the problem is ,user can not click on the web contents on the bottom side, because of my BottomNavigationView , is there any one to suggest me a good solution
this is my 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"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.hackerinside.jaisonjoseph.polysocial.MainActivity">
<FrameLayout
android:id="#+id/content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#android:color/holo_blue_dark">
<TextView
android:id="#+id/message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="#dimen/activity_vertical_margin"
android:layout_marginLeft="#dimen/activity_horizontal_margin"
android:layout_marginRight="#dimen/activity_horizontal_margin"
android:layout_marginTop="#dimen/activity_vertical_margin"
/>
</FrameLayout>
<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"
android:layout_alignParentBottom="true"
app:menu="#menu/navigation" />
this is my webview fragment
<LinearLayout 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"
android:orientation="vertical"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context="com.hackerinside.jaisonjoseph.polysocial.tab1">
<FrameLayout
android:id="#+id/frame1"
android:layout_width="match_parent"
android:layout_height="3dp"
android:background="#android:color/transparent">
<ProgressBar
android:id="#+id/progressBar1"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="fill_parent"
android:layout_height="3dp"
android:background="#android:color/transparent"
android:foregroundGravity="top"
android:progressDrawable="#drawable/custom_progress"
android:progress="20"/>
</FrameLayout>
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/swiperefresh1"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.hackerinside.jaisonjoseph.polysocial.EulaWebView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/webview"
android:focusable="true"
android:focusableInTouchMode="true" />
</android.support.v4.widget.SwipeRefreshLayout>
Try a Custom scrolling behaviour for the BottomNavigationView which allow you to hide it during scroll.
From Link
public final class BottomNavigationBehavior<V extends View> extends VerticalScrollingBehavior<V> {
private static final Interpolator INTERPOLATOR = new LinearOutSlowInInterpolator();
private final BottomNavigationWithSnackbar mWithSnackBarImpl = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? new LollipopBottomNavWithSnackBarImpl() : new PreLollipopBottomNavWithSnackBarImpl();
private boolean isTablet;
private int mTabLayoutId;
private boolean hidden = false;
private ViewPropertyAnimatorCompat mOffsetValueAnimator;
private ViewGroup mTabLayout;
private View mTabsHolder;
private int mSnackbarHeight = -1;
private boolean scrollingEnabled = true;
private boolean hideAlongSnackbar = false;
int[] attrsArray = new int[] {
android.R.attr.id };
public BottomNavigationBehavior() {
super();
}
public BottomNavigationBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
attrsArray);
mTabLayoutId = a.getResourceId(0, View.NO_ID);
a.recycle();
}
public static <V extends View> BottomNavigationBehavior<V> from(#NonNull 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 BottomNavigationBehavior");
}
return (BottomNavigationBehavior<V>) behavior;
}
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency) {
mWithSnackBarImpl.updateSnackbar(parent, dependency, child);
return dependency instanceof Snackbar.SnackbarLayout;
}
#Override
public void onDependentViewRemoved(CoordinatorLayout parent, V child, View dependency) {
updateScrollingForSnackbar(dependency, child, true);
super.onDependentViewRemoved(parent, child, dependency);
}
private void updateScrollingForSnackbar(View dependency, V child, boolean enabled) {
if (!isTablet && dependency instanceof Snackbar.SnackbarLayout) {
scrollingEnabled = enabled;
if (!hideAlongSnackbar && ViewCompat.getTranslationY(child) != 0) {
ViewCompat.setTranslationY(child, 0);
hidden = false;
hideAlongSnackbar = true;
}else if(hideAlongSnackbar){
hidden = true;
animateOffset(child, -child.getHeight());
}
}
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) {
updateScrollingForSnackbar(dependency, child, false);
return super.onDependentViewChanged(parent, child, dependency);
}
#Override
public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
boolean layoutChild = super.onLayoutChild(parent, child, layoutDirection);
if (mTabLayout == null && mTabLayoutId != View.NO_ID) {
mTabLayout = findTabLayout(child);
getTabsHolder();
}
return layoutChild;
}
#Nullable
private ViewGroup findTabLayout(#NonNull View child) {
if (mTabLayoutId == 0) return null;
return (ViewGroup) child.findViewById(mTabLayoutId);
}
#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, #ScrollDirection int scrollDirection) {
if (!scrollingEnabled) return;
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);
mOffsetValueAnimator.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 (mOffsetValueAnimator == null) {
mOffsetValueAnimator = ViewCompat.animate(child);
mOffsetValueAnimator.setDuration(100);
mOffsetValueAnimator.setInterpolator(INTERPOLATOR);
} else {
mOffsetValueAnimator.cancel();
}
}
private void getTabsHolder() {
if (mTabLayout != null) {
mTabsHolder = mTabLayout.getChildAt(0);
}
}
public boolean isScrollingEnabled() {
return scrollingEnabled;
}
public void setScrollingEnabled(boolean scrollingEnabled) {
this.scrollingEnabled = scrollingEnabled;
}
public void setHidden(V view, boolean bottomLayoutHidden) {
if (!bottomLayoutHidden && hidden) {
animateOffset(view, 0);
} else if (bottomLayoutHidden && !hidden) {
animateOffset(view, -view.getHeight());
}
hidden = bottomLayoutHidden;
}
private interface BottomNavigationWithSnackbar {
void updateSnackbar(CoordinatorLayout parent, View dependency, View child);
}
private class PreLollipopBottomNavWithSnackBarImpl implements BottomNavigationWithSnackbar {
#Override
public void updateSnackbar(CoordinatorLayout parent, View dependency, View child) {
if (!isTablet && dependency instanceof Snackbar.SnackbarLayout) {
if (mSnackbarHeight == -1) {
mSnackbarHeight = dependency.getHeight();
}
int targetPadding = child.getMeasuredHeight();
int shadow = (int) ViewCompat.getElevation(child);
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) dependency.getLayoutParams();
layoutParams.bottomMargin = targetPadding - shadow;
child.bringToFront();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
child.getParent().requestLayout();
((View) child.getParent()).invalidate();
}
}
}
}
private class LollipopBottomNavWithSnackBarImpl implements BottomNavigationWithSnackbar {
#Override
public void updateSnackbar(CoordinatorLayout parent, View dependency, View child) {
if (!isTablet && dependency instanceof Snackbar.SnackbarLayout) {
if (mSnackbarHeight == -1) {
mSnackbarHeight = dependency.getHeight();
}
int targetPadding = (mSnackbarHeight +
child.getMeasuredHeight());
dependency.setPadding(dependency.getPaddingLeft(),
dependency.getPaddingTop(), dependency.getPaddingRight(), targetPadding
);
}
}
}
}
apply it in your view:
<android.support.design.widget.BottomNavigationView
android:id="#+id/bottom_navigation"
android:layout_gravity="bottom"
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:layout_behavior=".BottomNavigationBehavior" //apply the behaviour
app:menu="#menu/bottom_navigation_main" />
output:
So easy, use CoordinatorLayout.
1. Change your root View to CoordinatorLayout
Remember to add the dependency in gradle file:
implementation 'com.google.android.material:material:1.1.0'
2. Wrapping Content View into NestedScrollView
<androidx.core.widget.NestedScrollView //or use ScrollView with "android:nestedScrollingEnabled="true"", see below
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<WebView
android:id="#+id/webView_webView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.core.widget.NestedScrollView>
3. Adding "layout_behavior" to your Bottom View
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior">
I need to move the listview up when I show a Snackbar in the bottom of the screen.
The snackbar works perfectly, but hide the content of the bottom of the listview.
This is the layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:elevation="4dp"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar" />
<TextView
android:id="#+id/lblInformacion"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#color/md_green_500"
android:padding="3dip"
android:textColor="#android:color/white"
android:textStyle="bold" />
<android.support.design.widget.CoordinatorLayout
android:id="#+id/clClientes"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ListView
android:id="#+id/lvClientes"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<TextView
android:id="#+id/lblEmpty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="No Result"
android:textSize="20sp"
android:visibility="gone" />
</RelativeLayout>
</android.support.design.widget.CoordinatorLayout>
class ListSnackbarBehavior extends AppBarLayout.ScrollingViewBehavior {
#Override
public boolean layoutDependsOn(CoordinatorLayout parent,
View child, View dependency) {
return super.layoutDependsOn(parent, child, dependency) && dependency instanceof Snackbar.SnackbarLayout;
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child,
View dependency) {
if (dependency instanceof Snackbar.SnackbarLayout) {
updateListTranslationForSnackbar(parent, child, dependency);
}
return false;
}
private void updateListTranslationForSnackbar(CoordinatorLayout parent, View child, View dependency) {
int translationY = (int) getListTranslationYForSnackbar(parent, child);
if (translationY > -1) {
ViewCompat.animate(child).translationY(-translationY).setDuration(100)
.setInterpolator(new LinearOutSlowInInterpolator()).start();
}
}
private float getListTranslationYForSnackbar(CoordinatorLayout parent,
View listView) {
View lastVisibleView = listView instanceof RecyclerView ?
getLastViewFromRecyclerView(listView) : listView instanceof ListView ? getLastViewFromListView(listView) : null;
float minOffset = 0;
if (lastVisibleView != null) {
final List<View> dependencies = parent.getDependencies(lastVisibleView);
for (int i = 0, z = dependencies.size(); i < z; i++) {
final View view = dependencies.get(i);
if (view instanceof Snackbar.SnackbarLayout && parent.doViewsOverlap(lastVisibleView, view)) {
minOffset = Math.min(minOffset,
ViewCompat.getTranslationY(view) - view.getHeight());
}
}
}
return -1;
}
private View getLastViewFromListView(View view) {
ListView listView = (ListView) view;
if (listView.getAdapter() != null && listView.getAdapter().getCount() > 0) {
int lastVisiblePosition = listView.getLastVisiblePosition();
if (lastVisiblePosition == listView.getAdapter().getCount() - 1) {
listView.smoothScrollToPosition(lastVisiblePosition);
return listView.getChildAt(listView.getChildCount() - 1);
}
}
return null;
}
private View getLastViewFromRecyclerView(View listView) {
RecyclerView recyclerView = (RecyclerView) listView;
if (recyclerView.getAdapter() != null && recyclerView.getAdapter().getItemCount() > 0) {
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
int lastPartiallyVisiblePosition = linearLayoutManager.findLastVisibleItemPosition();
if (lastPartiallyVisiblePosition == recyclerView.getAdapter().getItemCount() - 1) {
linearLayoutManager.scrollToPosition(lastPartiallyVisiblePosition);
return linearLayoutManager.getChildAt(lastPartiallyVisiblePosition);
}
}
return null;
}
}
I'm not sure if I got the translation y correct, but here is the deal:
You react only when there are changes on SnackbarLayout
Find if we are near the last item in the list
If the view overlaps, scroll to make it completely visible.
Lift the list to minus the offset when the Snackbar shows up and 0 when hides.
I am working with new FloatingActionButton and CoordinatorLayout in design support library. When I try to add custom Coordinator Behavior on FloatActionButton it work perfect on Lollipop+, but on Pre-Lollipop there are some weird gap margin on FloatActionButton
My main activity layout
<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"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent">
<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" />
</android.support.design.widget.AppBarLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp"
android:paddingBottom="16dp"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
</RelativeLayout>
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="#android:drawable/ic_dialog_email"
app:layout_behavior="com.test.fabdemo.FloatingActionButtonBehavior" />
</android.support.design.widget.CoordinatorLayout>
with custom CoordinatorLayout behavior
public class FloatingActionButtonBehavior extends CoordinatorLayout.Behavior<FloatingActionButton> {
public FloatingActionButtonBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton button, View dependency) {
return dependency instanceof AppBarLayout;
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton floatingActionButton, View dependency) {
if (dependency instanceof AppBarLayout) {
AppBarLayout appBarLayout = (AppBarLayout) dependency;
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) floatingActionButton.getLayoutParams();
int bottomMargin = lp.bottomMargin;
int distanceToScroll = floatingActionButton.getHeight() + bottomMargin;
float ratio = ViewCompat.getY(appBarLayout) / (float) appBarLayout.getTotalScrollRange();
ViewCompat.setTranslationY(floatingActionButton, -distanceToScroll * ratio);
return true;
}
return false;
}
}
Result in emulator 4.0.3 with and without behavior
After some research I found out that FAB's margin is managed by default Behavior, so make custom Behavior inherit from FloatActionButton.Behavior will solve the problem
public class FloatingActionButtonBehavior extends FloatingActionButton.Behavior {
public FloatingActionButtonBehavior(Context context, AttributeSet attrs) {
super();
}
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton button, View dependency) {
return dependency instanceof AppBarLayout || super.layoutDependsOn(parent, button, dependency);
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton floatingActionButton, View dependency) {
if (dependency instanceof AppBarLayout) {
AppBarLayout appBarLayout = (AppBarLayout) dependency;
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) floatingActionButton.getLayoutParams();
int bottomMargin = lp.bottomMargin;
int distanceToScroll = floatingActionButton.getHeight() + bottomMargin;
float ratio = ViewCompat.getY(appBarLayout) / (float) appBarLayout.getTotalScrollRange();
ViewCompat.setTranslationY(floatingActionButton, -distanceToScroll * ratio);
return true;
}
return super.onDependentViewChanged(parent, floatingActionButton, dependency);
}
}