EditText loses focus on Scroll in RecyclerView - java

I have a RecyclerView which have EditText as its list items. When i scroll the RecyclerView, the EditText loses focus when the item goes off screen. So the focus does not remain on the EditText when it comes back on the screen on scroll.
I want the focus to remain on the same item. For that i also tried storing position of the item on focus and reassigning the focus in onBindViewHolder. But that slow downs the scrolling.
I also tried this with ListView but there is another kind of focus related problem. Focus jumps there.
Have searched for this lot on SO and Googled a lot. but always found answers like android:focusableInTouchMode="true" and android:descendantFocusability="afterDescendants" which does not work.
Any help would be highly appreciated.

The whole point of a RecyclerView is to reuse the views so that the phone doesn't have to keep all the rows in memory at once and doesn't have to be constantly destroying and creating views. When your EditText leaves the screen, the phone takes that view, resets its contents and moves it to the bottom of the incoming view stack.
This means that once your EditText leaves the screen it no longer exists. It cannot keep focus because it is removed from the layout.
The only way around this would be what you mentioned, which is storing the position, checking when that position comes onscreen, and manually restoring focus.

As Elan Hamburger wrote, we should store the position and restore it when an item appears on screen after scrolling.
A problem is we don't know when the item appears and disappears. When we have several ViewHolders, onBindViewHolder can even not be invoked again. In this case we should use onViewAttachedToWindow and onViewDetachedFromWindow to learn when the item appears and disappears on screen.
private var focusPosition: Int = -1
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
super.onBindViewHolder(holder, position)
// Choose right ViewHolder and set focus event.
holder.edit_text.onFocusChangeListener = View.OnFocusChangeListener { _, hasFocus ->
focusPosition = position
}
}
override fun onViewAttachedToWindow(holder: ViewHolder) {
super.onViewAttachedToWindow(holder)
// Your condition, for instance, compare stored position or item id, or text value.
if (holder.adapterPosition == focusPosition && holder is SomeViewHolder) {
holder.edit_text.requestFocus()
}
}
//override fun onViewDetachedFromWindow(holder: UinBillViewHolder) { //
// super.onViewDetachedFromWindow(holder)
//}

Try use the next:
LinearSnapHelper snapHelper = new LinearSnapHelper();
snapHelper.attachToRecyclerView(recyclerView);
It helped me when using GridLayoutManager with Vertical orientation.
This option scrolls the list a little on its own so that when scrolling, the edge of the next items in the RecyclerView becomes is visible, so the focus is not lost.
But this does not help if you need to scroll too quickly (if the column did not have time to load, and you need to scroll further).
I found the answer here Scrolling recyclerview from the middle item

Related

Android ScrollView jumps up after changing child element visibility

