How to scroll up BottomNavigationBar with last item in Recycler view - android

I'm trying to make scrolling Bottom navigation bar view, which will scroll down when scrolling down, scroll up, when scrolling up, and scroll up with last item of recycler view when scrolling to the end. But I don't know how to implement the last thing. This is my BottomNavigationBehavior class:
public class NavigationBehavior extends CoordinatorLayout.Behavior {
private float height = 0;
public final void BottomNavigationBehavior() {
}
public NavigationBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onLayoutChild(#NonNull #NotNull CoordinatorLayout parent, #NonNull #NotNull View child, int layoutDirection) {
ViewGroup.MarginLayoutParams paramsCompat =
(ViewGroup.MarginLayoutParams) child.getLayoutParams();
height = child.getMeasuredHeight() + paramsCompat.bottomMargin;
return super.onLayoutChild(parent, child, layoutDirection);
}
#Override
public boolean onStartNestedScroll(#NonNull #NotNull CoordinatorLayout coordinatorLayout, #NonNull #NotNull View child, #NonNull #NotNull View directTargetChild, #NonNull #NotNull View target, int axes, int type) {
return axes == ViewCompat.SCROLL_AXIS_VERTICAL;
}
#Override
public void onNestedPreScroll(#NonNull #NotNull CoordinatorLayout coordinatorLayout, #NonNull #NotNull View child, #NonNull #NotNull View target, int dx, int dy, #NonNull #NotNull int[] consumed, int type) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
child.setTranslationY(Float.max(0.0F, Float.min(height, child.getTranslationY() + (float)dy)));
}
public void setEndOfList(boolean endOfList) {
this.endOfList = endOfList;
}
}
Can someone help me ?

Related

Want to create a custom FrameLayout class that implements NestedScrollParent3

I'm new to android development. currently I'm creating a widget so I need to create an FrameLayout with scroll effect. When look at the android docs come across the abstract/interface class NestedScrollParent3. Is this the correct way to do this?
package com.oasis.motion;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.view.NestedScrollingParent3;
public class CustomDrawerView extends ConstraintLayout implements NestedScrollingParent3 {
private NestedScrollingParent3 motionLayout;
public CustomDrawerView(Context context) {
super(context);
motionLayout = (NestedScrollingParent3) getParent();
}
public CustomDrawerView(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
motionLayout = (NestedScrollingParent3) getParent();
}
public CustomDrawerView(Context context, AttributeSet attributeSet, int defStyleAttr) {
super(context, attributeSet, defStyleAttr);
motionLayout = (NestedScrollingParent3) getParent();
}
#Override
public boolean onStartNestedScroll(View child, View target, int axes, int type) {
return motionLayout.onStartNestedScroll(child, target, axes, type);
}
#Override
public void onNestedScrollAccepted(View child, View target, int axes, int type) {
motionLayout.onNestedScrollAccepted(child, target, axes, type);
}
#Override
public void onStopNestedScroll(View target, int type) {
motionLayout.onStopNestedScroll(target, type);
}
#Override
public void onNestedScroll(View target, int dx, int dy, int consumed, int unconsumed, int type) {
motionLayout.onNestedScroll(target, dx, dy, consumed, unconsumed, type);
}
#Override
public void onNestedPreScroll(View target, int dx, int dy, int[] windowInset, int type) {
motionLayout.onNestedPreScroll(target, dx, dy, windowInset, type);
}
#Override
public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed,
int type, int[] windowInset) {
motionLayout.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type, windowInset);
}
}
Is this the correct way to make a FrameLayout scrollable? Especially horizontal scrollable?

how to restore recycler view item after swipe

