小心!Listview结合EditText使用实例中遇到的那些坑_Android

前几天一同学项目中的某个功能需要ListView+EditText来实现,希望我给他写个Demo,自己就随手写了一个小的Demo。后来想了想觉得这个功能其实挺常用的,而且期间也踩了几个坑,就整理了一下,希望能够帮到大家。好了,废话不多说了,接着就贴代码。

一、编写布局文件
1.activity的布局activity_main

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 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.example.mytestdemo.MainActivity" >

 <ListView
 android:id="@+id/list_view"
 android:layout_width="match_parent"
 android:layout_height="match_parent"/>

</RelativeLayout>

只有一个ListView,所以就不多说了。
2.item的布局edittext_item

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/item_content"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:paddingTop="20dp"
 android:paddingBottom="20dp"
 android:orientation="horizontal" >
 <TextView
 android:id="@+id/text_view"
 android:layout_width="wrap_content"
 android:layout_gravity="center_vertical"
 android:gravity="center_vertical"
 android:layout_height="50dp"/>
 <EditText
 android:id="@+id/edit_text"
 android:layout_gravity="center_vertical"
 android:layout_marginLeft="30dp"
 android:layout_width="match_parent"
 android:layout_height="100dp"
 android:gravity="top"
 android:padding="5dp"
 android:background="@drawable/shape_edittext"/>

</LinearLayout>

一个水平的LinearLayout,里面有一个TextView和一个EditText。
为了稍微好看那么一点,所以给EditText加了一个圆角矩形背景。

3.EditText的圆角矩形背景shape_edittext

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
 <solid android:color="#FFFFFFFF"/>
 <stroke android:width="1dp"
 android:color="#000000"/>
 <corners android:radius="5dp"/>
</shape>

OK,布局代码已经贴完了,接下来就看看咱们的逻辑代码吧。

二、编写MainActivity类

public class MainActivity extends Activity {
 private static final String TAG = "zbw";
 private static final int DATA_CAPACITY = 20;

 private ListView mListView;
 private List<String> mList = new ArrayList<String>(DATA_CAPACITY);
 private MyAdapter mAdapter;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 mListView = (ListView) findViewById(R.id.list_view);

 //填充数据
 for(int i = 0; i < DATA_CAPACITY; i++) {
 mList.add("" + i);
 }

 //设置Adapter
 mAdapter = new MyAdapter(this, mList);
 mListView.setAdapter(mAdapter);
 }
}

可以看到MainActivity的代码逻辑页比较简单,主要操作就是生成了一个长度为20的List,然后将其作为数据源扔进Adapter里面。好了,接下来就让我们一起来看一下最后的Adapter类。

三、编写MyAdapter类
好了,终于到了重头戏,接下来咱们就一步步的编写Adapter来解决ListView和EditText的各种冲突。
1.最普通的Adapter
首先咱们先按照以往的经验写一个最普通的Adapter,看一下会出现哪些问题:

public class MyAdapter extends BaseAdapter {
 private ViewHolder mViewHolder;
 private LayoutInflater mLayoutInflater;
 private List<String> mList;

 public MyAdapter(Context context, List<String> list) {
 mLayoutInflater = LayoutInflater.from(context);
 mList = list;
 }

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

 @Override
 public Object getItem(int position) {
 return mList.get(position);
 }

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

 @Override
 public View getView(final int position, View convertView, ViewGroup parent) {
 if (convertView == null) {
 mViewHolder = new ViewHolder();
 convertView = mLayoutInflater.inflate(R.layout.edittext_item, null);
 mViewHolder.mTextView = (TextView) convertView.findViewById(R.id.text_view);
 mViewHolder.mEditText = (EditText) convertView.findViewById(R.id.edit_text);
 convertView.setTag(mViewHolder);
 } else {
 mViewHolder = (ViewHolder) convertView.getTag();
 }

 if (position <= 9) {
 mViewHolder.mTextView.setText("0" + (position));
 } else {
 mViewHolder.mTextView.setText("" + (position));
 }
 mViewHolder.mEditText.setText(mList.get(position));
 return convertView;
 }

 static final class ViewHolder {
 TextView mTextView;
 EditText mEditText;
 }
}

代码如上,相信大家都写过无数遍这样类似的代码,让我们一起看一下这段代码会出什么问题。运行效果如图所示:

操作a:点击“0”,光标定位到“0”,弹出软键盘,“0”处的光标丢失;
操作b:再次点击“0”,光标重新定位到“0”;
之后我又重复了一遍此步骤,不过点击的是“1”的位置。
那么为什么会出现这种效果呢?点击“0”的时候大家看的可能不是太明显,但点击“1”的时候大家应该能明显看出来ListView移动了一下。没错,正如大家所知道的那样,软键盘弹出的时候会重新绘制界面,因此ListView进行了一次重新绘制,重新走了一边getView方法,生成了一个新的EditText,而之前展示光标的EditText被销毁,所以才造成了EditText的焦点丢失。既然我们已经知道了这个问题的原因,那么接下来我们就来解决掉它吧。

