Android RecyclerView Edittext issue
Asked Answered
H

8

12

I'm creating a list of editable items (received from backend). I'm using the "new" recyclerview for this. There a couple of possible viewTypes in my recyclerview:

  • checkbox
  • spinner
  • edittext

The problem I'm having is with the EditText gaining focus. AdjustResize kicks in fine and my keyboard is shown. But the EditText that has gained focus isn't visible anymore in the list. (position in the list in the now resized portion is below the visible positions). I don't want to be using AdjustPan in this case because on top there is a pager & stuff I would like to keep fixed there..

Hinkley answered 5/5, 2015 at 10:43 Comment(4)
I haven't quite grokked LinearLayoutManager just yet, so I'm posing this as a comment, not as an answer. Not sure this works. Looking at the source code, LLM picks one child view as the anchor for its scroll position. If you read the updateAnchorFromChildren(..) method, then setting focus on the EditText's parent view (whatever view is the actual itemView of your ViewHolder) should make that item the scroll anchor.Ileostomy
Any solution for this?Jabberwocky
have you found a solution?Guthrun
No, it still wasn't working as I wanted it to be so I ended up opening another view on top when editing somethingHinkley
C
6

This is happening because, at the time the keyboard is shown, your EditText does not exist anymore. I have made several attempts to find a clean solution to this problem maintaining adjustResize input mode and good user experience. Here's what I ended up with:

To ensure that EditText is visible before the keyboard is shown, you will have to scroll the RecyclerView manually and then focus your EditText. You can do so by overriding focus and click handling in your code.

Firstly, disable focus on the EditText by setting android:focusableInTouchMode property to false:

<EditText
    android:id="@+id/et_review"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:focusableInTouchMode="false"/>

Then you have to set a click listener for the EditText where you will manually scroll the RecyclerView and then show the keyboard. You will have to know the position of your ViewHolder which contains the EditText:

editText.setOnClickListener(new View.OnClickListener() {
    @Override public void onClick(View v) {
        LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
        // you may want to play with the offset parameter
        layoutManager.scrollToPositionWithOffset(position, 0);
        editText.setFocusableInTouchMode(true);
        editText.post(() -> {
            editText.requestFocus();
            UiUtils.showKeyboard(editText);
        });
    }
});

And finally, you must make sure that the EditText is made not focusable again when it naturally looses focus:

editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
    @Override public void onFocusChange(View v, boolean hasFocus) {
        if (!hasFocus) {
            editText.setFocusableInTouchMode(false);
            UiUtils.hideKeyboard();
        }
    }
});

This is far from a simple and clean solution, but hope it will help the way it helped me.

Crissycrist answered 4/4, 2017 at 17:26 Comment(1)
@temirbek this is just a static utility method to hide the keyboard. You should be able to find the implementation that fits your case using a simple google search.Crissycrist
J
5

Using this in layout worked : android:descendantFocusability="beforeDescendants"

<android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:descendantFocusability="beforeDescendants"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/extrasLayout"
        android:layout_below="@+id/anchorView"
        android:layout_marginTop="@dimen/margin5"
        android:fastScrollEnabled="false"
        />

Manifest file : Add this to the activity section android:windowSoftInputMode="stateHidden|adjustPan"

<activity
            android:name=".activites.CartActivity"
            android:label="@string/title_activity_cart"
            android:exported="true"
            android:windowSoftInputMode="stateHidden|adjustPan"
            android:parentActivityName=".activites.HomeActivity"
            android:screenOrientation="portrait">
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value=".activites.HomeActivity"/>
        </activity>
Jabberwocky answered 22/7, 2016 at 7:1 Comment(1)
OP explicitly said that they don't want to use adjustPanZoologist
F
5

I've had the same issue. Long story short - there is no right and elegant solution. Focus handling was always a big pain in Android.

