Monday, 3 December 2018

How to have RecyclerView snapped to center and yet be able to scroll to all items, while the center is "selected"?

Background

I'm try to achieve something similar to what the Camera app has for its modes:

enter image description here

I might probably not need to have a ViewPager, as seems that it uses above the horizontal list, but could be nice to have it as an option.

The problem

While technically I succeeded to have the RecyclerView center its items, it doesn't let you actually have the all items to be able to be on the center (example is the first/last one). When you try to scroll to the first or last items, it doesn't let you , because you've reached the edge of the RecyclerView :

enter image description here

Not only that, but in the beginning it doesn't really center, and if I have the RecyclerView have few items, it becomes a problem because I want them to be centered, but having android:layout_width="match_parent" (because all of it should be touch-able) produces this:

enter image description here

while having android:layout_width="wrap_content" get me this:

enter image description here

On both cases, I can't scroll at all. When it's "wrap_content", it's a problem because I won't be able to scroll on the sides.

What I've tried

It's possible to snap the items so that there will always be an item in the center of RecyclerView as such :

val snapHelper = LinearSnapHelper()
snapHelper.attachToRecyclerView(categoriesRecyclerView)

We can also get which is the item in the center (as shown here), by having a scroll listener and using snapHelper.findSnapView(layoutManagaer).

But as I wrote, I can't really have the first/last item being selected this way, because I can't scroll to it so that it will be at the middle.

I tried to look at the docs of the related classes, but I can't find such a thing.

Here's the current code (sample available here) :

MainActivity.kt

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        recyclerView.adapter = object : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
            override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
                val holder = object : RecyclerView.ViewHolder(
                    LayoutInflater.from(this@MainActivity).inflate(
                        R.layout.list_item,
                        parent,
                        false
                    )
                ) {}
                holder.itemView.setOnClickListener {
                }
                return holder
            }

            override fun getItemCount(): Int = 20

            override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
                holder.itemView.textView.text = "pos:$position"
            }
        }
        val snapHelper = LinearSnapHelper()
        snapHelper.attachToRecyclerView(recyclerView)
    }
}

activity_main.xml

<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView android:background="#66000000"
                                               android:id="@+id/recyclerView"
                                               android:layout_width="match_parent"
                                               android:layout_height="@dimen/list_item_size"
                                               android:orientation="horizontal"
                                               app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
                                               app:layout_constraintBottom_toBottomOf="parent"
                                               app:layout_constraintEnd_toEndOf="parent"
                                               app:layout_constraintStart_toStartOf="parent"
                                               app:layout_constraintTop_toTopOf="parent"
                                               tools:listitem="@layout/list_item"/>
</androidx.constraintlayout.widget.ConstraintLayout>

list_item.xml

<TextView
    android:id="@+id/textView" 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="wrap_content" android:layout_height="@dimen/list_item_size"
    android:background="?attr/selectableItemBackground" android:breakStrategy="balanced" android:clickable="true"
    android:focusable="true" android:gravity="center" android:maxLines="1" android:padding="8dp"
    android:shadowColor="#222" android:shadowDx="1" android:shadowDy="1" android:textColor="#fff"
    app:autoSizeTextType="uniform" tools:targetApi="m" tools:text="@tools:sample/lorem"/>

The question

How can I let the user freely scroll inside, so that the edge will be determined by whether the first/last item is in the middle? How can I always have the items centered, including when I just started seeing the RecyclerView), and including when there are few of them?



from How to have RecyclerView snapped to center and yet be able to scroll to all items, while the center is "selected"?

No comments:

Post a Comment