I have a recyclerview and I've add swipe to it with itemtouchhelper class and it works fine, there is 2 views in layout that foreground view moves on swipe over bacjground layer, problem is I want to foreground view get the previous location after swipe , like swipe effect that make call in Samsung phones.
I can make that with calling notifyitemchanged after swipe. but I works one time and if I swipe again on that item, view is not restored
this is my itemtouchhelper class
public class RecyclerItemTouchHelperlog extends ItemTouchHelper.SimpleCallback {
private RecyclerItemTouchHelperListener listener;
public RecyclerItemTouchHelperlog(int dragDirs, int swipeDirs, RecyclerItemTouchHelperListener listener) {
super(dragDirs, swipeDirs);
this.listener = listener;
}
#Override
public boolean onMove(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder, #NonNull RecyclerView.ViewHolder target) {
return true;
}
#Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
if (viewHolder != null) {
final View foregroundView = ((recycleAdapterlog.MyViewHolder) viewHolder).logitm;
getDefaultUIUtil().onSelected(foregroundView);
}
}
#Override
public void onChildDrawOver(Canvas c, RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder, float dX, float dY,
int actionState, boolean isCurrentlyActive) {
final View foregroundView = ((recycleAdapterlog.MyViewHolder) viewHolder).logitm;
getDefaultUIUtil().onDrawOver(c, recyclerView, foregroundView, dX, dY,
actionState, isCurrentlyActive);
}
#Override
public void clearView(#NonNull RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
final View foregroundView = ((recycleAdapterlog.MyViewHolder) viewHolder).logitm;
getDefaultUIUtil().clearView(foregroundView);
}
#Override
public int getSwipeDirs(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder) {
View hv=((recycleAdapterlog.MyViewHolder) viewHolder).hv;
if(hv==null)return super.getSwipeDirs(recyclerView, viewHolder);
else if(hv.getVisibility()==View.VISIBLE)return 0;
else return super.getSwipeDirs(recyclerView, viewHolder);
}
#Override
public void onChildDraw(Canvas c, #NonNull RecyclerView recyclerView,
#NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY,
int actionState, boolean isCurrentlyActive) {
final View foregroundView = ((recycleAdapterlog.MyViewHolder) viewHolder).logitm;
final View swipel = ((recycleAdapterlog.MyViewHolder) viewHolder).swipelogleft;
final View swiper = ((recycleAdapterlog.MyViewHolder) viewHolder).swipelogright;
if(dX<0){
swipel.setVisibility(View.VISIBLE);
swiper.setVisibility(View.GONE);
}
else {
swipel.setVisibility(View.GONE);
swiper.setVisibility(View.VISIBLE);
}
if(actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
final Float alpha =1 - Math.abs (dX) / ( viewHolder.itemView.getWidth ());
foregroundView.setAlpha (alpha);
foregroundView.setTranslationX (dX);
}
recentconfrag.swipedlog=true;
getDefaultUIUtil().onDraw(c, recyclerView, foregroundView, dX, dY,
actionState, isCurrentlyActive);
}
#Override
public void onSwiped(#NonNull RecyclerView.ViewHolder viewHolder, int direction) {
listener.onSwiped(viewHolder, direction, viewHolder.getAdapterPosition());
}
#Override
public int convertToAbsoluteDirection(int flags, int layoutDirection) {
return super.convertToAbsoluteDirection(flags, layoutDirection);
}
public interface RecyclerItemTouchHelperListener {
void onSwiped(RecyclerView.ViewHolder viewHolder, int direction, int position);
}
}
I don't know, where is a problem, I tested the solution below on emulator and Samsung Galaxy S4.
public class RecyclerItemTouchHelper extends ItemTouchHelper.SimpleCallback {
private RecyclerItemTouchHelperListener listener;
public RecyclerItemTouchHelper(int dragDirs, int swipeDirs, RecyclerItemTouchHelperListener listener) {
super(dragDirs, swipeDirs);
this.listener = listener;
}
#Override
public boolean onMove(#NotNull RecyclerView recyclerView,
#NotNull RecyclerView.ViewHolder viewHolder,
#NotNull RecyclerView.ViewHolder target) {
return true;
}
#Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
if (viewHolder instanceof YourAdapter.ItemViewHolder) {
final View foregroundView = ((YourAdapter.ItemViewHolder) viewHolder).getItem();
getDefaultUIUtil().onSelected(foregroundView);
}
}
#Override
public void onChildDrawOver(#NotNull Canvas c, #NotNull RecyclerView recyclerView,
#NotNull RecyclerView.ViewHolder viewHolder, float dX, float dY,
int actionState, boolean isCurrentlyActive) {
if (viewHolder instanceof YourAdapter.ItemViewHolder) {
final View foregroundView = ((YourAdapter.ItemViewHolder) viewHolder).getItem();
getDefaultUIUtil().onDrawOver(c, recyclerView, foregroundView, dX, dY,
actionState, isCurrentlyActive);
}
}
#Override
public void clearView(#NotNull RecyclerView recyclerView, #NotNull RecyclerView.ViewHolder viewHolder) {
if (viewHolder instanceof YourAdapter.ItemViewHolder) {
final View foregroundView = ((YourAdapter.ItemViewHolder) viewHolder).getItem();
getDefaultUIUtil().clearView(foregroundView);
}
}
#Override
public void onChildDraw(#NotNull Canvas c, #NotNull RecyclerView recyclerView,
#NotNull RecyclerView.ViewHolder viewHolder, float dX, float dY,
int actionState, boolean isCurrentlyActive) {
if (viewHolder instanceof YourAdapter.ItemViewHolder) {
final View foregroundView = ((YourAdapter.ItemViewHolder) viewHolder).getItem();
getDefaultUIUtil().onDraw(c, recyclerView, foregroundView, dX, dY,
actionState, isCurrentlyActive);
}
}
#Override
public void onSwiped(#NotNull RecyclerView.ViewHolder viewHolder, int direction) {
listener.onSwiped(viewHolder, direction, viewHolder.getAdapterPosition());
}
public interface RecyclerItemTouchHelperListener {
void onSwiped(RecyclerView.ViewHolder viewHolder, int direction, int position);
}
}
Then use it in a fragment.
class YourFragment : Fragment(), RecyclerItemTouchHelper.RecyclerItemTouchHelperListener {
private var adapter: YourAdapter? = null
private lateinit var itemTouchHelperCallback: RecyclerItemTouchHelper
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val view = inflater.inflate(LAYOUT, container, false)
adapter = YourAdapter(this)
view.recycler_view.run {
layoutManager = LinearLayoutManager(context)
adapter = this#YourFragment.adapter
setHasFixedSize(true)
// attaching the touch helper to recycler view
itemTouchHelperCallback =
RecyclerItemTouchHelper(0, ItemTouchHelper.LEFT, this#YourFragment)
ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(this)
}
return view
}
// callback when recycler view is swiped
// item will be removed on swiped
override fun onSwiped(viewHolder: RecyclerView.ViewHolder?, direction: Int, position: Int) {
// if (viewHolder is YourAdapter.ItemViewHolder) {
// remove the item from recycler view
// This is a custom dialog with "Yes", "No" buttons.
showDialog("Remove the item?",
{ adapter?.removeAt(position) },
{ adapter?.notifyItemChanged(position) })
// }
}
Notice that I use position in onSwiped, but viewHolder.getAdapterPosition() is also right. I used a solution for reverting an item from Android RecyclerView ItemTouchHelper revert swipe and restore view holder.

onClick method inside RecyclerView not working after NestedScrollView scrolled

I checked this stackoverflow question cause it's very similar, but the Google's bug have been fixed in current versions, but I still having the problem.
I have an RecyclerView inside a NestedScrollView, after NestedScrollView scrolled if I click on item inside RecyclerView, onClick method does not work propertly.
Can anyone help me? Thanks
Okey, I found the solution here, we need:
public class FixAppBarLayoutBehavior extends AppBarLayout.Behavior {
public FixAppBarLayoutBehavior() {
super();
}
public FixAppBarLayoutBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target,
int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed,
dxUnconsumed, dyUnconsumed, type);
stopNestedScrollIfNeeded(dyUnconsumed, child, target, type);
}
#Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
View target, int dx, int dy, int[] consumed, int type) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
stopNestedScrollIfNeeded(dy, child, target, type);
}
private void stopNestedScrollIfNeeded(int dy, AppBarLayout child, View target, int type) {
if (type == ViewCompat.TYPE_NON_TOUCH) {
final int currOffset = getTopAndBottomOffset();
if ((dy < 0 && currOffset == 0)
|| (dy > 0 && currOffset == -child.getTotalScrollRange())) {
ViewCompat.stopNestedScroll(target, ViewCompat.TYPE_NON_TOUCH);
}
}
}
}
and, in our AppBarLayout:
<android.support.design.widget.AppBarLayout>
...
app:layout_behavior="your.package.FixAppBarLayoutBehavior"
...
</android.support.design.widget.AppBarLayout>
Your RecyclerView shouldn't permit nested scrolling, so it must have nestedScrollingEnabled="false"
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"/>

