关于ListView中嵌套EditText,焦点及其他点击问题
程序员文章站
2022-05-30 23:41:36
...
之前项目做过这个类似的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);
}
}
所有处理已完成,如果大家有更好的,更简洁高效的方法,欢迎分享和留言。一起改进处理方式
上一篇: Android UI之EditText常用技巧整理
下一篇: 提示无法加载控制器View是怎么回事?