There multiple reasons why you are loosing focus:

  • Keyboard hides the descendant EditText views
  • Next EditText is not rendered, because RecycleView hasn't even created a ViewHolder for it. Example: you have 10 views, and 5 of them are on screen and others aren't visible, because those are below.
  • Some other view consumes focus, in example CheckBox or some DatePicker
  • RecyclerView due to scroll events consumes focus
  • Bunch of other hidden under-the-hood stuff

Few words in terms of architecture and structural approach in my solution:

  • Everything works with Data binding
  • RecyclerView works with Adapter that supports AdapterDelegate approach
  • Each EditText, Spinner, DatePicker, CheckBox or RadioButton group is a separate ViewModel which is a separate unit and handles a lot of stuff on its own.

I've used a lot of tricks and mixed them together. As someone already mentioned, the first step would be to add those params to your RecyclerView:

            android:descendantFocusability="beforeDescendants"
            android:focusable="true"
            android:focusableInTouchMode="true"

Base view model for all possible inputs has defined interface, let's call it SingleInputViewModel, among that interface you can have defined next functions/methods:

void onFocusGained();

void onFocusLost();

boolean isFocusable();

In each particular input item implementation you are able to control focus, for example you are able to implement CheckBox as non-focusable and focus will jump to next isFocusable() == true item. Also you will be able to control the state and action depending on consuming/gaining focus on particular view.

Next step for fixing some of the focus passing issues - was scrolling RecyclerView when IME_ACTION_NEXT occurs. In such case you need to delegate your scroll logic to LayoutManager.scrollHorizontallyBy() or LayoutManager.scrollToPosition() with calculating appropriate offset or position.

Hard and elegant approach is to override logic inside LayoutManager, which is also responsible for focus handling. LinearLayoutManager has a lot of hidden logic, which you won't be able to override, so probably you will need to write a lot of code from scratch.

And the last and the most complex way to fix that is to extend RecyclerView and override focus search related funs/methods:

  • RecyclerView.focusSearch()
  • RecyclerView.isPreferredNextFocus()
  • RecyclerView.onRequestFocusInDescendants()
  • RecyclerView.onFocusSearchFailed()
  • RecyclerView.onInterceptFocusSearch()
  • RecyclerView.onRequestChildFocus()

P.S. Have a look at the FocusFinder and it's usage, just for general knowledge. Now you have few option to choose. I hope you will find something helpful. Good luck!

Frei answered 6/9, 2019 at 16:48 Comment(0)
H
1

Ok I found a solution for this problem I'm using a library for that and it solved easily. when you make your recyclerView by this template, it solves this problem automatically.

NOTE: for checkbox and radiobutton only

use this

https://github.com/TakeoffAndroid/RecyclerViewTemplate

Hawkins answered 14/1, 2020 at 11:0 Comment(0)
Z
0

I'm going to post my solution because after 2 days being blocked by this I hope that it could help someone else.

My issue was that the RecyclerView was a wrap_content on the height, meaning it didn't have a fixed height, and every time i clicked for the first time on one of the edit text inside it, it wasn't giving the focus on the first tap, but it was just showing the keyboard, on the second tap it was gaining the focus.

The fix was to make sure the RecyclerView has a fixed height or match_parent, i don't really know why but this worked for me!

Hope it helps

Zumstein answered 15/6, 2020 at 14:9 Comment(0)
A
0

I put all these three things in recyclerview finally it stopped recyclerview edittext gaining focus

 android:descendantFocusability="beforeDescendants"
        android:focusable="true"
        android:focusableInTouchMode="true"
Alford answered 13/7, 2021 at 11:25 Comment(0)
J
-1

This is happening because from the android 9.0 you have to write post()-> to gain focus of edittext in Previous versions you can just put
edittext.requestFocus();

but from 9.0 you have to run thread to gain focus of edittext like this
et.post(() -> {et.requestFocus();});

Johnson answered 6/9, 2019 at 10:12 Comment(0)
S
-1

You can scroll recyclerview to selected recycler item position on edit text focus change listener . Have a look at link to use scroll option How to use RecyclerView.scrollToPosition() to move the position to the top of current view?

Sadiesadira answered 6/9, 2019 at 13:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.