Code Monkey home page Code Monkey logo

bouncy's Introduction

Bouncy

Android Arsenal

Add IOS-like overscroll animation to your scrolling views using SpringAnimation.

Currently includes BouncyRecyclerView and BouncyNestedScrollView.

Add Bouncy to your project

Gradle

Add it in your root build.gradle at the end of repositories:

allprojects {
  	repositories {
  		...
  		maven { url 'https://jitpack.io' }
  	}
  }

In your app module build.gradle, add dependency for recyclerview and bouncy:

   dependencies {
        implementation 'androidx.recyclerview:recyclerview:1.2.1'
        implementation 'com.github.valkriaine:Bouncy:2.3'
   }

BouncyNestedScrollView

NestedScrollView with bouncy overscroll effect, currently only supports vertical scrolling.

Achieved by overriding the default EdgeEffect

Usage:

Use as normal NestedScrollView. Place it in your layout:

<com.factor.bouncy.BouncyNestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:fling_animation_size=".7"
        app:overscroll_animation_size=".7">

    <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            
            ...
            ...
            ...

    </LinearLayout>

</com.factor.bouncy.BouncyNestedScrollView>

fling_animation_size specifies the magnitude of overscroll effect for fling, default is 0.5 if no value is given.

overscroll_animation_size specifies the magnitude of overscroll effect for drag, default is 0.5 if no value is given.

Strongly suggest to keep both values lower than 5.

It is now possible to bind bouncy animation to parent instead of BouncyNestedScrollView

bouncy_scroll_view.setBindSpringToParent(true);

// this will bind the spring animations to parent instead of self

BouncyRecyclerView

BouncyRecyclerView adds overscroll effect to RecyclerView and supports drag & drop and swiping gestures

Usage:

Use as normal RecyclerView. Place it in your layout:

<com.factor.bouncy.BouncyRecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:recyclerview_fling_animation_size=".7"
        app:recyclerview_overscroll_animation_size=".7"
        app:recyclerview_damping_ratio="DAMPING_RATIO_LOW_BOUNCY"
        app:recyclerview_stiffness="STIFFNESS_MEDIUM"
        app:allow_drag_reorder="true"
        app:allow_item_swipe="false"/>

set up layout manager and adapter. Theoratically supports any LayoutManager:

   recycler_view.setAdapter(myAdapter);
   recycler_view.setLayoutManager(new LinearLayoutManager(context));
   //recycler_view.setLayoutManager(new GridLayoutManager(context, 3));

Orientation

When you set the LayoutManager, BouncyRecyclerView will automatically detect the orientation of the layout.

   recycler_view.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false));

If the bounce animation is incorrect, you can also manually set the animation orientation:

   recycler_view.setOrientation(LinearLayoutManager.VERTICAL);

Customization

recyclerview_fling_animation_size specifies the magnitude of overscroll effect for fling, default is 0.5 if no value is given

recyclerview_overscroll_animation_size specifies the magnitude of overscroll effect for drag, default is 0.5 if no value is given

allow_drag_reorder and allow_item_swipe are set to false by default. If you would like to enable these features, simply set them to true.

Spring customization

recyclerview_damping_ratio and recyclerview_stiffness please refer to damping ratio and stiffness

Set in code:

   recycler_view.setFlingAnimationSize(0.3f);
   recycler_view.setOverscrollAnimationSize(0.3f);
   recycler_view.setDampingRatio(Bouncy.DAMPING_RATIO_HIGH_BOUNCY);
   recycler_view.setStiffness(Bouncy.STIFFNESS_HIGH);

Drag & drop

Drag & drop does not work out of the box.

For drag & drop or swipe gestures to work, make your adapter extend BouncyRecyclerView.Adapter. (If your adapter does not extend BouncyRecyclerView.Adapter, BouncyRecyclerView will simply disable the gestures)

public class MyAdapter extends BouncyRecyclerView.Adapter
{
    private final ArrayList<MyData> dataSet;