Vertical Scrolling CoordinatorLayout Behavior for BottomNavigationView with RecyclerView vertical scrolling

How to define a CoordinatorLayout.Behavior class for BottomNavigationView which scrolls synchronized with the RecyclerView veritical scroll.
I have seen this and this, but all it does is just show/hide the NavigationView on veritical scroll events immediately. I don't want to show/hide the NavigationView immediately, instead i want a behavior something like the AppbarLayout having a Toolbar with scroll flag as app:layout_scrollFlags="scroll|enterAlways".
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);
}
}
After some trying I came up with this solution:
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if(dy > 0 && visible){
mBinding.bnv.test.setY(mBinding.bnv.getY() + dy);
DisplayMetrics metrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
visible = mBiding.bnv.getY() > metrics.heightPixels;
if(!visible) {
mBinding.bnv.setY(metrics.heightPixels);
}
} else {
mBinding.bnv.setY(mBinding.bnv.getY() + dy);
DisplayMetrics metrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
visible = mBinding.bnv.getY() > metrics.heightPixels;
}
}
So you are scrollling the BottomNavigationView with the recycler view
Or with CoordinatorLayout.Behavior class:
public class ViewScrollWithRecyclerViewBehavior extends CoordinatorLayout.Behavior<View> {
private boolean visible = true;
private boolean inStartPosition = true;
private float oldY;
private DisplayMetrics metrics;
public ViewScrollWithRecyclerViewBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
metrics = Resources.getSystem().getDisplayMetrics();
}
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, View fab, View dependency) {
return dependency instanceof AppBarLayout;
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
if (dependency instanceof AppBarLayout) {
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
float dy = oldY - dependency.getY();
if(dy > 0 && visible){
moveDown(child, oldY);
} else if(!inStartPosition) {
moveUp(child, oldY);
}
oldY = dependency.getY();
}
return true;
}
private void moveUp(View child, float dy){
if(child.getY() + dy >= metrics.heightPixels - child.getHeight()){
child.setY(metrics.heightPixels - child.getHeight());
} else {
child.setY(child.getY() + dy);
}
inStartPosition = child.getY() == metrics.heightPixels - child.getHeight();
visible = child.getY() > metrics.heightPixels;
}
private void moveDown(View child, float dy){
child.setY(child.getY() + dy);
visible = child.getY() > metrics.heightPixels;
if(!visible) {
child.setY(metrics.heightPixels);
}
}
#Override
public boolean onStartNestedScroll(final CoordinatorLayout coordinatorLayout, final View child,
final View directTargetChild, final View target, final int nestedScrollAxes) {
return true;
}
#Override
public void onNestedScroll(final CoordinatorLayout coordinatorLayout,
final View child,
final View target, final int dxConsumed, final int dy,
final int dxUnconsumed, final int dyUnconsumed) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dy, dxUnconsumed, dyUnconsumed);
if(dy > 0 && visible){
moveDown(child, dy);
} else if(!inStartPosition) {
moveUp(child, dy);
}
}
}