2.解决焦点丢失的问题
解决思路:既然焦点丢失是因为ListView的重绘导致的,那我们就可以定义一个变量mTouchItemPosition来记录用户触碰的EditText的位置,然后在getView方法中去判断当前的position是否和用户触碰的位置相等,如果相等则让其获得焦点,否则清除焦点。而mTouchItemPosition的值可以在EditText的OnTouch事件中获取。
代码实现:

 //定义成员变量mTouchItemPosition,用来记录手指触摸的EditText的位置
 private int mTouchItemPosition = -1;
 ...
 @Override
 public View getView(int position, View convertView, ViewGroup parent) {
 if (convertView == null) {
 mViewHolder = new ViewHolder();
 convertView = mLayoutInflater.inflate(R.layout.edittext_item, null);
 mViewHolder.mTextView = (TextView) convertView.findViewById(R.id.text_view);
 mViewHolder.mEditText = (EditText) convertView.findViewById(R.id.edit_text);

 mViewHolder.mEditText.setOnTouchListener(new OnTouchListener() {
 @Override
 public boolean onTouch(View view, MotionEvent event) {
  //注意,此处必须使用getTag的方式,不能将position定义为final,写成mTouchItemPosition = position
  mTouchItemPosition = (Integer) view.getTag();
  return false;
 }
 });

 convertView.setTag(mViewHolder);
 } else {
 mViewHolder = (ViewHolder) convertView.getTag();
 }

 if (position <= 9) {
 mViewHolder.mTextView.setText("0" + (position));
 } else {
 mViewHolder.mTextView.setText("" + (position));
 }
 mViewHolder.mEditText.setText(mList.get(position));

 mViewHolder.mEditText.setTag(position);

 if (mTouchItemPosition == position) {
 mViewHolder.mEditText.requestFocus();
 mViewHolder.mEditText.setSelection(mViewHolder.mEditText.getText().length());
 } else {
 mViewHolder.mEditText.clearFocus();
 }

 return convertView;
 }

让我们重新运行看一下效果:

可以看到焦点丢失这个问题已经被我们解决了。接下来就让我们给EditText增加保存数据的功能。

3.添加保存数据的功能
首先让我们来分析一下怎么保存EditText中的数据。其实保存数据比较简单,我们只需要做两步就可以了,第一步我们需要拿到EditText变化之后的数据;第二步我们将这些数据替换掉之前的就大功告成了。
让我们再次对MyAdapter类进行修改,而用于TextWatcher的afterTextChanged方法中获取不到当前position,所以我们需要新建一个内部类MyTextWatcher实现TextWatcher接口并持有一个position,其次在ViewHolder中需要持有一个MyTextWatcher的引用来动态更新其position的值,代码如下:

@Override
 public View getView(int position, View convertView, ViewGroup parent) {
 if (convertView == null) {
 mViewHolder = new ViewHolder();
 convertView = mLayoutInflater.inflate(R.layout.edittext_item, null);
 mViewHolder.mTextView = (TextView) convertView.findViewById(R.id.text_view);
 mViewHolder.mEditText = (EditText) convertView.findViewById(R.id.edit_text);

 mViewHolder.mEditText.setOnTouchListener(new OnTouchListener() {

 @Override
 public boolean onTouch(View view, MotionEvent event) {
  //注意,此处必须使用getTag的方式,不能将position定义为final,写成mTouchItemPosition = position
  mTouchItemPosition = (Integer) view.getTag();
  return false;
 }
 });

 // 让ViewHolder持有一个TextWathcer,动态更新position来防治数据错乱;不能将position定义成final直接使用,必须动态更新
 mViewHolder.mTextWatcher = new MyTextWatcher();
 mViewHolder.mEditText.addTextChangedListener(mViewHolder.mTextWatcher);
 mViewHolder.updatePosition(position);

 convertView.setTag(mViewHolder);
 } else {
 mViewHolder = (ViewHolder) convertView.getTag();
 //动态更新TextWathcer的position
 mViewHolder.updatePosition(position);
 }

 if (position <= 9) {
 mViewHolder.mTextView.setText("0" + (position));
 } else {
 mViewHolder.mTextView.setText("" + (position));
 }
 mViewHolder.mEditText.setText(mList.get(position));

 mViewHolder.mEditText.setTag(position);

 if (mTouchItemPosition == position) {
 mViewHolder.mEditText.requestFocus();
 mViewHolder.mEditText.setSelection(mViewHolder.mEditText.getText().length());
 } else {
 mViewHolder.mEditText.clearFocus();
 }

 return convertView;
 }

 static final class ViewHolder {
 TextView mTextView;
 EditText mEditText;
 MyTextWatcher mTextWatcher;

 //动态更新TextWathcer的position
 public void updatePosition(int position) {
 mTextWatcher.updatePosition(position);
 }
 }

 class MyTextWatcher implements TextWatcher {
 //由于TextWatcher的afterTextChanged中拿不到对应的position值,所以自己创建一个子类
 private int mPosition;

 public void updatePosition(int position) {
 mPosition = position;
 }

 @Override
 public void onTextChanged(CharSequence s, int start, int before, int count) {

 }

 @Override
 public void beforeTextChanged(CharSequence s, int start, int count, int after) {

 }

 @Override
 public void afterTextChanged(Editable s) {
 mList.set(mPosition, s.toString());
 }
 };

