欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

关于ListView中嵌套EditText,焦点及其他点击问题

程序员文章站 2022-05-30 23:41:36
...

之前项目做过这个类似的UI,处理起来也很麻烦,要求的效果是
1.EditText点击出现输入法,游标显示在最后,效果如图
2.编辑完内容后,收起键盘保存修改内容。

这里会用到几个类
1.自定义Adapter(主要的处理也是在这里)
2.自定义的EditText(处理关闭输入法)
3.网上找的监听输入法关闭和开启SoftKeyboardStateHelper

关于ListView中嵌套EditText,焦点及其他点击问题

ListView的Item的XML文件

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:background="#ffffff"
    android:id="@+id/my_layout">
    <com.example.listandeditfocus.MyEditText
        android:id="@+id/edit"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:text="张三"
        android:gravity="center"
        android:paddingTop="10dp"
        android:paddingBottom="10dp"
        android:background="@null"
       />

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="好的"
        app:layout_constraintStart_toEndOf="@id/edit"
        android:gravity="center"
        app:layout_constraintTop_toTopOf="@id/edit"/>
</android.support.constraint.ConstraintLayout>

这里用到的是自定义的EditText,其实就是重写了一个方法,去处理关闭输入法,后面会介绍,EditText嵌套在ListView中,会出现几个问题
1.EditText游标显示不正常
2.EditText焦点消失
3.造成ListView的setOnItemClickListener方法设置的Listenner无效,即点击Item却没有响应,

下面的代码中会借用到一个类来监听输入法键盘的打开和收起,这里先介绍下
SoftKeyboardStateHelper,这个类的出处来自该类的来源地,代码如下

public class SoftKeyboardStateHelper implements ViewTreeObserver.OnGlobalLayoutListener {

    public interface SoftKeyboardStateListener {
        void onSoftKeyboardOpened(int keyboardHeightInPx);
        void onSoftKeyboardClosed();
    }

    private final List<SoftKeyboardStateListener> listeners = new LinkedList<SoftKeyboardStateListener>();
    private final View activityRootView;
    private int        lastSoftKeyboardHeightInPx;
    private boolean    isSoftKeyboardOpened;

    public SoftKeyboardStateHelper(View activityRootView) {
        this(activityRootView, false);
    }

    public SoftKeyboardStateHelper(View activityRootView, boolean isSoftKeyboardOpened) {
        this.activityRootView     = activityRootView;
        this.isSoftKeyboardOpened = isSoftKeyboardOpened;
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

    @Override
    public void onGlobalLayout() {
        final Rect r = new Rect();
        //r will be populated with the coordinates of your view that area still visible.
        activityRootView.getWindowVisibleDisplayFrame(r);

        final int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
        if (!isSoftKeyboardOpened && heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
            isSoftKeyboardOpened = true;
            notifyOnSoftKeyboardOpened(heightDiff);
        } else if (isSoftKeyboardOpened && heightDiff < 200) {  //这个200是我做的一个修改,实际使用的时候debug发现有时候高度不一定是小于100 我的是189,具体情况具体修改
            isSoftKeyboardOpened = false;
            notifyOnSoftKeyboardClosed();
        }
    }

    public void setIsSoftKeyboardOpened(boolean isSoftKeyboardOpened) {
        this.isSoftKeyboardOpened = isSoftKeyboardOpened;
    }

    public boolean isSoftKeyboardOpened() {
        return isSoftKeyboardOpened;
    }

    /**
     * Default value is zero (0)
     * @return last saved keyboard height in px
     */
    public int getLastSoftKeyboardHeightInPx() {
        return lastSoftKeyboardHeightInPx;
    }

    public void addSoftKeyboardStateListener(SoftKeyboardStateListener listener) {
        listeners.add(listener);
    }

    public void removeSoftKeyboardStateListener(SoftKeyboardStateListener listener) {
        listeners.remove(listener);
    }

    private void notifyOnSoftKeyboardOpened(int keyboardHeightInPx) {
        this.lastSoftKeyboardHeightInPx = keyboardHeightInPx;

        for (SoftKeyboardStateListener listener : listeners) {
            if (listener != null) {
                listener.onSoftKeyboardOpened(keyboardHeightInPx);
            }
        }
    }

    private void notifyOnSoftKeyboardClosed() {
        for (SoftKeyboardStateListener listener : listeners) {
            if (listener != null) {
                listener.onSoftKeyboardClosed();
            }
        }
    }
}

接着看下在ListView的Adapter中进行的处理

public class ListItemAdapter extends BaseAdapter implements SoftKeyboardStateHelper.SoftKeyboardStateListener {
    List<Integer> data = null;
    Context mContext = null;
    private String s;
    boolean isKeyboardOpen = false;
    int currentFocusPosition = -1;
    EditText onFocusEdit = null;