Android -FAB Behaviour with half list

I have the FAB working when recyclerview has enough items to scroll, but i need to handle the case when recyclerview does not scroll (the total of items do not cover the screen).
At the moment this is how I handle the scroll:
public class FABBehavior extends FloatingActionButton.Behavior {
public FABBehavior() {
super();
}
public FABBehavior(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
#Override
public void onNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child, final View target, final int dxConsumed, final int dyConsumed, final int dxUnconsumed, final int dyUnconsumed) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
if (dyConsumed > 0) {
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
int fab_bottomMargin = layoutParams.bottomMargin;
child.animate().translationY(child.getHeight() + fab_bottomMargin).setInterpolator(new LinearInterpolator()).start();
} else if (dyConsumed < 0) {
child.animate().translationY(0).setInterpolator(new LinearInterpolator()).start();
}
}
#Override
public boolean onStartNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child, final View directTargetChild, final View target, final int nestedScrollAxes) {
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;
}
}
How to handle when recyclerview has few items?
You have to handle another case independently from CoordinatorLayout.
override a function layoutDependsOn:
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
return super.layoutDependsOn(parent, child, dependency) || dependency instanceof RecyclerView;
}
onNestedScroll should also handle another case:
if (target instanceof RecyclerView) {
handleRecyclerViewScrolling(target, child);
return;
}
handleRecyclerViewScrolling should look like:
private void handleRecyclerViewScrolling(View target, FloatingActionButton child) {
if (scrollListener != null) {
return;
}
RecyclerView recyclerView = (RecyclerView) target;
scrollListener = new RecyclerViewScrollListener(child);
recyclerView.addOnScrollListener(scrollListener);
}
scrollListener should be a field in your FABBehavior class. Also declare inner class inside FABBehavior:
private class RecyclerViewScrollListener extends RecyclerView.OnScrollListener {
FloatingActionButton mChild;
public RecyclerViewScrollListener(FloatingActionButton child) {
this.mChild = child;
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
mChild.show();
} else {
mChild.hide();
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (!recyclerView.canScrollVertically(Integer.MAX_VALUE)) {
mChild.show();
}
}
}
RecyclerViewScrollListener hides FAB, when it is scrolling and shows it when it is in idle state.

Categories

Resources