I'm trying to implement coordinator layout behavior on a custom FAB menu as described here
The program compiles and runs but the coordinator layout behavior functions are never called so the SnackBar overlays the FAB menu.
The only other question on SO about it is here but the solution provided is not my issue.
Here is the xml:
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.getbase.floatingactionbutton.FloatingActionsMenu
android:id="#+id/fab_host"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="#dimen/fab_margin"
app:layout_behavior="com.companionfree.quizinator.couple.shared.FloatingActionMenuBehavior"
fab:fab_icon="#drawable/ic_add_white_24dp"
fab:fab_addButtonColorNormal="#color/colorAccent">
<com.getbase.floatingactionbutton.FloatingActionButton
android:id="#+id/fab_host_bluetooth"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fab:fab_colorNormal="#color/colorAccent"
fab:fab_icon="#drawable/ic_bluetooth_white_24dp"/>
<com.getbase.floatingactionbutton.FloatingActionButton
android:id="#+id/fab_host_nearby"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fab:fab_colorNormal="#color/colorAccent"
fab:fab_icon="#drawable/ic_signal_wifi_4_bar_white_24dp"/>
</com.getbase.floatingactionbutton.FloatingActionsMenu>
</android.support.design.widget.CoordinatorLayout>
And here is my CoordinatorLayout.Behavior class:
public class FloatingActionMenuBehavior extends CoordinatorLayout.Behavior<FloatingActionsMenu> {
public FloatingActionMenuBehavior(Context context, AttributeSet attrs) {}
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionsMenu child, View dependency) {
return dependency instanceof Snackbar.SnackbarLayout;
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionsMenu child, View dependency) {
float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
child.setTranslationY(translationY);
return true;
}
}
There are no errors, the snackbar displays, it just overlays the FAB menu...I can't seem to figure out where the error is.
For the snack bar to push the FAB you need to pass the FloatingActionsMenu view to the snackBar while making it.
Snackbar.make(FloatingActionsMenuVIEWINSTANCEHERE, mErrorMessage, Snackbar.LENGTH_LONG);
and then call the show() method. This should fix the issue
Related
I have bottom nav bar define in the Main activity. I have three fragments linked with BottomNavigation bar in fragments I have recycler view so I want to hide BottomNavigation bar when RecyclerView scrolls down and shows when RecyclerView scrolls up.
My problem is how can I access BottomNavigation bar in fragments because it is defined in MainActivity.
This 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay"
app:elevation="0dp"
android:background="#color/colorPrimary"
android:paddingBottom="7dp"
android:fitsSystemWindows="true">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay"
app:layout_scrollFlags="scroll|enterAlways|snap">
<Spinner
android:layout_width="110dp"
android:layout_height="50dp"
android:id="#+id/chooseLocation"
app:backgroundTint="#android:color/white"/>
</android.support.v7.widget.Toolbar>
<EditText
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:id="#+id/search"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:paddingRight="6dp"
android:paddingLeft="12dp"
android:hint="Search here"
android:textColorHint="#9e9e9e"
android:textColor="#000"
tools:ignore="HardcodedText"
android:background="#drawable/search_edit_text"
android:paddingEnd="6dp"
android:paddingStart="12dp"/>
</android.support.design.widget.AppBarLayout>
<include layout="#layout/content_main" />
<android.support.design.widget.BottomNavigationView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/bottomBar"
android:layout_gravity="bottom"
app:menu="#menu/bottom_menu"
android:background="#fff"
app:itemIconTint="#drawable/nav_check"
app:itemTextColor="#drawable/nav_check"/>
</android.support.design.widget.CoordinatorLayout>
fragment_home.xml
<?xml version="1.0" encoding="utf-8"?>
<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=".Tab1Fragment"
android:background="#fff">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/purchasedBook"/>
</RelativeLayout>
This is how my fragments are defined as there is no bottom nav bar in any fragments so how can I access bottom nav bar in fragments.
Someone, please let me know any help would be appreciated.
THANKS
navController.addOnDestinationChangedListener { _, destination, _ ->
if(destination.id == R.id.full_screen_destination) {
bottomNavigationView.visibility = View.GONE
} else {
bottomNavigationView.visibility = View.VISIBLE
}
}
Do this in the main activity.
Here R.id.full_screen_destination is id of the fragment in navigation fragment.
To access your BottomNavigationView from within the fragments use the following code:
BottomNavigationView navBar = getActivity().findViewById(R.id.bottomBar);
As the fragment is always inside an activity and you can call getActivity() in fragment to access objects that already exist in the activity. So you can do this:
Activity
public class MainActivity extends Activity {
//...
Toolbar toolbar;
//...
public Toolbar getNav() {
return toolbar;
}
//...
}
Fragment
//...
if(getActivity() != null && getActivity instanceOf MainActivity)
((MainActivity)getActivity()).getNav().setVisiblity(View.GONE);
//...
Try this,
Add this line in the BottomNavigationView in Xml
app:layout_behavior="#string/hide_bottom_view_on_scroll_behavior"
And Implement this BottomNavigation behavior using CoOrdinator Layout and you can hide or show the view using the scroll listeners.
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);
}
}
Add this line code to your Activity where it contains bottom navigation
bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_nav);
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams)
bottomNavigationView .getLayoutParams();
layoutParams.setBehavior(new BottomNavigationViewBehavior());
Try this and let me know Digvijay.Happy Coding.
Fragment has onAttach() method which give you context. So you have to create instance of activity using,
MainActivity mainActivity;
#Override
public void onAttach(Context context) {
super.onAttach(context);
mainActivity = (MainActivity)context;
}
Now make method with boolean param which hide and show bottom bar.
public void visibilityOfBottom(boolean isScroll){
if(isScroll){
// hide bottom bar
} else{
// show bottom bar
}
}
Now access above method in fragment using MainActivity context by,
mainActivity.visibilityOfBottom(false);
Kotlin
Access the navigation view from a fragment
val navBar: BottomNavigationView = activity!!.findViewById(R.id.bottomBar)
The only way I found for hiding bottom navigator in V6
was making structure like this -
Stack Nav {
Bottom Nav
Screen 1
Screen 2
Screen 3
}
Add this code to your fragment. will hide the Bottom nav.
AppCompatActivity activity = (AppCompatActivity) view.getContext();
chipNavigationBar = activity.findViewById(R.id.chipNavigation);
chipNavigationBar.animate().translationY(chipNavigationBar.getHeight()).setDuration(1000);
Use this code :
when Scrolling down the Recyclerview to your fragment will hide the bottom navigation.
then when Scrolled Up it will show the Bottom nav.
private View view;
private AppCompatActivity activity;
private ChipNavigationBar chipNavigationBar;
//...............................................
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (view == null) {
view = inflater.inflate(R.layout.list_fragment, container, false);
hide_NavigationBar_adwhen_Scrolling();
}
return view;
}
//...........................................................
private void hide_NavigationBar_adwhen_Scrolling() {
activity = (AppCompatActivity) view.getContext();
chipNavigationBar = activity.findViewById(R.id.chipNavigation);
RecyclerView recyclerView = view.findViewById(R.id.recylerView);
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (dy > 0) {//on_Scrolled_down
// chipNavigationBar.animate().translationY(200).setDuration(500);
chipNavigationBar.animate().translationY(banner_ad_card_1.getHeight()).setDuration(1000);
} else {//on_Scrolled_up
chipNavigationBar.setVisibility(View.VISIBLE);
chipNavigationBar.animate().translationY(0).setDuration(1000);
// chipNavigationBar.setItemSelected(R.id.home, true);
}
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
});
}
Add
app:layout_behavior="#string/hide_bottom_view_on_scroll_behavior"
to
BottomNavigationView
app:layout_behavior="#string/appbar_scrolling_view_behavior"
to
RecyclerView
I am trying to use both AppBarLayout and BottomNavigationLayout in a single CoordinatorLayout and I'm having difficulties hiding the BottomNavigationLayout as required by the material guideline.
I mean something like this:
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="false">
<android.support.design.widget.AppBarLayout
android:id="#+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_insetEdge="top"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="#style/AppTheme.PopupOverlay"
app:layout_scrollFlags="scroll|enterAlways"/>
</android.support.design.widget.AppBarLayout>
<android.support.design.widget.BottomNavigationView
android:id="#+id/bottom_nav"
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_gravity="bottom"
app:menu="#menu/menu_bottom_navigation"/>
<FrameLayout
android:id="#+id/content_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>
As you can see, I also have a FrameLayout that's used to contain a fragment with the actual content. Currently there are no default/built-in behaviors for the BottomNavigationView - neither for the view itself, nor for its siblings. The existing appbar_scrolling_view_behavior handles the content view in coordination with the appbar but ignores other siblings.
I am looking for a solution to hide and show both the appbar and the bottom navigation view on scroll.
After a day or two of searching I settled with a custom Behavior attached to the BottomNavigationView. Its main idea is to detect when the BottomNavigationView's sibling is scrolled so that it can hide the BottomNavigationView. Something like this:
public class BottomNavigationBehavior extends CoordinatorLayout.Behavior<BottomNavigationView> {
public BottomNavigationBehavior() {
super();
}
public BottomNavigationBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, BottomNavigationView child, View dependency) {
boolean dependsOn = dependency instanceof FrameLayout;
return dependsOn;
}
#Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, BottomNavigationView child, View directTargetChild, View target, int nestedScrollAxes) {
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;
}
#Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, BottomNavigationView child, View target, int dx, int dy, int[] consumed) {
if(dy < 0) {
showBottomNavigationView(child);
}
else if(dy > 0) {
hideBottomNavigationView(child);
}
}
private void hideBottomNavigationView(BottomNavigationView view) {
view.animate().translationY(view.getHeight());
}
private void showBottomNavigationView(BottomNavigationView view) {
view.animate().translationY(0);
}
}
As you can see, I'm using simple ViewPropertyAnimator, obtained using the child views's animate method. This leads to a simple animation that doesn't really match the AppBarLayout's behavior but it's decent enough to look good and at the same time it's simple enough to implement.
I expect that at some point the Android team will add a default Behavior for the BottomNavigationView in the support library so I don't think it's reasonable to invest a lot more time to exactly duplicate the AppBarLayout's behavior.
edit (April 2018): see the comments section for a minor clarification about onStartNestedScroll and onNestedPreScroll and their new versions.
You can also use HideBottomViewOnScrollBehavior. This behavior works in basically the same way, but also handles cancelling any existing animations that are running which should be better for performance.
I have a BottomSheetDialogFragment with a RecyclerView. The problem is, I want to disable the drag close function of the BottomSheetDialogFragment as long as the RecyclerView is not scrolled up (currently I can't scroll my RecyclerView as the attempt will always close the BottomSheetDialogFragment). Any ideas how to achieve this?
RecyclerView scrolling problem in BottomSheetDialog can be solved by this way.
from: https://android.jlelse.eu/recyclerview-within-nestedscrollview-scrolling-issue-3180b5ad2542
<?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.widget.NestedScrollView
android:id="#+id/nestedScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</LinearLayout>
Try this in your Fragment that extends RecyclerView in onCreateView
recyclerView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
v.getParent().requestDisallowInterceptTouchEvent(true);
v.onTouchEvent(event);
return true;
}
});
Just add one (!) attribute to your layout:
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/receiversList"
android:nestedScrollingEnabled="false"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:nestedScrollingEnabled="false" doing all the job.
Also, if you wrap around your RecyclerView with SwipeRefreshLayout - refresh function will continue to work. And even you put your layout with more complex Views, your BottomSheetDialog will continue to support drag-down-to-dismiss behaviour (If touch with swipe down gesture occurs outside of RecyclerView & SwipeRefreshLayout).
Just treat it as a BottomSheetDialog , and simply disable its dragging or sliding when touch .
When create a BottomSheetDialog , it will automatically wrap your layout in a
CoordinatorLayout , so if you want to get a behaviour from your view , call
final BottomSheetBehavior behavior = BottomSheetBehavior.from((View) view.getParent());
Then through this behaviour, you can do what you need.
final BottomSheetBehavior behavior = BottomSheetBehavior.from((View) view.getParent());
behavior.setHideable(false);
behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
#Override
public void onStateChanged(#NonNull View bottomSheet, int newState) {
}
#Override
public void onSlide(#NonNull View bottomSheet, float slideOffset) {
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
});
This worked for me.Hope it helps.
When recycle view has scope to scroll, bottom sheet will remain expanded. But if recycle view has no scroll to up direction, than drag from up, will revert normal behaviour.
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
behavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
#Override
public void onStateChanged(#NonNull View bottomSheet, int newState) {
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
dismiss();
}
if (newState == BottomSheetBehavior.STATE_DRAGGING) {
if(!recyclerView.canScrollVertically(1)) {
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
}
}
#Override
public void onSlide(#NonNull View bottomSheet, float slideOffset) {
}
});
try this solution :
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/layout_form"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<!-- elements-->
<!-- elements-->
</LinearLayout>
</androidx.core.widget.NestedScrollView>
Change the Behaviour in the BottomSheetDialogFragment in setupDialog method:
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) ((View) contentView.getParent()).getLayoutParams();
final CoordinatorLayout.Behavior behavior = layoutParams.getBehavior();
if (behavior != null && behavior instanceof BottomSheetBehavior) {
((BottomSheetBehavior) behavior).setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
#Override
public void onStateChanged(#NonNull View bottomSheet, int newState) {
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
dismiss();
}
if (newState == BottomSheetBehavior.STATE_DRAGGING) {
((BottomSheetBehavior) behavior).setState(BottomSheetBehavior.STATE_EXPANDED);
}
}
#Override
public void onSlide(#NonNull View bottomSheet, float slideOffset) {
}
});
}
I had the same situation and what worked for me is just a single line of code inside onCreate function.
What you have to do is just enable recyclerViews nested scrolling.
recyclerView.setNestedScrollingEnabled(true);
I had the same problem recently on my project and I have tried all the answer suggested in this and in other threads on SO but without luck so I decided to solve it my way.
Just a note, my situation it's that I have a BottomSheetDialog with a complex layout that result in something like this
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="_mainLayout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id=" />
<LinearLayout
android:id="">
<ImageView
android:id=""/>
<LinearLayout
android:id="">
<TextView
android:id="" />
<TextView
android:id=""/>
</LinearLayout>
<LinearLayout
android:id="">
<TextView
android:id=""/>
</LinearLayout>
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:layout_marginTop="10dp"
android:id=""
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<Button
android:id="">
<Button
android:id="">
</LinearLayout>
Basically what it's needed it's to add a BottomSheetBehavior to the main layout of the dialog
_bottomSheetBehavior = BottomSheetBehavior.From((View)_mainLayout.Parent);
once we have this we create a custom BottomSheetCallback implementation and we add it to the behavior
_bottomSheetCallback = new BottomSheetFullScreenCallback(_bottomSheetBehavior);
_bottomSheetBehavior.AddBottomSheetCallback(_bottomSheetCallback);
last step it's to create a custo OntouchListener that we will add to the RecyclerView
_recyclerView.SetOnTouchListener(new MyTouchListener(_bottomSheetBehavior));
Now we have everything in place and we just need to manage the touch on the recyclerView. So in our custom OnTouchListener we implement the OnTouch method in this way
public bool OnTouch(View view, MotionEvent e)
{
if (e.Action == MotionEventActions.Down || e.Action == MotionEventActions.Move)
{
_bottomSheetBehavior.Draggable = false;
}
if (e.Action == MotionEventActions.Up)
{
_bottomSheetBehavior.Draggable = true;
}
return true;
}
Once we have done this we need to be sure that the OnSlide event of the custom BottomSheetCallback will never be called once the bottomSheetBehavior it's not draggable, and we can do this in the following way:
public override void OnSlide(View bottomSheet, float slideOffset)
{
if (_bottomSheetBehavior.Draggable)
{
OnSlideEvent?.Invoke(this, slideOffset);
}
}
And that's it ;)
To scroll RecyclerView inside BottomSheet can use this way:
Add android:focusableInTouchMode="true" to RecyclerView in file XML.
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="5dp"
android:clipToPadding="false"
android:focusableInTouchMode="true"
android:paddingBottom="50dp" />
- In func onCreateDialog for BottomSheet:
+ where: maxDesiredHeight is set fixed size of BottomSheet.
+ this.isDraggable = false -> disabling dragging on BottomSheet.
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog
dialog.setOnShowListener {
dialog.findViewById<FrameLayout>(com.google.android.material.R.id.design_bottom_sheet)!!
.apply {
val maxDesiredHeight = (resources.displayMetrics.heightPixels * 0.95).toInt()
val bottomSheetLayoutParams = this.layoutParams
bottomSheetLayoutParams.height = maxDesiredHeight
this.layoutParams = bottomSheetLayoutParams
BottomSheetBehavior.from(this).apply {
this.state = BottomSheetBehavior.STATE_EXPANDED
this.isDraggable = false
}
}
}
return dialog
}
- Call the setOnTouchListener function for RecyclerView:
Recycler.setOnTouchListener { v, event ->
v.parent.requestDisallowInterceptTouchEvent(true)
v.onTouchEvent(event)
true
}
With Constraint layout
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_team_mates"
android:layout_width="#dimen/dp_0"
android:layout_height="#dimen/dp_0"
android:layout_marginVertical="#dimen/dp_20"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/txt_title_custom"
app:layout_constraintBottom_toBottomOf="parent"/>
Put your RecyclerView under NestedScrollView where NestedScroll is parent of recycler
I want to show a CardView as prompt like in Material Guidelines Specs:
https://www.google.com/design/spec/growth-communications/onboarding.html#onboarding-quickstart
Here is the link to the image:
Image
I've changed the behavior of the FAB Button like this and set to the .xml file:
public class FABBehavior extends FloatingActionButton.Behavior {
public FABBehavior(Context context, AttributeSet attr) {
super();
}
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
return dependency instanceof CardView;
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
child.setTranslationY(translationY);
return true;
}
}
My .xml file look like 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:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="com.nearme.client.activities.fragments.ScannerFragment">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:title="#string/title_scanner"
app:popupTheme="#style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="#layout/fragment_scanner_content" />
<android.support.v7.widget.CardView
android:id="#+id/cvBluetooth"
android:layout_width="match_parent"
android:layout_height="150dp"
android:animateLayoutChanges="true"
android:layout_gravity="bottom" >
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Prueba" />
</android.support.v7.widget.CardView>
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|bottom|end"
android:layout_margin="#dimen/fab_margin"
app:layout_behavior="com.nearme.client.utils.FABBehavior"
android:src="#android:drawable/ic_dialog_email" />
And the click listener of the FAB Button is this:
View.OnClickListener onClick = new View.OnClickListener() {
#Override
public void onClick(View v) {
if (v == fab) {
if (cardBluetooth.isShown()) {
cardBluetooth.setVisibility(View.GONE);
} else
cardBluetooth.setVisibility(View.VISIBLE);
}
}
};
The problem is when the CardView's visibility goes GONE the FAB button remain up in its position.
How can I update the position of the FAB when the CardView is GONE?
Even if I set the CardView in the CoordinatorLayout dinamically and remove it doesnt work.
Well, the problem was that onDependentViewChangedfrom FloatingActionButton.Behavior only get called when the dependent view have changed its position. But I only was changing its visibility so it didnt get called.
Now in the onClick I add to the view and remove it:
View.OnClickListener onClick = new View.OnClickListener() {
#Override
public void onClick(View v) {
if (v == fab) {
if (cardBluetooth.isShown()) {
coordinatorLayout.removeView(cardBluetooth);
} else
coordinatorLayout.addView(cardBluetooth);
}
}
};
And in FloatingActionButton.Behavior I added:
#Override
public void onDependentViewRemoved(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
child.animate().translationY(0);
child.setTranslationY(0);
}
And this is the result:
GIF of result
Change the method layoutDependsOn method according to this:
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
return dependency instanceof CardView && onDependentViewChanged(parent, child, dependency);
}
Otherwise, the fab button is going to move up only the first time it gets clicked.
I've seen various posts about FABs responding to Snackbar popups at the bottom of the screen as well as scroll-sensitive FABs. But is there some implementation of FloatingActionButton.Behavior (or similar) to move the FAB above the keyboard when it pops up?
Right now, the keyboard covers the FAB when I click for example in an EditText box. My goal is to animate the FAB so it is always visible, independent of the keyboard status.
EDIT: Both android:windowSoftInputMode="adjustResize" and ...="adjustPan" won't change anything. Well, adjustResize resizes the underlying layout (which is in my case a map) but the FAB doesn't move.
Hi there i know it's old but for future or current readers/searchers and also the thread maker, he hasn't found answer yet. This is how i am having this behaviour in my app.
Fab hides on RecyclerView scroll, goes up when snack bar pops out, if fab is not shown and snackbar popus up and if you scroll then still Fab will be shown top of snack bar and will move down when SB disappears and last with keypad if it opens Fab will be pushed up. (sorry, i had to write coz i don't know how to give gif with eclipse emulator)
Image
Layout
<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/layout_drawer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/GrayGood"
android:clipToPadding="false"
android:fitsSystemWindows="true" >
<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/layout_coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.design.widget.AppBarLayout
android:id="#+id/layout_appLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/ThemeOverlay.AppCompat.Dark" >
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways|snap"
android:background="?attr/colorPrimary" />
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerViewMain"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:clipToPadding="false" />
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom|end"
android:layout_margin="#dimen/floating_action_button_margin"
app:layout_behavior="com.faisal.cvcd.anim.ScrollingFABAnimation"
android:src="#drawable/ic_fab"
android:tint="#color/White"
app:backgroundTint="#color/colorPrimary"
app:borderWidth="0dp"
app:elevation="6dp"
app:fabSize="normal" />
</android.support.design.widget.CoordinatorLayout>
</android.support.v4.widget.DrawerLayout>
As you can see i am using FabAnimation class to override some of its default methods.
ScrollingFABAnimation
import android.content.Context;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.View;
public class ScrollingFABAnimation extends
CoordinatorLayout.Behavior<FloatingActionButton> {
public ScrollingFABAnimation(Context context, AttributeSet attrs) {
super(context, attrs);
}
//For SnackBar to push up the Floating Button when it pops up
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
return dependency instanceof Snackbar.SnackbarLayout;
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
child.setTranslationY(translationY);
return true;
}
//For FAB to hide or show on scroll
#Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View directTargetChild, View target,
int nestedScrollAxes) {
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll(coordinatorLayout, child,
directTargetChild, target, nestedScrollAxes);
}
#Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View target, int dxConsumed,
int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
if (dyConsumed > 0 && child.getVisibility() == View.VISIBLE) {
child.hide();
} else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {
child.show();
}
}
}
I had the same problem, i tried to add a ScrollView inside my root view but it did not works because the content doesn't exceed the display's height (the fab react as expected in that case).
So i tried android:windowSoftInputMode="adjustResize" and it works for me.
For informations here's what my layout xml looks like:
<?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">
<ManyViewsExceptScrollViews/>
<android.support.design.widget.FloatingActionButton
[..]
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"/>
</RelativeLayout>
it's a Fragment inflated in an activity (in a FrameLayout that match_parent the root view) for which i added android:windowSoftInputMode="adjustResize" in the manifest.