Issues focusing EditTexts in a ListView (Android)

2019-01-25 20:00发布

问题:

I have a ListView with a few EditTexts in each item. I have no issues with a hardware keyboard, but things freak out a bit with the soft keyboard. I have two issues.

  1. When I first click on an EditText, it briefly appears to have focus but then loses focus once the keyboard has shown. I must then click the EditText again to get focus.
  2. When an EditText with focus is scrolled out of view, focus goes to... well... see the screenshot. I'm not sure what's happening.

More details on #1:

  • When the screen first loads, focus is in the "Gr High School Scale" field, but the keyboard is not shown.
  • If I immediately click on a desired EditText, its OnFocusChangeListener tells me that it receives focus and then loses focus. Visually, I see the cursor appear in the field, but when the keyboard loads the cursor jumps away (like in the screenshot) and I don't know where focus has gone.

I've played around a bit the Focusable and DescendantFocusability attributes of the ListView, but to no avail. Any advice?

Each time an EditText with focus gets scrolled out of view, another drunk cursor shows up:

UPDATE: Relevant code.
Activity layout with ListView XML:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res/com.NsouthProductions.gradetrackerpro"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.NsouthProductions.gradetrackerpro.Activity_EditCourseGPA" >

<TextView
    android:id="@+id/textView1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentLeft="true"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="34dp"
    android:text="Define the GPA and Percent scale for this course." />

<RadioGroup
    android:id="@+id/radioGroup1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentLeft="true"
    android:layout_below="@+id/textView1"
    android:layout_marginLeft="5dp" >

    <LinearLayout
        android:id="@+id/linlay_rad_group_existing_scale"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <RadioButton
            android:id="@+id/radio0_existing_scale"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="0"
            android:checked="true"
            android:text="Existing Scale" />

        <Spinner
            android:id="@+id/spinner_existing_scales"
            android:layout_width="wrap_content"
            android:layout_height="33dp"
            android:layout_gravity="right"
            android:layout_weight="1" />

    </LinearLayout>

    <LinearLayout
        android:id="@+id/linlay_rad_group_new_scale"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <RadioButton
            android:id="@+id/radio1_new_scale"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="0"
            android:text="New Scale" />

        <TextView
            android:id="@+id/textView2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:lines="1"
            android:maxLines="1"
            android:text=" " />

        <EditText
            android:id="@+id/editText1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:inputType="textPersonName"
            android:maxLines="1"
            android:scrollHorizontally="true"
            android:text="Gr High School Scale" >

            <requestFocus />
        </EditText>

    </LinearLayout>
</RadioGroup>


 <LinearLayout
    android:id="@+id/linlay_scale_save_buttons"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_alignParentLeft="true" >

    <Button
        android:id="@+id/btn_gpa_cancel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button" />

    <Button
        android:id="@+id/btn_gpa_save"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button" />

</LinearLayout>

 <ListView
     android:id="@+id/listview_scale"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:layout_above="@id/linlay_scale_save_buttons"
     android:layout_alignParentLeft="true"
     android:layout_below="@id/radioGroup1"
     android:focusable="true"
     android:focusableInTouchMode="true"
     android:headerDividersEnabled="true"
     android:scrollingCache="true" >

 </ListView>

List Items XML:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/linlay_scale_list_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >

        <CheckBox
                android:id="@+id/checkbox_scale_item"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="A-" />

            <EditText
                android:id="@+id/et_gpa"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:enabled="true"
                android:inputType="numberDecimal"
                android:maxLength="6"
                android:text="4.000" />

            <EditText
                android:id="@+id/et_min"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:enabled="true"
                android:inputType="numberDecimal"
                android:maxLength="6"
                android:text="94.75" />

            <EditText
                android:id="@+id/et_max"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:inputType="numberDecimal"
                android:maxLength="6"
                android:text="100.0" />

Setting the view in my adapter:

View v = convertView;
ViewHolder holder;

    v = inflater.inflate(R.layout.scale_list_item, 
    holder = new ViewHolder();
    holder.cb = (CheckBox) v.findViewById(R.id.checkbox_scale_item);
    holder.et_gpa = (EditText) v.findViewById(R.id.et_gpa);
    holder.et_min = (EditText) v.findViewById(R.id.et_min);
    holder.et_max = (EditText) v.findViewById(R.id.et_max);

I'm not sure what else to post. I have focus and textChange listeners, but the problem exists even if those are commented out. Let me know if anything else is needed. Thank you.

More detail about how focus is behaving when the EditText is touched:

  1. The EditText is clicked (touched)
  2. EditText Receives focus
  3. EditText loses focus
  4. ListView gains focus (and tries to set child focus view requestChildFocus... doesn't seem to succeed).
  5. ListView loses focus
  6. ListView gains focus (and tries to set child focus again).

The above is based on having listeners for both the EditText and the ListView. Note: with a hardware keyboard, the EditText gets focus and that's that. I think the soft keyboard appearing affects the focus.

FINAL UPDATE: In the end, working with the ListView was too difficult, especially because I wanted to update multiple rows based on changes to an EditText in one row. That's hard to do when the keyboard is up and the ListView only considers a few of the rows to exist at any one time.

I instead made nested LinearLayouts. Each horizontal layout was a "row" and I put them inside of an ArrayList in order to manage them and it was a piece of cake, comparatively (still not easy).

回答1:

You are facing ListView recycling issue. When you scroll up or down or when keyboard appears ListView again refreshes and lost your EditText's focus. So, first of all read this answer to understand ListView Recycling mechanism and then follow the suggestion from my side the solution of your current scenario in my mind.

I suggest you should use a Button on ListView Item and text of this button should be generic like Enter Student Info or what ever you'd like. After clicking on this Button open AlertDialog and set your xml view (currently your all edittexts like et_gpa, et_min, et_max etc on listview items)

For Example:

btnInfo.setOnClickListener(new OnClickListener() {

        public void onClick(View v) {

                showStudentInfoAlert();

}

});

public void showStudentInfoAlert() 
    {
        AlertDialog.Builder builder = new AlertDialog.Builder(context);

       LayoutInflater in = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

View v = in.inflate(R.layout.your_xml_having_edittexts, null);

EditText et_gpa = (EditText) v.findViewById(R.id.et_gpa);

//add all edit texts like this and after that just set view on alert dialog

        builder.setTitle("Enter student's info");

        builder.setView(v);

        builder.setPositiveButton("Save", new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {

               //save all edittexts values and do what erver you want with those values 

            }
        });

        dialog.show();
    }


回答2:

first of all when the screen loads,, focus is on the "Gr High School Scale" field, because of this <requestFocus /> in your xml. but this does no gurantee that the keyboard might show.. to make the keyboard to show use getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); also i am thinking that it looses focus immediately because the listview has taken focus away from the edittext

<ListView
 android:id="@+id/listview_scale"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:layout_above="@id/linlay_scale_save_buttons"
 android:layout_alignParentLeft="true"
 android:layout_below="@id/radioGroup1"
 android:focusable="true" //this
 android:focusableInTouchMode="true" // and that
 android:headerDividersEnabled="true"
 android:scrollingCache="true" >

, and since none of your listview edittext is asking for focus,, the focus stays on just the listview not the edittext..so you need to re-surface the focus back to the edittext..also from the android documentation it states By default the user can not move focus to a view;.. and also it states that "if focusableInTouchMode is true for a view, that view can gain focus when clicked on, and can keep focus if another view is clicked on that doesn't have this attribute set to true. " mostly when you first create an edittext it automatically adds <requestFocus /> to it specifying that it needs to take focus, or it can take focus..but in your case and with this documentaion the edittext will never keep focus because listview is taking it away from him.. so in short try this on all views(including your edittext) you want to take focus like you did on the listview

android:focusable="true" 
android:focusableInTouchMode="true" 

so try that on the edittext and let me know if it helps..thanks for waiting