现在保存数据的问题也已经完成了,接下来让我们看最后一个滚动冲突的问题。

4.解决滚动冲突的问题
其实这个问题我在完美解决EditText和ScrollView的滚动冲突(上)和 完美解决EditText和ScrollView的滚动冲突(下)这两篇博客中详细的讲过,原理都是一样的,所以这儿就不多说了,直接将原来的代码拷过来就可以了。感兴趣的同学可以去看一下之前的两篇。

@Override
 public View getView(int position, View convertView, ViewGroup parent) {
 if (convertView == null) {
 mViewHolder = new ViewHolder();
 convertView = mLayoutInflater.inflate(R.layout.edittext_item, null);
 mViewHolder.mTextView = (TextView) convertView.findViewById(R.id.text_view);
 mViewHolder.mEditText = (EditText) convertView.findViewById(R.id.edit_text);

 mViewHolder.mEditText.setOnTouchListener(new OnTouchListener() {

 @Override
 public boolean onTouch(View view, MotionEvent event) {
  //注意,此处必须使用getTag的方式,不能将position定义为final,写成mTouchItemPosition = position
  mTouchItemPosition = (Integer) view.getTag();

  //触摸的是EditText并且当前EditText可以滚动则将事件交给EditText处理;否则将事件交由其父类处理
  if ((view.getId() == R.id.edit_text && canVerticalScroll((EditText)view))) {
  view.getParent().requestDisallowInterceptTouchEvent(true);
  if (event.getAction() == MotionEvent.ACTION_UP) {
  view.getParent().requestDisallowInterceptTouchEvent(false);
  }
  }
  return false;
 }
 });

 // 让ViewHolder持有一个TextWathcer,动态更新position来防治数据错乱;不能将position定义成final直接使用,必须动态更新
 mViewHolder.mTextWatcher = new MyTextWatcher();
 mViewHolder.mEditText.addTextChangedListener(mViewHolder.mTextWatcher);
 mViewHolder.updatePosition(position);

 convertView.setTag(mViewHolder);
 } else {
 mViewHolder = (ViewHolder) convertView.getTag();
 //动态更新TextWathcer的position
 mViewHolder.updatePosition(position);
 }

 if (position <= 9) {
 mViewHolder.mTextView.setText("0" + (position));
 } else {
 mViewHolder.mTextView.setText("" + (position));
 }
 mViewHolder.mEditText.setText(mList.get(position));

 mViewHolder.mEditText.setTag(position);

 if (mTouchItemPosition == position) {
 mViewHolder.mEditText.requestFocus();
 mViewHolder.mEditText.setSelection(mViewHolder.mEditText.getText().length());
 } else {
 mViewHolder.mEditText.clearFocus();
 }

 return convertView;
 }

 /**
 * EditText竖直方向是否可以滚动
 * @param editText 需要判断的EditText
 * @return true:可以滚动 false:不可以滚动
 */
 private boolean canVerticalScroll(EditText editText) {
 //滚动的距离
 int scrollY = editText.getScrollY();
 //控件内容的总高度
 int scrollRange = editText.getLayout().getHeight();
 //控件实际显示的高度
 int scrollExtent = editText.getHeight() - editText.getCompoundPaddingTop() -editText.getCompoundPaddingBottom();
 //控件内容总高度与实际显示高度的差值
 int scrollDifference = scrollRange - scrollExtent;

 if(scrollDifference == 0) {
 return false;
 }

 return (scrollY > 0) || (scrollY < scrollDifference - 1);
 }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索listview
, EditText焦点丢失
滚动冲突
listview嵌套edittext、listview edittext、listview中edittext、edittext搜索listview、listview中的edittext,以便于您获取更多的相关知识。

时间: 2024-08-04 07:45:30

小心!Listview结合EditText使用实例中遇到的那些坑_Android的相关文章

小心!Listview结合EditText使用实例中遇到的那些坑

