Why does Bottom Sheet Dialog not expand on tablet? - android

I have impelemented a BottomSheetDialog in my app but when I install it on a tablet and have the tablet laying down it does not expand fully on first click. It expands up to Collapsed state first and you have to drag it up to see everything. Why does it do this? Is it some setting you can change in your style?
<LinearLayout 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:orientation="vertical"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
app:behavior_peekHeight="0dp"
>
...
</LinearLayout>
val view = layoutInflater.inflate(R.layout.home_bottom_sheet_dialog, null)
val bottomSheetDialog = BottomSheetDialog(activity!!)
bottomSheetDialog.setContentView(view)
bottomSheetDialog.show()
I use API 22 AndroidX with kotlin.

As Sinan Ceylan said this part of the layout is not needed.
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
app:behavior_peekHeight="0dp"
But to fix my problem I sat the peakHeight variable of BottomSheetBehavior to something large before it is shown.
bottomSheetDialog.setContentView(view)
bottomSheetDialog.behavior.peekHeight = 1000
bottomSheetDialog.show()

Implementing fully expanded bottom sheet a little tricky. You should override onViewCreated method in your BottomSheetDialogFragment class and listen GlobalLayout as below:
(Java Code)
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
BottomSheetDialog dialog = (BottomSheetDialog) getDialog();
if (dialog != null) {
FrameLayout bottomSheet = dialog.findViewById(com.google.android.material.R.id.design_bottom_sheet);
if (bottomSheet != null) {
BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
bottomSheetBehavior.setPeekHeight(0);
bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
#Override
public void onStateChanged(#NonNull View view1, int i) {
if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_COLLAPSED || bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_HIDDEN) {
if (!isStateSaved())
dismissAllowingStateLoss();
}
}
#Override
public void onSlide(#NonNull View view1, float v) {
}
});
}
}
});
}
Additionally no need that attributes in xml:
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
app:behavior_peekHeight="0dp"

Related

How to hide bottom nav bar in fragment

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

How i can set Half Expanded state for my BottomSheet

I have layout with bottom sheet.
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="#+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:contentScrim="#color/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed" />
</com.google.android.material.appbar.AppBarLayout>
<include layout="#layout/content_main_weather_map" />
<include layout="#layout/bottom_sheet" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Bottom sheet layout
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView 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/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/white"
android:clipToPadding="true"
app:behavior_peekHeight="80dp"
app:layout_behavior="#string/bottom_sheet_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/weather_recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
tools:listitem="#layout/item_weather" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
It is necessary for me that my bottom sheet opens first half, and after re-dragging it opens to full screen. How is it done in google maps app. But I have no idea how to do this.
It is better to use the framework with its full potential. As official documentation states for method setFitToContents :
Sets whether the height of the expanded sheet is determined by the height of its contents, or if it is expanded in two stages (half the height of the parent
container, full height of parent container). Default value is true.
So all you need is set setFitToContent to false with:
behavior = BottomSheetBehavior.from(your_bottom_sheet_xml)
behavior.isFitToContents = false
behavior.halfExpandedRatio = 0.6f
With this 3-line-code the bottom sheet will expand till 60% of the screen at first, and afterwards it will fully expand to 100%.
Hope it helps!
Just set BottomSheetBehaivor state to BottomSheetBehavior.STATE_HALF_EXPANDED.
Also if you need after full expanding let user again go back to half expanded mode, you need to set peek height to half of window height.
val bottomSheetBehavior = BottomSheetBehavior.from<NestedScrollView>(bottom_sheet)
val metrics = resources.displayMetrics
bottomSheetBehavior.peekHeight = metrics.heightPixels / 2
bottomSheetBehavior.state = BottomSheetBehavior.STATE_HALF_EXPANDED
I have tried the #Massab and #HeyAlex but didn't match my desired behavior.
With the following solution in kotlin, if your bottomsheet sliding is near the expanded state, it stays expanded, if is near the half state, stays at half and if it's near collapsed, it stays collapsed:
val bottomSheet = view.findViewById<View>(R.id.bottom_sheet1)
val mBottomSheetBehavior = BottomSheetBehavior.from(bottomSheet)
mBottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
mBottomSheetBehavior.addBottomSheetCallback(object: BottomSheetBehavior.BottomSheetCallback(){
override fun onStateChanged(bottomSheet: View, newState: Int) {
}
override fun onSlide(bottomSheet: View, slideOffset: Float) {
val upperState = 0.66
val lowerState = 0.33
if (bottomSheetEventsFilterBehavior.state == BottomSheetBehavior.STATE_SETTLING ) {
if(slideOffset >= upperState){
mBottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
}
if(slideOffset > lowerState && slideOffset < upperState){
mBottomSheetBehavior.state = BottomSheetBehavior.STATE_HALF_EXPANDED
}
if(slideOffset <= lowerState){
mBottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
}
}
}
})
Although this question has been answered, but just got another way to implement this behavior so sharing for others.
Create a global variable and initialize it with the default state of your BottomSheetBehavior, like
int state = BottomSheetBehavior.STATE_COLLAPSED;
Then, in BottomSheetBehavior.BottomSheetCallback update your state variable to the current state
and in
BottomSheetBehavior.STATE_DRAGGING, if state is not half expanded,
set the state to BottomSheetBehavior.STATE_HALF_EXPANDED
sheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
#Override
public void onStateChanged(#NonNull View view, int i) {
switch (i) {
case BottomSheetBehavior.STATE_COLLAPSED:
state = BottomSheetBehavior.STATE_COLLAPSED;
binder.imgRefresh.setVisibility(View.GONE);
break;
case BottomSheetBehavior.STATE_EXPANDED:
binder.imgRefresh.setVisibility(View.VISIBLE);
state = BottomSheetBehavior.STATE_EXPANDED;
break;
case BottomSheetBehavior.STATE_DRAGGING:
if (state != BottomSheetBehavior.STATE_HALF_EXPANDED) {
sheetBehavior.setState(BottomSheetBehavior.STATE_HALF_EXPANDED);
}
break;
case BottomSheetBehavior.STATE_HALF_EXPANDED:
state = BottomSheetBehavior.STATE_HALF_EXPANDED;
break;
}
}
#Override
public void onSlide(#NonNull View view, float v) {
binder.viewExtender.setAlpha(1 - v);
}
});
This will make your BottomSheet to take three steps , i.e., Collapsed, Half Expanded, Expanded.
Hope it can help someone!
class BottomSheetFragment : BottomSheetDialogFragment() {
/* inside of your Bottom Sheet Dialog Fragment */
override fun onStart() {
super.onStart()
BottomSheetBehavior.from(requireView().parent as View).apply {
state = BottomSheetBehavior.STATE_HALF_EXPANDED
}
}
}
Use this block in onCreateView before returning root view
dialog!!.setOnShowListener { dialog ->
val d = dialog as BottomSheetDialog
BottomSheetBehavior.from(requireView().parent as View).apply {
state = BottomSheetBehavior.STATE_EXPANDED
}
}

