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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/One_Month/article/details/80584473

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

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

这里写图片描述

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);
    }
}

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

猜你喜欢

转载自blog.csdn.net/One_Month/article/details/80584473