Good Afternoon, I have been trying to add my extended floating button to change to a normal floating button when scrolling but nothing happen and it stays extended and im not sure when:
my orderdetail (Where I call the extension):
recyclerView.addOnScrollListener(FabExtendingOnScrollListener(extended_fab))
where it should do extend or squish:
class FabExtendingOnScrollListener(
private val floatingActionButton: ExtendedFloatingActionButton
) : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
if (newState == RecyclerView.SCROLL_STATE_IDLE
&& !floatingActionButton.isExtended
&& recyclerView.computeVerticalScrollOffset() == 0
) {
floatingActionButton.extend()
}
super.onScrollStateChanged(recyclerView, newState)
}
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
if (dy != 0 && floatingActionButton.isExtended) {
floatingActionButton.shrink()
}
super.onScrolled(recyclerView, dx, dy)
}
}
My xml file(Added only area that matters):
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/btnContainer">
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="#+id/extended_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|bottom"
android:layout_margin="16dp"
android:layout_marginEnd="275dp"
app:icon="#drawable/ic_hourglass"
android:text="Delay order"
android:backgroundTint="#color/standardOrange"
android:layout_marginBottom="667dp">
</com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</RelativeLayout>
</layout>
So this helps to show the button but however it doesnt shrink or extend at all
please use
floatingActionButton.shrink();
Related
I have a RecyclerView inside a CoordinatorLayout along with a FAB :
<androidx.coordinatorlayout.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">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<TextView
android:id="#+id/empty"
android:visibility="invisible"
android:textSize="#dimen/title"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="#dimen/margin"
android:src="#drawable/add"
android:layout_gravity="bottom|right"
app:fabSize="normal"
app:tint="#android:color/white" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
When attaching behaviour to FAB like this :
(fab.layoutParams as CoordinatorLayout.LayoutParams).behavior = ScrollAwareFABBehavior()
Then first click event is not dispatched to item views click listeners (ViewHolder.itemView.setOnClickListener()) when FAB is hidden. Here is behaviour implementation :
class ScrollAwareFABBehavior : FloatingActionButton.Behavior() {
override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout, child: FloatingActionButton, directTargetChild: View, target: View, axes: Int, type: Int): Boolean {
return axes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type)
}
override fun onNestedScroll(coordinatorLayout: CoordinatorLayout, child: FloatingActionButton, target: View, dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int, type: Int, consumed: IntArray) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type, consumed)
if (dyConsumed > 0) {
child.hide(object : OnVisibilityChangedListener() {
override fun onHidden(fab: FloatingActionButton) {
super.onHidden(fab)
fab.visibility = View.INVISIBLE
}
})
} else if (dyConsumed < 0) {
child.show()
}
}
}
OnStartNestedScroll gets called but not the item click listener unless I really take care not to scroll screen from 1px when clicking recycler item.
Everything works as expected when :
removing behaviour from FAB
removing OnVisibilityChangedListener from behaviour implementation
or removing only fab.visibility = View.INVISIBLE from behaviour impl
But without the fab.visibility = View.INVISIBLE FAB never shows up again after it has been hidden by the FabBehaviour (F.A.B Hides but Doesn't Show)...
If anyone can help me show/hide the FAB using behaviour and not have first click not delivered to item view, it would be great !
Using a RecyclerView.OnScrollListener helped me solve this issue in a simpler manner :
// (fab.layoutParams as CoordinatorLayout.LayoutParams).behavior = ScrollAwareFABBehavior()
recyclerView.addOnScrollListener(object: RecyclerView.OnScrollListener() {
var scrolling : Boolean = false
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
if (!scrolling) return
if (dy > 0) {
fab.hide()
} else {
fab.show()
}
}
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
scrolling = newState == RecyclerView.SCROLL_STATE_DRAGGING
}
})
I want to add a headerview that hides when user scrolls to down and shows again when user scrolls to up.
Example: https://imgur.com/a/tTq70B0
As you can see in the link "You are writing as..." pharase is showing only when user scrolls to top. Is there something like that in Android sdk?
How can i achive the same thing?
Obtaining the scroll event is just the first step to achieving this. Animations is required to achieve the effect. I recreated a simple version of the gif example you posted.
Layout for the main activity, activity_main.xml
<?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:animateLayoutChanges="true"> <!-- Note the last line-->
<TextView
android:id="#+id/textview_hello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="8dp"
android:text="Hello Stack Overflow!"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerview_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
Below is the code for the main activity where we populate the RecyclerView and use the addOnScrollListener to provide animations to the TextView. Do note the commented out lines these will provide a default fade-out or fade-in animation due to the noted line in the xml layout above. The method slideAnimation() is an example of creating a custom animation. This link proved useful for creating the animations.
class MainActivity : AppCompatActivity() {
private lateinit var viewAdapter: RecyclerView.Adapter<*>
private lateinit var viewManager: LinearLayoutManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Some data for the RecyclerView
val data: List<String> = (1..100).toList().map { it.toString() }
viewManager = LinearLayoutManager(this)
viewAdapter = TextAdapter(data)
findViewById<RecyclerView>(R.id.recyclerview_main).apply {
setHasFixedSize(true)
layoutManager = viewManager
adapter = viewAdapter
addOnScrollListener(
object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val pastVisibleItems = viewManager.findFirstCompletelyVisibleItemPosition()
if (pastVisibleItems == 0) {
slideAnimation(0f, 1f, View.VISIBLE)
//textview_hello.visibility = View.VISIBLE
} else if (textview_hello.visibility != View.GONE) {
slideAnimation(-150f, 0f, View.GONE)
//textview_hello.visibility = View.GONE
}
}
}
)
}
... // SlideAnimation function
}
}
The slideAnimation function
private fun slideAnimation(translationY: Float, alpha: Float, viewVisibility: Int) {
textview_hello.visibility = View.VISIBLE
textview_hello.clearAnimation()
textview_hello
.animate()
.translationY(translationY)
.alpha(alpha)
.setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
super.onAnimationEnd(animation)
textview_hello.clearAnimation()
textview_hello.visibility = viewVisibility
}
})
.duration = 500
}
The adapter for the RecycleView:
class TextAdapter(private val textList: List<String>) :
RecyclerView.Adapter<TextAdapter.TextViewHolder>() {
class TextViewHolder(val textView: TextView) : RecyclerView.ViewHolder(textView)
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): TextViewHolder {
val textView = LayoutInflater.from(parent.context)
.inflate(R.layout.item_text_view, parent, false) as TextView
return TextViewHolder(textView)
}
override fun onBindViewHolder(holder: TextViewHolder, position: Int) {
holder.textView.text = textList[position]
}
override fun getItemCount() = textList.size
}
Item to display in the RecyclerView, item_text_view.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_margin="8dp"
android:padding="16dp"
android:textAlignment="center"
android:background="#android:color/darker_gray">
</TextView>
Here's my function that detect end off RecyclerView that will be use in pagination later.
private fun setOnScrollListener(categoryRecyclerView: RecyclerView, categoryPresenter: EntertainmentPresenter){
categoryRecyclerView.addOnScrollListener(object: RecyclerView.OnScrollListener(){
override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val totalItemCount:Int = categoryLayoutManager.itemCount
val firstVisibleItemPosition:Int = categoryLayoutManager.findFirstVisibleItemPosition()
val visibleItemCount:Int
val lastVisibleItemPosition: Int = categoryLayoutManager.findLastVisibleItemPosition()
visibleItemCount = lastVisibleItemPosition - firstVisibleItemPosition
Log.e("q", lastVisibleItemPosition.toString())
Log.e("q", dy.toString())
if (loading) {
if (totalItemCount > previousTotal) {
loading = false
previousTotal = totalItemCount
}
}
if (!loading && totalItemCount - visibleItemCount <= firstVisibleItemPosition) {
Log.e("q", lastVisibleItemPosition.toString())
loading = true
}
}
})
}
I used it before on RecyclerView that has linear LayoutManger and every thing was great.
But when I used it with RecyclerView that has GridLayoutManager, things got weird.
It implemented only once and doesn't work when i scroll.
Last visible item position return totalItemCount-1.
Last thing: this I call this function in onCreate and I tried it with RecyclerView that has linear layout manager and everything was great
Here's my logcat,
2018-10-06 06:10:15.919 11033-11033/com.example.karem.moviesdatabase E/q: 0
2018-10-06 06:10:15.919 11033-11033/com.example.karem.moviesdatabase E/q: 18
It only called once and nothing happen when I scroll.
my RecyclerView height was set to match_parent
but after i change height to be 500dp everything works well
i don't know why but anyway here's my old layout
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/layoutBackground"
tools:context=".fragments.AllItemsFragment">
<android.support.v7.widget.RecyclerView
android:id="#+id/categoryRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:listitem="#layout/entertainment_recycler_view_item" />
<ProgressBar
android:id="#+id/loadingCategoryList"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="#+id/categoryRecyclerView"
app:layout_constraintEnd_toEndOf="#+id/categoryRecyclerView"
app:layout_constraintStart_toStartOf="#+id/categoryRecyclerView"
app:layout_constraintTop_toTopOf="#+id/categoryRecyclerView" />
</android.support.constraint.ConstraintLayout>
i only change recyclerview height to 500dp and it works
answer more weird than the question
I have created an app with BottomNavigationView but I want when user scroll the list view then BottomNavigationView should hide. I tried a lot but I am not able to do that. Can anyone help me?
Layout code:
<android.support.design.widget.BottomNavigationView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/windowBackground"
app:menu="#menu/bottom_nav_menu"/>
you must create a class to handle the behavior of the BottomNavigationView.
Below I show a code made in Kotlin.
class BottomNavigationBehavior constructor(
context: Context,
attrs: AttributeSet? = null
) : CoordinatorLayout.Behavior<BottomNavigationView>(context, attrs) {
override fun layoutDependsOn(
parent: CoordinatorLayout,
child: BottomNavigationView,
dependency: View
): Boolean {
return dependency is FrameLayout
}
override fun onStartNestedScroll(
coordinatorLayout: CoordinatorLayout,
child: BottomNavigationView,
directTargetChild: View,
target: View,
axes: Int,
type: Int
): Boolean {
return axes == ViewCompat.SCROLL_AXIS_VERTICAL
}
/*this is where bottomnavigation is shown or hidden*/
override fun onNestedPreScroll(coordinatorLayout: CoordinatorLayout, child: BottomNavigationView,
target: View, dx: Int, dy: Int, consumed: IntArray, type: Int) {
if (dy < 0) {
showBottomNavigationView(child)
} else if (dy > 0) {
hideBottomNavigationView(child)
}
}
private fun hideBottomNavigationView(view: BottomNavigationView) {
view.animate().translationY(view.height.toFloat())
}
private fun showBottomNavigationView(view: BottomNavigationView) {
view.animate().translationY(0f)
}
}
After creating the class, we need to tell the BottomNavigationView which is the class that will control its behavior.
val layoutParams: CoordinatorLayout.LayoutParams = mBottomNavigation.layoutParams as CoordinatorLayout.LayoutParams
layoutParams.behavior = BottomNavigationBehavior(this)
I hope it helps you.
I'm trying to implement the same behaviour than the CollapsingToolbarLayout for a view above my recyclerview that collapses to a minimum height on downscrolling and restores to its normal height when the scroll reaches the top. This header is not a Toolbar, hence the custom view.
I managed to do it, but when scrolling up it restores the height but the whole view starts flickering.
This is the header layout, I am trying to make the Textview disappear while keeping the inner linear layout visible at all times:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center">
<TextView
android:id="#+id/welcome_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:text="welcome"/>
<LinearLayout
android:id="#+id/always_show"
android:layout_width="match_parent"
android:layout_height="36dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_margin="12dp">
...
</LinearLayout>
</LinearLayout>
And this is the code
var maxHeight: Int = 0
var initialSize = false
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
...
header.getViewTreeObserver().addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
if (initialSize)
return
initialSize = true
maxHeight = header.measuredHeight
}
})
recyclerView.addOnScrollListener(object: RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (dy > 0) {
//scroll down
val params = header.layoutParams as LinearLayout.LayoutParams
params.topMargin = Math.max(params.topMargin-dy, -maxHeight/2)
header.layoutParams = params
} else if (layoutManager.findFirstCompletelyVisibleItemPosition() == 0) {
//top
val params = header.layoutParams as LinearLayout.LayoutParams
params.topMargin = 0
header.layoutParams = params
}
}
})
}
Any ideas on how to make this animation more natural, closest to the CollapsingToolbarLayout behaviour.