DiscreteScrollView
A scrollable list of items that centers the current element and provides easy-to-use APIs for cool item animations.
Top Related Projects
:octocat: 📃 FoldingCell is an expanding content cell with animation made by @Ramotion
Android Carousel LayoutManager for RecyclerView
Infinite cycle ViewPager with two-way orientation and interactive effect.
Quick Overview
The DiscreteScrollView
is an Android library that provides a scrollable view with a discrete set of items, similar to a carousel or a gallery. It allows for smooth scrolling between items and provides various customization options to fit different use cases.
Pros
- Smooth Scrolling: The library ensures smooth and fluid scrolling between items, providing a pleasant user experience.
- Customization: Developers can customize various aspects of the view, such as the orientation, item spacing, and scroll behavior.
- Snap Behavior: The view automatically snaps to the nearest item when the user stops scrolling, ensuring a consistent and intuitive user interface.
- Performance: The library is designed to be efficient and performant, handling large datasets without compromising the user experience.
Cons
- Limited Functionality: The library is focused on providing a discrete scrolling view, and may not offer advanced features or functionality beyond this core use case.
- Android-specific: The
DiscreteScrollView
is an Android-specific library, and cannot be used in other platforms or environments. - Dependency Management: Integrating the library into an existing project may require managing additional dependencies and configurations.
- Learning Curve: Developers new to the library may need to invest time in understanding its API and customization options.
Code Examples
Here are a few examples of how to use the DiscreteScrollView
library:
- Basic Setup:
val discreteScrollView = findViewById<DiscreteScrollView>(R.id.discrete_scroll_view)
discreteScrollView.adapter = MyAdapter()
- Customizing Orientation:
discreteScrollView.orientation = Orientation.HORIZONTAL
- Handling Item Selection:
discreteScrollView.addOnItemChangedListener { _, position ->
// Handle item selection
}
- Scrolling to a Specific Item:
discreteScrollView.scrollToPosition(3)
Getting Started
To get started with the DiscreteScrollView
library, follow these steps:
- Add the library to your project's dependencies:
dependencies {
implementation 'com.yarolegovich:discrete-scrollview:1.5.0'
}
- Add the
DiscreteScrollView
to your layout file:
<com.yarolegovich.discretescrollview.DiscreteScrollView
android:id="@+id/discrete_scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
- Create an adapter for your items and set it on the
DiscreteScrollView
:
class MyAdapter : DiscreteScrollAdapter<MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup): MyViewHolder {
// Create and return your view holder
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
// Bind data to the view holder
}
override fun getItemCount(): Int {
// Return the number of items
}
}
val discreteScrollView = findViewById<DiscreteScrollView>(R.id.discrete_scroll_view)
discreteScrollView.adapter = MyAdapter()
- Customize the
DiscreteScrollView
as needed, such as setting the orientation, item spacing, or scroll behavior.
That's it! You can now use the DiscreteScrollView
in your Android application.
Competitor Comparisons
:octocat: 📃 FoldingCell is an expanding content cell with animation made by @Ramotion
Pros of Folding Cell
- Folding Cell provides a unique and visually appealing UI element that can be used to display expandable content.
- The library includes a variety of customization options, allowing developers to tailor the appearance and behavior of the folding cell to their specific needs.
- Folding Cell is well-documented and has a large community of users, making it easier to find support and resources.
Cons of Folding Cell
- Folding Cell may be more complex to implement than a simple scrollable list, as it requires additional logic to handle the folding and unfolding of the cells.
- The library may not be as flexible as a custom-built scrollable list, as it is designed with a specific use case in mind.
Code Comparison
Folding Cell:
let cell = FoldingCell(style: .default, reuseIdentifier: "MyCell")
cell.contentView.backgroundColor = .white
cell.layer.cornerRadius = 10
cell.layer.masksToBounds = true
Discrete Scroll View:
val discreteScrollView = DiscreteScrollView(context)
discreteScrollView.adapter = MyAdapter()
discreteScrollView.setItemTransformer(ScaleTransformer())
discreteScrollView.setOffscreenItems(2)
Android Carousel LayoutManager for RecyclerView
Pros of CarouselLayoutManager
- Provides a carousel-style layout for RecyclerView, which can be useful for creating engaging and visually appealing user interfaces.
- Supports various configuration options, such as setting the carousel's orientation, item spacing, and scaling.
- Includes built-in support for snap-to-center behavior, making it easy to create a smooth carousel experience.
Cons of CarouselLayoutManager
- Lacks some of the advanced features and customization options available in DiscreteScrollView, such as the ability to set a specific scroll position or handle complex scrolling behaviors.
- May have a steeper learning curve compared to DiscreteScrollView, as it requires more configuration and setup to achieve the desired layout and behavior.
Code Comparison
DiscreteScrollView
val dsv = findViewById<DiscreteScrollView>(R.id.dsv)
dsv.setAdapter(MyAdapter())
dsv.setItemTransformer(ScaleTransformer())
dsv.scrollToPosition(3)
CarouselLayoutManager
val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
val carouselLayoutManager = CarouselLayoutManager(CarouselLayoutManager.HORIZONTAL)
recyclerView.layoutManager = carouselLayoutManager
recyclerView.adapter = MyAdapter()
Infinite cycle ViewPager with two-way orientation and interactive effect.
Pros of InfiniteCycleViewPager
- Supports infinite scrolling, allowing users to continuously cycle through content without reaching the end.
- Provides a smooth and seamless scrolling experience, with no visible gaps between the first and last items.
- Includes various customization options, such as setting the scroll duration and enabling auto-scrolling.
Cons of InfiniteCycleViewPager
- May require more complex implementation and setup compared to DiscreteScrollView.
- Potentially less efficient for large datasets, as the entire dataset needs to be loaded and managed.
- May not be as suitable for scenarios where precise control over the scrolling behavior is required.
Code Comparison
DiscreteScrollView
DiscreteScrollView scrollView = findViewById(R.id.discrete_scroll_view);
scrollView.setAdapter(new MyAdapter());
scrollView.addOnItemChangedListener(new DiscreteScrollViewOptions.OnItemChangedListener() {
@Override
public void onCurrentItemChanged(@Nullable RecyclerView.ViewHolder viewHolder, int adapterPosition) {
// Handle item change
}
});
InfiniteCycleViewPager
InfiniteCycleViewPager viewPager = findViewById(R.id.infinite_cycle_view_pager);
viewPager.setAdapter(new MyPagerAdapter());
viewPager.setScrollDuration(500);
viewPager.startAutoScroll(3000);
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
DiscreteScrollView
The library is a RecyclerView-based implementation of a scrollable list, where current item is centered and can be changed using swipes. It is similar to a ViewPager, but you can quickly and painlessly create layout, where views adjacent to the currently selected view are partially or fully visible on the screen.
Gradle
Add this into your dependencies block.
compile 'com.yarolegovich:discrete-scrollview:1.5.1'
Reporting an issue
If you are going to report an issue, I will greatly appreciate you including some code which I can run to see the issue. By doing so you maximize the chance that I will fix the problem.
By the way, before reporting a problem, try replacing DiscreteScrollView with a RecyclerView. If the problem is still present, it's likely somewhere in your code.
Sample
Please see the sample app for examples of library usage.
Wiki
General
The library uses a custom LayoutManager to adjust items' positions on the screen and handle scroll, however it is not exposed to the client code. All public API is accessible through DiscreteScrollView class, which is a simple descendant of RecyclerView.
If you have ever used RecyclerView - you already know how to use this library. One thing to note - you should NOT set LayoutManager.
Usage:
- Add DiscreteScrollView to your layout either using xml or code:
- Create your implementation of RecyclerView.Adapter. Refer to the sample for an example, if you don't know how to do it.
- Set the adapter.
- You are done!
<com.yarolegovich.discretescrollview.DiscreteScrollView
android:id="@+id/picker"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:dsv_orientation="horizontal|vertical" /> <!-- orientation is optional, default is horizontal -->
DiscreteScrollView scrollView = findViewById(R.id.picker);
scrollView.setAdapter(new YourAdapterImplementation());
API
General
scrollView.setOrientation(DSVOrientation o); //Sets an orientation of the view
scrollView.setOffscreenItems(count); //Reserve extra space equal to (childSize * count) on each side of the view
scrollView.setOverScrollEnabled(enabled); //Can also be set using android:overScrollMode xml attribute
Related to the current item:
scrollView.getCurrentItem(); //returns adapter position of the currently selected item or -1 if adapter is empty.
scrollView.scrollToPosition(int position); //position becomes selected
scrollView.smoothScrollToPosition(int position); //position becomes selected with animated scroll
scrollView.setItemTransitionTimeMillis(int millis); //determines how much time it takes to change the item on fling, settle or smoothScroll
Transformations
One useful feature of ViewPager is page transformations. It allows you, for example, to create carousel effect. DiscreteScrollView also supports page transformations.
scrollView.setItemTransformer(transformer);
public interface DiscreteScrollItemTransformer {
/**
* In this method you apply any transform you can imagine (perfomance is not guaranteed).
* @param position is a value inside the interval [-1f..1f]. In idle state:
* |view1| |currentlySelectedView| |view2|
* -view1 and everything to the left is on position -1;
* -currentlySelectedView is on position 0;
* -view2 and everything to the right is on position 1.
*/
void transformItem(View item, float position);
}
In the above example view1Position == (currentlySelectedViewPosition - n)
and view2Position == (currentlySelectedViewPosition + n)
, where n
defaults to 1 and can be changed using the following API:
scrollView.setClampTransformProgressAfter(n);
Because scale transformation is the most common, I included a helper class - ScaleTransformer, here is how to use it:
cityPicker.setItemTransformer(new ScaleTransformer.Builder()
.setMaxScale(1.05f)
.setMinScale(0.8f)
.setPivotX(Pivot.X.CENTER) // CENTER is a default one
.setPivotY(Pivot.Y.BOTTOM) // CENTER is a default one
.build());
You may see how it works on GIFs.
Slide through multiple items
To allow slide through multiple items call:
scrollView.setSlideOnFling(true);
The default threshold is set to 2100. Lower the threshold, more fluid the animation. You can adjust the threshold by calling:
scrollView.setSlideOnFlingThreshold(value);
Infinite scroll
Infinite scroll is implemented on the adapter level:
InfiniteScrollAdapter wrapper = InfiniteScrollAdapter.wrap(yourAdapter);
scrollView.setAdapter(wrapper);
An instance of InfiniteScrollAdapter
has the following useful methods:
int getRealItemCount();
int getRealCurrentPosition();
int getRealPosition(int position);
/*
* You will probably want this method in the following use case:
* int targetAdapterPosition = wrapper.getClosestPosition(targetPosition);
* scrollView.smoothScrollTo(targetAdapterPosition);
* To scroll the data set for the least required amount to reach targetPosition.
*/
int getClosestPosition(int position);
Currently InfiniteScrollAdapter
handles data set changes inefficiently, so your contributions are welcome.
Disabling scroll
It's possible to forbid user scroll in any or specific direction using:
scrollView.setScrollConfig(config);
Where config
is an instance of DSVScrollConfig
enum. The default value enables scroll in any direction.
Callbacks
- Scroll state changes:
scrollView.addScrollStateChangeListener(listener);
scrollView.removeScrollStateChangeListener(listener);
public interface ScrollStateChangeListener<T extends ViewHolder> {
void onScrollStart(T currentItemHolder, int adapterPosition); //called when scroll is started, including programatically initiated scroll
void onScrollEnd(T currentItemHolder, int adapterPosition); //called when scroll ends
/**
* Called when scroll is in progress.
* @param scrollPosition is a value inside the interval [-1f..1f], it corresponds to the position of currentlySelectedView.
* In idle state:
* |view1| |currentlySelectedView| |view2|
* -view1 is on position -1;
* -currentlySelectedView is on position 0;
* -view2 is on position 1.
* @param currentIndex - index of current view
* @param newIndex - index of a view which is becoming the new current
* @param currentHolder - ViewHolder of a current view
* @param newCurrent - ViewHolder of a view which is becoming the new current
*/
void onScroll(float scrollPosition, int currentIndex, int newIndex, @Nullable T currentHolder, @Nullable T newCurrentHolder);
}
- Scroll:
scrollView.addScrollListener(listener);
scrollView.removeScrollListener(listener);
public interface ScrollListener<T extends ViewHolder> {
//The same as ScrollStateChangeListener, but for the cases when you are interested only in onScroll()
void onScroll(float scrollPosition, int currentIndex, int newIndex, @Nullable T currentHolder, @Nullable T newCurrentHolder);
}
- Current selection changes:
scrollView.addOnItemChangedListener(listener);
scrollView.removeOnItemChangedListener(listener);
public interface OnItemChangedListener<T extends ViewHolder> {
/**
* Called when new item is selected. It is similar to the onScrollEnd of ScrollStateChangeListener, except that it is
* also called when currently selected item appears on the screen for the first time.
* viewHolder will be null, if data set becomes empty
*/
void onCurrentItemChanged(@Nullable T viewHolder, int adapterPosition);
}
Special thanks
Thanks to Tayisiya Yurkiv for sample app design and beautiful GIFs.
License
Copyright 2017 Yaroslav Shevchuk
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Top Related Projects
:octocat: 📃 FoldingCell is an expanding content cell with animation made by @Ramotion
Android Carousel LayoutManager for RecyclerView
Infinite cycle ViewPager with two-way orientation and interactive effect.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot