kaneda
kaneda

Reputation: 6187

How to keep RecyclerView from scrolling when softkeyboard shows up with adjustResize set?

My app has an Activity that's declared with:

android:windowSoftInputMode="adjustResize|stateAlwaysHidden"

adjustResize is set and in that activity I have a RecyclerView and an EditText as in a chat like app.

The problem I'm facing is that when the keyboard shows up the layout is resized as intented but it also scrolls up the RecyclerView contents.

The behavior desired is that the scroll stays put.

I've tried using LayoutManager#onSaveInstanceState() and it's counterpart LayoutManager#onRestoreInstanceState() with no avail.

I know many had similar/same issues but I couldn't find a good solutions for this.

Upvotes: 0

Views: 2728

Answers (2)

Sadique Khan
Sadique Khan

Reputation: 320

use addOnscrollListener to check weather currently recyclerview is at bottom or not if yes then use the method provided in accepted answer by doing it youe recycler only scroll when last item is visible else it wont scroll

recycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            cannotScrollVertically = !recyclerView.canScrollVertically(1);//use a Boolean variable
        }
    });

Upvotes: 0

kaneda
kaneda

Reputation: 6187

Ok, amazing how a clear head and a sudden glimpse of thought makes wonders.

I don't know about everyone but I haven't found a solution for this simple problem this way, and it works for me. Sharing:

recyclerView.addOnLayoutChangeListener { _, _, _, _, bottom, _, _, _, oldBottom ->
    val y = oldBottom - bottom
    val firstVisibleItem = linearLayoutManager.findFirstCompletelyVisibleItemPosition()
    if (y.absoluteValue > 0 && !(y < 0 && firstVisibleItem == 0)) {
        recycler_view.scrollBy(0, y)
    }
}

Only drawback so far is that when you're scrolled to the second item and you hide the soft keyboard, it scrolls to the very end but no big deal for me.

Hope it helps someone.

EDIT: Here's how I solved without any drawbacks now:

private var verticalScrollOffset = AtomicInteger(0)

recyclerView.addOnLayoutChangeListener { _, _, _, _, bottom, _, _, _, oldBottom ->
    val y = oldBottom - bottom
    if (y.absoluteValue > 0) {
        // if y is positive the keyboard is up else it's down
        recyclerView.post {
            if (y > 0 || verticalScrollOffset.get().absoluteValue >= y.absoluteValue) {
                recyclerView.scrollBy(0, y)
            } else {
                recyclerView.scrollBy(0, verticalScrollOffset.get())
            }
        }
    }
}

recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
    var state = AtomicInteger(RecyclerView.SCROLL_STATE_IDLE)

    override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
        state.compareAndSet(RecyclerView.SCROLL_STATE_IDLE, newState)
        when (newState) {
            RecyclerView.SCROLL_STATE_IDLE -> {
                if (!state.compareAndSet(RecyclerView.SCROLL_STATE_SETTLING, newState)) {
                    state.compareAndSet(RecyclerView.SCROLL_STATE_DRAGGING, newState)
                }
            }
            RecyclerView.SCROLL_STATE_DRAGGING -> {
                state.compareAndSet(RecyclerView.SCROLL_STATE_IDLE, newState)
            }
            RecyclerView.SCROLL_STATE_SETTLING -> {
                state.compareAndSet(RecyclerView.SCROLL_STATE_DRAGGING, newState)
            }
        }
    }

    override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
        if (state.get() != RecyclerView.SCROLL_STATE_IDLE) {
            verticalScrollOffset.getAndAdd(dy)
        }
    }
})

EDIT2: This can be easily converted to Java if that's your poison.

Upvotes: 4

Related Questions