    public MyAdapter(ArrayList<MyData> dataSet)
    {
        this.dataSet = dataSet;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
    {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false);
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
    {
        MyViewHolder h = (MyViewHolder) holder;
        h.getTextView().setText(dataSet.get(position).getData());
    }

    @Override
    public int getItemCount()
    {
        return dataSet.size();
    }

    @Override
    public void onItemMoved(int fromPosition, int toPosition)
    {
        //*****must override to save changes 
        //called repeatedly when item is dragged (reordered)
        
        //example of handling reorder
        MyData item = dataSet.remove(fromPosition);
        dataSet.add(toPosition, item);
        notifyItemMoved(fromPosition, toPosition);
    }

    @Override
    public void onItemSwipedToStart(RecyclerView.ViewHolder viewHolder, int position)
    {
        //item swiped left
    }

    @Override
    public void onItemSwipedToEnd(RecyclerView.ViewHolder viewHolder, int position)
    {
        //item swiped right
    }

    @Override
    public void onItemSelected(RecyclerView.ViewHolder viewHolder)
    {
        //item long pressed (selected)
    }

    @Override
    public void onItemReleased(RecyclerView.ViewHolder viewHolder)
    {
        //item released (unselected)
    }
}

Also refer to the Kotlin example

My other projects

Factor Launcher - A Windows Phone inspired launcher with some modern touch

Mutify - Automatically turn on do-not-disturb based on where you are

bouncy's People

Contributors

valkriaine avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

bouncy's Issues

NullPointerException

Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter previouslyFocusedRect

Holding the bounce position when dragging is not working

Hi,

I noticed when your drag the scrollview (up & down) it bounces perfectly but if you keep your finger down the scrollview doesn't stay in place, it goes back to the zero offset position with a very weird (choppy/jerky) animation.

In the meanwhile on an iOS device when you hold your finger down the scrollview stays still until you take your finger up when it goes back to the initial position.

Than you,
Achref

RecyclerView inside ScrollView causes runtime exception

Killing the app through logcat and opening it again throws this exception

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.andric.app/com.andric.app.MainActivity}: java.lang.RuntimeException: Parcel android.os.Parcel@2920ae7: Unmarshalling unknown type code 2131296744 at offset 1040
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3792)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3968)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2307)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:246)
        at android.app.ActivityThread.main(ActivityThread.java:8506)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1139)
     Caused by: java.lang.RuntimeException: Parcel android.os.Parcel@2920ae7: Unmarshalling unknown type code 2131296744 at offset 1040
        at android.os.Parcel.readValue(Parcel.java:3265)
        at android.os.Parcel.readSparseArrayInternal(Parcel.java:3662)
        at android.os.Parcel.readSparseArray(Parcel.java:2804)
        at android.os.Parcel.readValue(Parcel.java:3243)
        at android.os.Parcel.readArrayMapInternal(Parcel.java:3579)
        at android.os.BaseBundle.initializeFromParcelLocked(BaseBundle.java:292)
        at android.os.BaseBundle.unparcel(BaseBundle.java:236)
        at android.os.Bundle.getSparseParcelableArray(Bundle.java:1079)
        at androidx.fragment.app.FragmentStateManager.restoreState(FragmentStateManager.java:405)
        at androidx.fragment.app.FragmentManager.restoreSaveState(FragmentManager.java:2733)
        at androidx.fragment.app.FragmentController.restoreSaveState(FragmentController.java:198)
        at androidx.fragment.app.FragmentActivity$2.onContextAvailable(FragmentActivity.java:149)
        at androidx.activity.contextaware.ContextAwareHelper.dispatchOnContextAvailable(ContextAwareHelper.java:99)
        at androidx.activity.ComponentActivity.onCreate(ComponentActivity.java:293)
        at androidx.fragment.app.FragmentActivity.onCreate(FragmentActivity.java:273)
        at androidx.appcompat.app.AppCompatActivity.onCreate(AppCompatActivity.java:115)
        at com.andric.app.MainActivity.onCreate(MainActivity.kt:53)
        at android.app.Activity.performCreate(Activity.java:8198)
        at android.app.Activity.performCreate(Activity.java:8182)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3765)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3968) 
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85) 
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2307) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:246) 
        at android.app.ActivityThread.main(ActivityThread.java:8506) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1139)

The problem is in the xml layout, specifically the id of the RecyclerView. Removing it no longer causes the exception.

<com.factor.bouncy.BouncyNestedScrollView
        android:id="@+id/nested_scroll_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:damping_ratio="DAMPING_RATIO_LOW_BOUNCY"
        app:fling_animation_size=".2"
        app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
        app:overscroll_animation_size=".2"
        app:stiffness="STIFFNESS_LOW">

        <LinearLayout
            android:id="@+id/linear_root"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recycler_view"
                android:layout_width="match_parent"
                android:layout_height="181dp"
                android:nestedScrollingEnabled="false"
                android:orientation="horizontal"
                android:overScrollMode="never"
                android:paddingHorizontal="20dp"
                android:paddingVertical="10dp"
                app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
                app:reverseLayout="false" />

                ...

        </LinearLayout>

    </com.factor.bouncy.BouncyNestedScrollView>

Flicker problem

Hi, I have a strange problem with items flickering on every update of the list, i tried setting supportAnimations to false, using stable ids and using diffutils but non of that worked(some of the solutions related to regular recycler view). So I want to try maybe here is the problem?

translationY becomes NaN sometimes when scrolling a lot

I have this issue with BouncyRecyclerview that that translationY becomes NaN sometimes. It is kind of hard to reproduce but happens regularly. I fling the view upwards a lot until it happens. Then the full content of the RecyclerView disappears. If I check with it in the LayoutInspector I can see that translationY is not 0dp but NaN which I assume is the issue.

Cant compile due to duplicate string values

