小心!Listview結(jié)合EditText使用實(shí)例中遇到的那些坑
前幾天一同學(xué)項(xiàng)目中的某個功能需要ListView+EditText來實(shí)現(xiàn),希望我給他寫個Demo,自己就隨手寫了一個小的Demo。后來想了想覺得這個功能其實(shí)挺常用的,而且期間也踩了幾個坑,就整理了一下,希望能夠幫到大家。好了,廢話不多說了,接著就貼代碼。
一、編寫布局文件
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。
為了稍微好看那么一點(diǎn),所以給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,布局代碼已經(jīng)貼完了,接下來就看看咱們的邏輯代碼吧。
二、編寫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); //填充數(shù)據(jù) for(int i = 0; i < DATA_CAPACITY; i++) { mList.add("" + i); } //設(shè)置Adapter mAdapter = new MyAdapter(this, mList); mListView.setAdapter(mAdapter); } }
可以看到MainActivity的代碼邏輯頁比較簡單,主要操作就是生成了一個長度為20的List,然后將其作為數(shù)據(jù)源扔進(jìn)Adapter里面。好了,接下來就讓我們一起來看一下最后的Adapter類。
三、編寫MyAdapter類
好了,終于到了重頭戲,接下來咱們就一步步的編寫Adapter來解決ListView和EditText的各種沖突。
1.最普通的Adapter
首先咱們先按照以往的經(jīng)驗(yàn)寫一個最普通的Adapter,看一下會出現(xiàn)哪些問題:
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; } }
代碼如上,相信大家都寫過無數(shù)遍這樣類似的代碼,讓我們一起看一下這段代碼會出什么問題。運(yùn)行效果如圖所示:
操作a:點(diǎn)擊“0”,光標(biāo)定位到“0”,彈出軟鍵盤,“0”處的光標(biāo)丟失;
操作b:再次點(diǎn)擊“0”,光標(biāo)重新定位到“0”;
之后我又重復(fù)了一遍此步驟,不過點(diǎn)擊的是“1”的位置。
那么為什么會出現(xiàn)這種效果呢?點(diǎn)擊“0”的時候大家看的可能不是太明顯,但點(diǎn)擊“1”的時候大家應(yīng)該能明顯看出來ListView移動了一下。沒錯,正如大家所知道的那樣,軟鍵盤彈出的時候會重新繪制界面,因此ListView進(jìn)行了一次重新繪制,重新走了一邊getView方法,生成了一個新的EditText,而之前展示光標(biāo)的EditText被銷毀,所以才造成了EditText的焦點(diǎn)丟失。既然我們已經(jīng)知道了這個問題的原因,那么接下來我們就來解決掉它吧。
2.解決焦點(diǎn)丟失的問題
解決思路:既然焦點(diǎn)丟失是因?yàn)長istView的重繪導(dǎo)致的,那我們就可以定義一個變量mTouchItemPosition來記錄用戶觸碰的EditText的位置,然后在getView方法中去判斷當(dāng)前的position是否和用戶觸碰的位置相等,如果相等則讓其獲得焦點(diǎn),否則清除焦點(diǎn)。而mTouchItemPosition的值可以在EditText的OnTouch事件中獲取。
代碼實(shí)現(xiàn):
//定義成員變量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; }
讓我們重新運(yùn)行看一下效果:
可以看到焦點(diǎn)丟失這個問題已經(jīng)被我們解決了。接下來就讓我們給EditText增加保存數(shù)據(jù)的功能。
3.添加保存數(shù)據(jù)的功能
首先讓我們來分析一下怎么保存EditText中的數(shù)據(jù)。其實(shí)保存數(shù)據(jù)比較簡單,我們只需要做兩步就可以了,第一步我們需要拿到EditText變化之后的數(shù)據(jù);第二步我們將這些數(shù)據(jù)替換掉之前的就大功告成了。
讓我們再次對MyAdapter類進(jìn)行修改,而用于TextWatcher的afterTextChanged方法中獲取不到當(dāng)前position,所以我們需要新建一個內(nèi)部類MyTextWatcher實(shí)現(xiàn)TextWatcher接口并持有一個position,其次在ViewHolder中需要持有一個MyTextWatcher的引用來動態(tài)更新其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,動態(tài)更新position來防治數(shù)據(jù)錯亂;不能將position定義成final直接使用,必須動態(tài)更新 mViewHolder.mTextWatcher = new MyTextWatcher(); mViewHolder.mEditText.addTextChangedListener(mViewHolder.mTextWatcher); mViewHolder.updatePosition(position); convertView.setTag(mViewHolder); } else { mViewHolder = (ViewHolder) convertView.getTag(); //動態(tài)更新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; //動態(tài)更新TextWathcer的position public void updatePosition(int position) { mTextWatcher.updatePosition(position); } } class MyTextWatcher implements TextWatcher { //由于TextWatcher的afterTextChanged中拿不到對應(yīng)的position值,所以自己創(chuàng)建一個子類 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()); } };
現(xiàn)在保存數(shù)據(jù)的問題也已經(jīng)完成了,接下來讓我們看最后一個滾動沖突的問題。
4.解決滾動沖突的問題
其實(shí)這個問題我在完美解決EditText和ScrollView的滾動沖突(上)和 完美解決EditText和ScrollView的滾動沖突(下)這兩篇博客中詳細(xì)的講過,原理都是一樣的,所以這兒就不多說了,直接將原來的代碼拷過來就可以了。感興趣的同學(xué)可以去看一下之前的兩篇。
@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并且當(dāng)前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,動態(tài)更新position來防治數(shù)據(jù)錯亂;不能將position定義成final直接使用,必須動態(tài)更新 mViewHolder.mTextWatcher = new MyTextWatcher(); mViewHolder.mEditText.addTextChangedListener(mViewHolder.mTextWatcher); mViewHolder.updatePosition(position); convertView.setTag(mViewHolder); } else { mViewHolder = (ViewHolder) convertView.getTag(); //動態(tài)更新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(); //控件內(nèi)容的總高度 int scrollRange = editText.getLayout().getHeight(); //控件實(shí)際顯示的高度 int scrollExtent = editText.getHeight() - editText.getCompoundPaddingTop() -editText.getCompoundPaddingBottom(); //控件內(nèi)容總高度與實(shí)際顯示高度的差值 int scrollDifference = scrollRange - scrollExtent; if(scrollDifference == 0) { return false; } return (scrollY > 0) || (scrollY < scrollDifference - 1); }
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android中實(shí)現(xiàn)下載和解壓zip文件功能代碼分享
這篇文章主要介紹了Android中實(shí)現(xiàn)下載和解壓zip文件功能代碼分享,本文直接給出了實(shí)現(xiàn)代碼,需要的朋友可以參考下2015-03-03Android創(chuàng)建Menu菜單實(shí)例
這篇文章主要介紹了Android創(chuàng)建Menu菜單實(shí)例,講述了Android菜單項(xiàng)的創(chuàng)建方法,在Android應(yīng)用程序開發(fā)中非常具有實(shí)用價值,需要的朋友可以參考下2014-10-10Android無需權(quán)限調(diào)起系統(tǒng)相機(jī)
在進(jìn)行一些小型APP的開發(fā),或者是對拍照界面沒有自定義要求時,我們可以用調(diào)起系統(tǒng)相機(jī)的方式快速完成拍照需求2023-03-03Android檢測手機(jī)多點(diǎn)觸摸點(diǎn)數(shù)的方法
這篇文章主要為大家詳細(xì)介紹了Android檢測手機(jī)多點(diǎn)觸摸點(diǎn)數(shù)的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-05-05android基于SwipeRefreshLayout實(shí)現(xiàn)類QQ的側(cè)滑刪除
本篇文章主要介紹了android基于SwipeRefreshLayout實(shí)現(xiàn)類QQ的側(cè)滑刪除,非常具有實(shí)用價值,需要的朋友可以參考下2017-10-10Android實(shí)現(xiàn)梯形TextView效果
TextView(文本框),用于顯示文本的一個控件,Android開發(fā)中經(jīng)常使用,本文講述如何實(shí)現(xiàn)一個梯形的TextView2021-05-05Android實(shí)現(xiàn)循環(huán)輪播跑馬燈的效果
這篇文章主要介紹了為大家詳細(xì)介紹了如何通過Android實(shí)現(xiàn)循環(huán)輪播跑馬燈的效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-05-05詳解Retrofit 動態(tài)參數(shù)(非固定參數(shù)、非必須參數(shù))(Get、Post請求)
這篇文章主要介紹了詳解Retrofit 動態(tài)參數(shù)(非固定參數(shù)、非必須參數(shù))(Get、Post請求),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-04-04解決android 顯示內(nèi)容被底部導(dǎo)航欄遮擋的問題
今天小編就為大家分享一篇解決android 顯示內(nèi)容被底部導(dǎo)航欄遮擋的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-07-07