前几天一同学项目中的某个功能需要ListView+EditText来实现,希望我给他写个Demo,自己就随手写了一个小的Demo.后来想了想觉得这个功能其实挺常用的,而且期间也踩了几个坑,就整理了一下,希望能够帮到大家.好了,废话不多说了,接着就贴代码. 一.编写布局文件 1.activity的布局activity_main <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xml

Android编程之ListView和EditText发布帖子隐藏软键盘功能详解

本文实例讲述了Android编程之ListView和EditText发布帖子隐藏软键盘功能.分享给大家供大家参考,具体如下: 在Android开发中,手动调用软件盘的隐藏和显示有时候也是非常常见的需求. EditText控件实现了点击打开软键盘输入功能,but why ? 为什么EditText可以点击弹出keyboard,而TextView却不可以,EditText继承TextView做了哪些修改呢?关于这些问题得查看相关具体代码如何实现可以参考,看似简单的控件其实系统封装实现的很复杂.这里告

ASM实例中使用ASMCMD工具管理ASM目录及文件

在ASM实例中,所有的存储于ASM磁盘组中的文件对于操作系统命令而言是不可访问的,因此也无法使用常规的命令来操纵ASM磁盘中的文件.所幸的是,我们有ASMCMD工具来代替操作系统命令来完成这部分工作.ASMCMD工具提供了类似于操作系统的常用命令,如ls , du ,find,cd ,rm ,mkdir等等.借助这些工具可以更轻松的完成ASM实例的相关管理工作. 1.ASMCMD文件所在的位置 [root@oradb ~]# su - oracle [oracle@oradb ~]$ which

iBATIS分页实例中ObjectDataSource的应用浅析

iBATIS分页实例中ObjectDataSource的应用首先让我们来看看属性方面的特点,ObjectDataSource 控件内置了对分页的支持.我们需要设置 ObjectDataSource 的 EnablePaging属性,然后要设置SelectMethod.SelectCountMethod .StartRowIndexParameterName和MaximumRowsParameterName 属性.当 EnablePaging 属性设置为 true 时,SelectParamete

这里我们可以看到:Person是个多层次对象,包含多层嵌入属性对象(multi-layer embeded objects)。如果需要更改Person类型实例中的任何字段时,我们可以直接用行令方式(imperative style):

  scala中的case class是一种特殊的对象:由编译器(compiler)自动生成字段的getter和setter.如下面的例子: 1 case class City(name:String, province: String) 2 case class Address(street: String, zip: String, city: City) 3 case class Person(name: String, age: Int, phone: String, address:

sql server 2012的数据库实例中的bin可执行文件存放问题

问题描述 sql server 2012的数据库实例中的bin可执行文件存放问题 怎样才能将sql server 2012的数据库实例中的bin可执行文件部分放到c盘,数据部分放到d盘 因为公司是希望C盘是程序,为避免病毒等,以后就固定不要动了 D盘放以后每天工作变动的东西 多谢! 解决方案 安装的时候程序默认装C盘,配置实例的时候可以选D盘啊.SQL Server 2012实例配置 解决方案二: sql server2012附加数据库问题Sql server 2012 创建数据库架构关于SQL

呈现用户控件错误,未将对象引用设置到对像实例中

问题描述 我创建了一个WEB用户控件,拖拽的时候出现呈现用户控件错误,未将对象引用设置到对像实例中的错误,但是控件可以正常运行,并能得到结果.以下是我创建控件TextBoxCustom.cs的代码:usingSystem;usingSystem.Collections.Generic;usingSystem.ComponentModel;usingSystem.Text;usingSystem.Web;usingSystem.Web.UI;usingSystem.Web.UI.WebContro

openstack 启动docker实例,docker实例中运行手写的发包程序,docker实例无法启动

问题描述 各位好,小弟最近遇到了一个问题,已经搞了好多天了.背景:就是我想用openstack启动docker实例(docker镜像里面有个http请求客户端),让docker实例启动之后,请求默认的HTTP服务器.我在openstackKilo版本,(controller+network+compute1(kvm)+compute2(docker)),装了novadockerdriver.可以用openstack启动docker实例,但是只能启动两个指定的镜像(类似于ubuntu-sshd这种

网络爬虫-用Java来抓取网页实例中HttpClient类的问题

问题描述 用Java来抓取网页实例中HttpClient类的问题 报这么一大堆错误我也是受不了了...... 主要的问题应该是HttpClient类这个东西,在网上查了这个类是httpclient-2.x.jar包的产物,我导入的是httpclient-4.2.2.jar和httpcore-4.2.2.jar包,而这两个新的工具包并不包含HttpClient类,查阅了Java API帮助文档后,自己并没有找到HttpClient类的替代类,而是一堆接口和抽象类,由于是刚开始写这个,所以有点懵.