How can i create this layout?

I am working on app which requires layout as described below.
There are four sections in layout, let's say Layout A, B, C and D.
Layout A is AppBar and fixed on top.
Layout D is hidden and appears on scrolling up with some additional effects.
On scrolling up Layout-D appears from bottom while Layout-C starts fading.
Everything remains as it is until Layout-D completely overlaps Layout-C.
After Layout-D completely overlaps Layout-C, on further scrolling up Layout B starts scrolling up.
Similarly on scrolling down, first Layout-D scrolls down then Layout-B appears. On further scrolling down Layout-D gets hide under and Layout-C appears again with fade-out effect.
I already have spent two days and tried using BottomSheet with Coordinator layout but couldn't achieve Exact effects. In addition with BottomSheet scrolling effect isn't that smooth i.e if appears and hide abruptly.
Another thing that i'm thinking to use is Custom behaviour with Coordinator layout but not sure how to proceed.
.
use a fragment with a bottom sheet behavior as follow from the parent activity or fragment :
<android.support.design.widget.CoordinatorLayout
android:id="#+id/bottomsheetfragmenCoordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorBlackOpacity"
android:clickable="true"
>
<android.support.constraint.ConstraintLayout
android:id="#+id/bottomsheetfragmentLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:behavior_hideable="true"
app:behavior_peekHeight="50dp"
app:elevation="4dp"
app:layout_behavior="#string/bottom_sheet_behavior">
<fragment
android:id="#+id/bottomsheetfragment"
android:name="com.example.inslem.it.BottomSheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent" />
</android.support.constraint.ConstraintLayout>
</android.support.design.widget.CoordinatorLayout>
bottomsheet fragment :
public class BottomSheet extends Fragment {
public BottomSheet() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_bottom_sheet, container, false);
return view;
}
#Override
public void onViewCreated(#NonNull final View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
and from the parent activity or parent fragment
get the bottom sheet
#Override
public void onViewCreated(#NonNull final View view, #Nullable Bundle savedInstanceState) {
bottomsheetfragmenCoordinatorLayout = view.findViewById(R.id.bottomsheetfragmenCoordinatorLayout);
behavior = BottomSheetBehavior.from(bottomsheetfragmentLayout);
behavior.setPeekHeight(250);
behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
#Override
public void onStateChanged(#NonNull View bottomSheet, int newState) {
switch (newState) {
case BottomSheetBehavior.STATE_DRAGGING:
break;
case BottomSheetBehavior.STATE_SETTLING:
break;
case BottomSheetBehavior.STATE_EXPANDED:
break;
case BottomSheetBehavior.STATE_COLLAPSED:
break;
case BottomSheetBehavior.STATE_HIDDEN:
break;
}
}
#Override
public void onSlide(#NonNull View bottomSheet, float slideOffset) {
Log.i("BottomSheetCallback", "slideOffset: " + slideOffset);
}
});
}

BottomSheetDialogFragment - Allow scrolling child

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

FAB Behavior when showing CardView (Up & Down)

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.

Categories

Resources