ListView中的多个EditText,点击以聚焦一个EditText并将焦点跳转到第一个

时间:2017-08-03 00:41:17

标签: android listview focus

我在ListView中的行中有EditTexts。当我点击其中一个EditTexts时,软键盘出现,焦点跳转到列表中的第一个EditText,而不是停留在我点击的字段中。

以下是视频:

https://youtu.be/ZwuFrX-WWBo

我创建了一个完全剥离的应用程序来演示问题。完整代码位于:https://pastebin.com/YT8rxqKa

我没有做任何事情来改变我的代码中的焦点:

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    if (convertView == null) {
        convertView = layoutInflater.inflate(R.layout.cell_textfield, parent, false);
    }

    TextView label = (TextView) convertView.findViewById(R.id.textview1);
    EditText textfield = (EditText) convertView.findViewById(R.id.textview2);

    String text = String.format("Row %d", position);
    label.setText(text);
    textfield.setText(text);

    return convertView;
}

我发现another post on StackOverflow为这种愚蠢的Android行为提供了一种解决方法,其中包括在所有文本字段上放置一个OnFocusChangedListener,以便在不正确地从它们中取出时可以重新获得焦点。

这有助于重新获得焦点,但随后我发现当文本字段重新聚焦时,光标会在文本的开头而不是结束处结束,这对我的用户来说是不自然的并且很烦人。

以下是视频:

https://youtu.be/A35wLqbuIac

这是OnFocusChangeListener的代码。它可以解决移动焦点的愚蠢Android行为,但是光标在重新获得焦点后会被放错位置。

View.OnFocusChangeListener onFocusChangeListener = new View.OnFocusChangeListener() {
    @Override
    public void onFocusChange(View view, boolean hasFocus) {
        long t = System.currentTimeMillis();
        long delta = t - focusTime;
        if (hasFocus) {     // gained focus
            if (delta > minDeltaForReFocus) {
                focusTime = t;
                focusTarget = view;
            }
        }
        else {              // lost focus
            if (delta <= minDeltaForReFocus &&  view == focusTarget) {
                focusTarget.post(new Runnable() {   // reset focus to target
                    public void run() {
                        Log.d("BA", "requesting focus");
                        focusTarget.requestFocus();
                    }
                });
            }
        }
    }
};

我讨厌在一个绑在一个绑带上绑一个绑带,试图让Android按照自然预期的行为表现,但我会采取我能得到的。

1)我可以做些什么来解决这个问题,而不必使用OnFocusChangeListener吗?

2)如果(1)不可能,那么当我将焦点强制回到正确的字段时,我如何确保将光标放在最后?我在requestFocus()之后尝试使用setSelection(),但由于文本字段尚未聚焦,因此忽略了选择。

2 个答案:

答案 0 :(得分:2)

这是我的解决方案。&#34;简而言之:ListView是愚蠢的,并且在涉及EditText时总是一场噩梦,因此我更改了Fragment / Adapter代码以适应ListView布局或ScrollView布局。它只适用于行数较少的情况,因为scrollview实现无法利用延迟加载和查看回收。值得庆幸的是,在ListView中我想要EditTexts的任何情况,我很少有超过20行左右。

BaseListFragment中夸大我的观点时,我会通过依赖hasTextFields()方法的方法获取布局ID:

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(getLayoutId(), container, false);
    return view;
}

public boolean hasTextfields() {
    return false;
}

public int getLayoutId() {
    if (hasTextfields()) {
        return R.layout.scrollfragment;
    } else {
        return R.layout.listfragment;
    }
}

在我的BaseListFragment的各个子类中,如果我需要在其中一个字段中有EditText,我只需覆盖hasTextFields()方法返回true然后我的片段/适配器切换到使用基本的scrollview实现。

从那里开始,确保适配器处理ListViewListView方案的标准ScrollView操作是一个问题。像这样:

public void notifyDataSetChanged() {
    // If scrollContainer is not null, that means we're in a ScrollView setup
    if (this.scrollContainer != null) {
        // intentionally not calling super
        this.scrollContainer.removeAllViews();
        this.setupRows();
    } else {
        // use the real ListView
        super.notifyDataSetChanged();
    }
}

public void setupRows() {
    for (int i = 0; i < this.getCount(); i++) {
        View view = this.getView(i, null, this.scrollContainer);
        view.setOnClickListener(myItemClickListener);
        this.scrollContainer.addView(view);
    }
}

点击侦听器提出的一个问题是ListView需要AdapterView.OnItemClickListener,但View内的任意ScrollView想要一个简单的View.OnClickListener。所以,我让我的ItemClickListener也实现了View.OnClickListener,然后只是将OnClick调度到OnItemClick方法:

public class MyItemClickListener implements AdapterView.OnItemClickListener, View.OnClickListener {

    @Override
    public void onClick(View v) {
        // You can either have your Adapter set the tag on the View to be its position
        // or you could have your click listener use v.getParent() and iterate through
        // the children to find the position. I find its faster and easier to have my
        // adapter set the Tag on the view.
        int position = v.getTag();
        this.onItemClick(null, v, config.getPosition(), 0);
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        // ...
    }
}

然后在MyEditTextListFragment中,我创建了这样的适配器:

listener = createClickListener();
adapter = createListAdapter();

if (scrollContainer != null) {
    adapter.setScrollContainer(scrollContainer);
    adapter.setMenuItemClickListener(listener);
    adapter.setupRows();

} else {
    getListView().setOnItemClickListener(listener);
    getListView().setAdapter(adapter);
}

以下是我的scrollfragment.xml供参考:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#fff"
    android:clickable="true"
    >

    <!-- 
        The following LinearLayout as a focus catcher that won't cause the keyboard to 
        show without it, the virtual keyboard shows up immediately/always which means we 
        never get to the enjoy the full size of our screen while scrolling, and 
        that sucks.
    -->
    <LinearLayout
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:layout_width="0px"
        android:layout_height="0px"/>

    <!--
        This ListView is still included in the layout but set to visibility=gone. List 
        fragments require a standard ListView in the layout, so this gets us past that
        check and allows us to use the same adapter code in both listview and scrollview
        situations.
    -->
    <ListView android:id="@id/android:list"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:layout_weight="1"
              android:drawSelectorOnTop="false"
              android:background="@null"
              android:layout_alignParentTop="true"
              android:descendantFocusability="afterDescendants"

              android:visibility="gone"
        />


    <!-- 
        This scrollview will act as our fake listview so that we don't have to deal with
        all the stupid crap that comes along with having EditTexts inside a ListView.
    -->
    <ScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:descendantFocusability="afterDescendants"
        >

        <LinearLayout
            android:id="@+id/scrollContainer"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            >

        </LinearLayout>
    </ScrollView>
</RelativeLayout>

答案 1 :(得分:0)

尝试一次,它对我有用:

public void setCursorPosition() {
                focusTarget.requestFocus();
                focusTarget.setCursorVisible(true);
                other.setCursorVisible(false);
            } else {
                other.setCursorVisible(true);
                focusTarget.setCursorVisible(false);
            }
        }