I have a ScrollView that contains elements with visibility set to View.GONE by default. The problem is that whenever this visibility changes from View.GONE to View.VISIBLE, scroll jumps up as it is shown on second picture:
https://i.imgur.com/WmRc4M2.png
The red box represents current scroll position, blue rectangle is an element which visibility is changed, and green rectangle is some element (for example button) that I can refer to. I want my ScrollView to work as it is shown on third picture. If green button is on the screen, it should also be visible on screen after displaying new element (the blue one) above it. Any ideas how can I achieve it?
well, it behaves exacly as it should - scroll position is on e.g. 200px from top and this doesn't change when new item changes its visibility
you should scroll by yourself to proper (new) point, use scrollTo method - measure height of this new View and call scrollView.scrollTo(0, currentScrollY + heightOfNewView)
be aware that when you change visibility then you can't call above scrollTo just one line below, you have to wait for this View being measured and drawn. so you should call scrollTo in e.g.
visibilityChangedView.setVisibility(View.VISIBLE);
visibilityChangedView.post(new Runnable() {
// this will be called when visibilityChangedView won't have
// any pending tasks to do, but we just changed visibility,
// so at first view will be measured and drawn, after that
// below run() method will be called
#Override
public void run() {
int heightOfNewView = visibilityChangedView.getMeasuredHeight();
int currentScrollY = scrollView.getScrollY();
scrollView.scrollTo(0, currentScrollY + heightOfNewView);
}
);
there is a chance that this automatic scroll will be visible to user as quick jump - at first whole view will be drawn as #2 and after split second/one frame it will redraw at new position like #3. I don't know how your content is built (rest of childs in Scrollview), but I suspect that this should be RecyclerView or at least ListView with proper Adapter, not ScrollView with fixed layout
You can use NestedScrollView. It did the job.
Put your scrollView inside constraint layout and set all constraints to parent.

Hide view on scroll in NestedScrollView and take it's place

I would like the yellow LinearLayout to hide as I'm scrolling the NestScrollView. Preferably with the parallax effect. What I was able to achieve already was increase alpha and set visibility to gone on appBar scroll. But I want the recyclerView below it to take it's space as I'm scrolling. Thanks
How I got it to work perfectly.
1. Be above RecyclerView
Added as a row with different layout, used getItemViewType() to specify different view for that first row.
2. Disappear on RecyclerView scroll with parallax effect
Had to override AppBar's addOnOffsetChangedListener() which provided me with verticallOffset variable which I used to translate just the first row in recyclerView
recyclerView.getChildAt(0).setTranslationY(verticallOffset / 2);
3. RecyclerView take its place when it dissapears
Still using the verticallOffset:
if (alpha < -200) {
readingsRecyclerAdapter.hideFirstRow();
}
and then inside the readingsRecyclerAdapter I removed the row
public void hideFirstRow() {
if (headerShowing) {
appsList.remove(0);
notifyItemRemoved(0);
headerShowing = false;
}
}
notifyItemRemoved(0); is important here as it causes the recyclerview to take the first rows empty space.

RecyclerView expanding background

I have a RecyclerView in which I add items regularly. I want the background of the RecyclerView to expand while the list of items grows.
For example, if the list has two items and a new one is added, a new part of the background is discovered, and so on...
What I expect
The complete RecyclerView is as in the image below. The user can only see a part of it, but discovers the rest when he/she scrolls up or down (which is why RecyclerView exists, I believe).
Here we can see that the whole background is actually expanding beyond the screen's top and bottom border. So whenever the user scrolls the RecyclerView, the background moves with it (matching the movements of the list).
I'm completely lost with that, do you have any idea how to achieve such a behavior?

Android - Custom ListView disable scrolling

I have a custom ListView which checks for a value in its adapter, and if its false it should disable scrolling. The reason I need to do this is because inside the listview are rows of horizontal listviews (using this implementation: https://github.com/dinocore1/DevsmartLib-Android) and if you scroll slightly up or down whilst scrolling on of these horizontal views, they stop scrolling and the listview instead scrolls.
The issue with this code is that it doesn't always seem to prevent the listview from scrolling, and it also doesn't pass the action through to the child (horizontal listview) like I believe it should (passing false).
Does anyone have any idea how I can get it to stop scrolling and still pass the movement action through to the child? Any help would be appreciated.
Thanks.

ListView: Scroll to an item at the top of the ListView (and keep it there)?

I have a ListView with a significant number of items on it. I would like to figure out a way to get it to scroll to a certain position, but have that position be at the very top of the view.
The other issue is that the ListView gets invalidated and re-drawn at some point after this happens and I need it to return to this position when it is re-drawn.
I have tried smoothScrollToPosition, but this only brings the item into the view, and does not place it at the top. I have tried scrollTo(x,y) but I don't know the exact y-position of the item in the view. I have tried setSelection and setSelectionFromTop, but the selection gets reset when the view is invalidated. I have also tried all of these things within a post(Runnable) call, with no luck.
Here is the solution I have at the moment, which is awful.
listView.post(new Runnable() {
#Override
public void run() {
listView.smoothScrollToPosition(10000);
listView.smoothScrollToPosition(pos);
}
});
This forces it to scroll down to the bottom and scroll back up until the item at the specified position comes into the view, which has the effect of placing it at the top. Fortunately, the animation only goes so far as to scroll from the top to the end position, so it doesn't actually play out scrolling to the bottom and then back up. However, this solution is slow, hacky, and just generally awful. Does anyone have a better one?

Categories