    public ListItemAdapter(List<Integer> data, Context context) {
        this.data = data;
        mContext = context;
    }

    @Override
    public int getCount() {
        return this.data.size();
    }

    @Override
    public Integer getItem(int position) {
        return this.data.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        if (convertView == null) {
            holder = new ViewHolder();
            convertView = View.inflate(mContext, R.layout.item_layout, null);
            holder.editText = convertView.findViewById(R.id.edit);
            holder.layout = convertView.findViewById(R.id.my_layout);
            holder.textView = convertView.findViewById(R.id.tv);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

		/**
			通过给Item的布局文件的最外层的ViewGroup添加OnCLick,来响应Item的点击(解决问题3 ListView的setOnItemClickListener不响应的问题,具体为什么不响应可以看下面的链接)
*/
        holder.layout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mContext.startActivity(new Intent(mContext,Main2Activity.class));
            }
        });

        final ViewHolder finalHolder = holder;
        /**
			给EditText设置FocusChangeListener,监听焦点变化,获取焦点的时候,将游标设置到最后方便删除修改
*/
        holder.editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (hasFocus) {
                    finalHolder.editText.setSelection(finalHolder.editText.getText().length());
                    //保存当前获取到焦点的位置和控件对象
                    currentFocusPosition = position;
                    onFocusEdit = finalHolder.editText;
                } else {
					/**
					失去焦点的时候保存当前的修改内容
*/
                    finalHolder.editText.setText(finalHolder.editText.getText());
                }
            }
        });

/**
	这一步用来处理问题2,焦点消失,点击EditText后获取到焦点,
	输入法打开,焦点又立刻消失,这是因为输入法键盘打开会是布局发生变化,会造成重新调用Adapter的getView方法,
	使EditText的焦点消失,这里我们利用一个变量currentFocusPosition记录获取到焦点的控件所处的位置,
	初始值设置为-1,表示当前没有获得焦点的控件
	(在上方的focusChange中记录),判断位置相等,重新请求获得焦点。
	!!! 注意这里不能用记录的获取焦点的控件,即onFocusEdit来做比较,因为这里的View存在复用,
	这个EditText可能获取的是上一个EditText的焦点
*/
        if (currentFocusPosition == position) {
            holder.editText.requestFocus();
        }
        return convertView;
    }

/**
	下面2个回调方法就是实现了上面的SoftKeyboardStateHelper.SoftKeyboardStateListener接口,获取到键盘打开和关闭提醒(SoftKeyboardStateHelper在Activity中通过
	 SoftKeyboardStateHelper softKeyboardStateHelper = 
	 new SoftKeyboardStateHelper(findViewById(R.id.main_layout));(Activity的布局的最外层ViewGroup)
        softKeyboardStateHelper.addSoftKeyboardStateListener(adapter);)设置
*/
    @Override
    public void onSoftKeyboardOpened(int keyboardHeightInPx) {
        isKeyboardOpen = true;

    }

    @Override
    public void onSoftKeyboardClosed() {
        isKeyboardOpen = false;
        currentFocusPosition = -1;
        //当我们收到输入法键盘关闭的时候,清除当前的EditText获取到的焦点
        if (onFocusEdit != null) {
            onFocusEdit.clearFocus();
        }
    }

    class ViewHolder {
        EditText editText;
        ConstraintLayout layout;
        TextView textView;
    }

OnItemClick不响应分析及为什么给item的外层设置OnCLick可以解决响应问题

接下来是处理关闭输入法,这个处理是在自定义的EditText中进行

public class MyEditText extends android.support.v7.widget.AppCompatEditText {
    public MyEditText(Context context) {
        super(context);
    }

    public MyEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
	    //有的键盘有收起按键,有的没有,所以这里是利用手机的后退键关闭键盘
	    //判断当前点击的是后退,手指抬起,而且!! 当前EditText是有焦点的,关闭输入法,否则按照原处理方式进行
	    //EditText没有焦点 很有可能键盘已经收起了,就不需要再次关闭键盘。这时候之前设置的键盘变化监听就会回调
	    //清除当前焦点
        if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP && this.isFocused()) {
            InputMethodManager inputMethodManager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
            inputMethodManager.hideSoftInputFromWindow(this.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
            return true;
        } else
            return super.onKeyPreIme(keyCode, event);
    }
}

所有处理已完成,如果大家有更好的,更简洁高效的方法,欢迎分享和留言。一起改进处理方式