App wont compile due to duplicate string values:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--<declare-styleable name="BouncyNestedScrollView">
        <attr format="float" name="overscroll_animation_size"/>
        <attr format="float" name="fling_animation_size"/>

        <attr format="integer" name="damping_ratio">
            <enum name="DAMPING_RATIO_NO_BOUNCY" value="0"/>
            <enum name="DAMPING_RATIO_LOW_BOUNCY" value="1"/>
            <enum name="DAMPING_RATIO_MEDIUM_BOUNCY" value="2"/>
            <enum name="DAMPING_RATIO_HIGH_BOUNCY" value="3"/>
        </attr>
        <attr format="integer" name="stiffness">
            <enum name="STIFFNESS_VERY_LOW" value="0"/>
            <enum name="STIFFNESS_LOW" value="1"/>
            <enum name="STIFFNESS_MEDIUM" value="2"/>
            <enum name="STIFFNESS_HIGH" value="3"/>
        </attr>
    </declare-styleable>-->
    <declare-styleable name="BouncyRecyclerView">
        <attr format="float" name="recyclerview_overscroll_animation_size"/>
        <attr format="float" name="recyclerview_fling_animation_size"/>
        <attr format="boolean" name="allow_drag_reorder"/>
        <attr format="boolean" name="allow_item_swipe"/>

        <attr format="integer" name="recyclerview_damping_ratio">
            <enum name="DAMPING_RATIO_NO_BOUNCY" value="0"/>
            <enum name="DAMPING_RATIO_LOW_BOUNCY" value="1"/>
            <enum name="DAMPING_RATIO_MEDIUM_BOUNCY" value="2"/>
            <enum name="DAMPING_RATIO_HIGH_BOUNCY" value="3"/>
        </attr>
        <attr format="integer" name="recyclerview_stiffness">
            <enum name="STIFFNESS_VERY_LOW" value="0"/>
            <enum name="STIFFNESS_LOW" value="1"/>
            <enum name="STIFFNESS_MEDIUM" value="2"/>
            <enum name="STIFFNESS_HIGH" value="3"/>
        </attr>
    </declare-styleable>
</resources>

I had to manually comment out the nestedscrolling values to get it to compile, please fix this.

IllegalArgumentException When Setting LayoutManager Thru XML

Noticed that when you set the layout manager on the recycler view in xml like so:

    <com.factor.bouncy.BouncyRecyclerView
        ...
       app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />

We see the following crash:

 Caused by: java.lang.IllegalArgumentException: Spring stiffness constant must be positive.
        at androidx.dynamicanimation.animation.SpringForce.setStiffness(SpringForce.java:138)
        at com.factor.bouncy.BouncyRecyclerView.setupDirection(BouncyRecyclerView.kt:125)
        at com.factor.bouncy.BouncyRecyclerView.setOrientation(BouncyRecyclerView.kt:30)
        at com.factor.bouncy.BouncyRecyclerView.setLayoutManager(BouncyRecyclerView.kt:103)
        at androidx.recyclerview.widget.RecyclerView.createLayoutManager(RecyclerView.java:813)
        at androidx.recyclerview.widget.RecyclerView.<init>(RecyclerView.java:711)
        at androidx.recyclerview.widget.RecyclerView.<init>(RecyclerView.java:650)
        at com.factor.bouncy.BouncyRecyclerView.<init>(BouncyRecyclerView.kt:16)

Easy enough to set it through code for now, but just wanted to point this out.

Just a comment.

Amazing Lib but it'll be better if you create BouncyNestedScrollView from NestedScrollView and allow to scroll parrent of BouncyNestedScrollView or BouncyRecyclerView.
Scroll content and include of Toolbar with title of activity will make app smoothly as IOS ^_^.

BouncyNestedScrollView is not wokring!

I did exactly as the instructions said but bouncy effect didn't work

  • Video
video_2023-10-13_02-08-24.mp4

fragment_home.xml

image

 <com.factor.bouncy.BouncyNestedScrollView
        android:id="@+id/bouncy_nsv"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:orientation="vertical"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

           ...
           ...

        </androidx.constraintlayout.widget.ConstraintLayout>
    </com.factor.bouncy.BouncyNestedScrollView>

HomeFragment.java

bouncyNestedScrollView.setBindSpringToParent(true); 

Flinging only works until performing an overscroll on the same side

When I fling my list and it reaches the end, it bounces as expected, but if I drag on the same side (perform an overscroll) and then fling again, it no longer bounces and just abruptly stops. Eg if I drag (overscroll) on the bottom side of the list, flinging animation will no longer work on the bottom

The BouncyRecyclerView is in a CoordinatorLayout. The layout file is for a fragment. My adapter extends BouncyRecyclerView.
BouncyRecyclerView has the following settings:

app:recyclerview_fling_animation_size="0.1"
app:recyclerview_overscroll_animation_size="0.5"
app:recyclerview_damping_ratio="DAMPING_RATIO_LOW_BOUNCY"
app:recyclerview_stiffness="STIFFNESS_LOW"```

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.