EditText+ListView實時重新整理ListView
阿新 • • 發佈:2018-12-18
寫給自己和各位讀者:
首先說本人目前還是個菜鳥,每當遇到問題時,第一時間想到的是在網上查詢大神的解決方案,其實在借鑑他人的方法時,也是在不斷地提升自己,不管是程式碼的閱讀能力,還是接收新知識,還是僅僅為以後的專案開發留下一個印象,都起到了很好的幫助。總之,多上網查詢資料,多看一些好的解決方法,當你在實際開發中遇到這些問題時你就可以信手拈來。下面是我最近碰到的,通過在文字框中輸入內容,如何實時重新整理ListView的問題。
下面是效果圖:
搜尋前:
搜尋後:
根據使用的ListView介面卡的不同,有兩種不同的實現方法。
第一種,如果你的ListView使用的是系統的ArrayAdapter,那麼這個事情就變得很簡單,只需要幾行程式碼就可以解決:
//給編輯框新增監聽事件 etSearch.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { //文字改變時,給adapter設定過濾器就行了,只需要一行程式碼 listViewAdapter.getFilter().filter(charSequence); } @Override public void afterTextChanged(Editable editable) { } });
第二種:由於大多數Adapter都是自定義的,所以不能直接呼叫上面這個方法,但是我們可以模仿ArrayAdapter來實現這個功能。那就是實現Filterable介面,通過實現Filterrable介面,我們就可以像上面那樣呼叫getFilter()和filter()方法了。
首先要有一個實體類,用來模擬資料。
public class AppInfoBitmap { private String mName; private Bitmap mBitmap; //無參構造 public AppInfoBitmap() { } //帶參構造 public AppInfoBitmap(String mName, Bitmap mBitmap) { this.mName = mName; this.mBitmap = mBitmap; } public String getmName() { return mName; } public void setmName(String mName) { this.mName = mName; } public Bitmap getmBitmap() { return mBitmap; } public void setmBitmap(Bitmap mBitmap) { this.mBitmap = mBitmap; } }
接下來自定義一個Adapter(ListViewAdapter)繼承自BaseAdapter,實現了Filterable介面。
實現Filterable,必須要實現兩個方法。
分別是:
protected FilterResults performFiltering(CharSequence charSequence);
protected void publishResults(CharSequence charSequence, FilterResults filterResults);
其次我們向外提供了一個getFilter()方法,用來生成Filter物件,進而呼叫Filter類中的filter()方法,最終實現過濾功能。
public Filter getFilter() {
if (mFilter==null){
mFilter=new ArrayFilter();
}
return mFilter;
}
下面就是比較完整的程式碼了(此處沒有包含Adapter一些常用處理,用最簡潔的程式碼來說明問題):
public class AppListViewAdapter extends BaseAdapter implements Filterable{
private Context mContext;
//Adapter資料來源
private List<AppInfoBitmap> mAppInfoList;
//過濾相關
/*
* 過濾器上的鎖可以同步複製原始資料
* */
private final Object mLock=new Object();
//物件陣列的備份,當呼叫ArrayFilter的時候初始化和使用。此時,物件陣列只包含已經過濾的資料。
private ArrayList<AppInfoBitmap> mOriginalValues;
private ArrayFilter mFilter;
@Override
public Filter getFilter() {
if (mFilter==null){
mFilter=new ArrayFilter();
}
return mFilter;
}
//寫一個ArrayFilter類繼承Filter
public class ArrayFilter extends Filter{
//執行過濾的方法
@Override
protected FilterResults performFiltering(CharSequence charSequence) {
/*
* 一個帶有首字母約束的陣列過濾器,每一項
* 不是以該首字母開頭的都會被移除該list
* */
//過濾的結果
FilterResults results=new FilterResults();
//原始資料為空時,上鎖,同步複製原始資料
if (mOriginalValues==null){
synchronized (mLock){
mOriginalValues=new ArrayList<>(mAppInfoList);
}
}
//當首字母為空時
if (charSequence==null||charSequence.length()==0){
ArrayList<AppInfoBitmap> list;
synchronized (mLock){
//同步複製一個原始備份資料
list=new ArrayList<>(mOriginalValues);
}
//此時返回的results就是原始的資料,不必進行過濾
results.values=list;
results.count=list.size();
}else {
//轉化為小寫
String prefixString=charSequence.toString().toLowerCase() ;
ArrayList<AppInfoBitmap> values;
//同步複製一個原始備份資料
synchronized (mLock){
values=new ArrayList<>(mOriginalValues);
}
final int count=values.size();
final ArrayList<AppInfoBitmap> newValues =new ArrayList<>();
for (int i = 0; i < count; i++) {
//從ArrayList<AppInfoBitmap>中拿到AppInfoBitmap物件
final AppInfoBitmap value=values.get(i);
/*
* 這個地方是,要進行搜尋的關鍵字,將app的name屬性當作關鍵字來進行篩選
* */
final String valueText=value.getmName().toString().toLowerCase();
/*
* 首字元匹配:valueText.startsWith(prefixString)
* 輸入的關鍵字包含在了某個item中:valueText.indexOf(prefixString.toString())!=-1
* */
if (valueText.startsWith(prefixString)|| valueText.indexOf(prefixString.toString())!=-1){
newValues.add(value);
}
else {
//處理首字元是空格,去掉text中的空格,然後賦值給字串words
final String[] words=valueText.split(" ");
//獲取除去空格的字串的長度
final int wordCount=words.length;
//Start at index 0,in case valueText starts with space(s)
for (int k = 0; k < wordCount; k++) {
if (words[k].startsWith(prefixString)){
//一旦找到匹配的就break,跳出for迴圈
newValues.add(value);
break;
}
}
}
}
//此時的results就是過濾後的List<AppInfoBitmap>
results.values=newValues;
results.count=newValues.size();
}
//將結果返回
return results;
}
//篩選結果
@Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
//此時,Adapter資料來源就是過濾後的esults
mAppInfoList=(List<AppInfoBitmap>) filterResults.values;
/*
* 資料容器變化------->notifyDataSetInValidated
* 容器中資料發生變化------->notifyDataSetChanged
* */
if (filterResults.count>0){
//這個相當於從mAppInfoList中刪除了一些資料,只是資料的變化,所以使用notifyDataSetChanged()
notifyDataSetChanged();
}else {
//當results.count<=0,此時資料來源就是重新new出來的,說明原始資料來源已經失效了
notifyDataSetInvalidated();
}
}
}
}
下面是我的完整的Aadpter程式碼,希望可以幫助到陷入沉思的你。
public class AppListViewAdapter extends BaseAdapter implements Filterable{
private Context mContext;
//Adapter資料來源
private List<AppInfoBitmap> mAppInfoList;
//過濾相關
/*
* 過濾器上的鎖可以同步複製原始資料
* */
private final Object mLock=new Object();
//物件陣列的備份,當呼叫ArrayFilter的時候初始化和使用。此時,物件陣列只包含已經過濾的資料。
private ArrayList<AppInfoBitmap> mOriginalValues;
private ArrayFilter mFilter;
//帶參構造
public AppListViewAdapter(Context context, List<AppInfoBitmap> appInfoList) {
this.mContext = context;
this.mAppInfoList = appInfoList;
}
@Override
public int getCount() {
return mAppInfoList.size();
}
@Override
public AppInfoBitmap getItem(int i) {
return mAppInfoList.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
//通過ViewHolder優化listView
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder=new ViewHolder();
if (view==null){
view=View.inflate(mContext, R.layout.activity_app_list_item,null);
//獲取佈局控制元件
viewHolder.imageView=view.findViewById(R.id.app_image);
viewHolder.textView=view.findViewById(R.id.app_name);
//設定佈局控制元件
viewHolder.imageView.setImageBitmap(mAppInfoList.get(i).getmBitmap());
viewHolder.textView.setText(mAppInfoList.get(i).getmName());
//設定Tag
view.setTag(viewHolder);
}else {
//獲取Tag
viewHolder=(ViewHolder) view.getTag();
//設定佈局控制元件
viewHolder.imageView.setImageBitmap(mAppInfoList.get(i).getmBitmap());
viewHolder.textView.setText(mAppInfoList.get(i).getmName());
}
//將view檢視返回
return view;
}
public class ViewHolder{
ImageView imageView;
TextView textView;
}
@Override
public Filter getFilter() {
if (mFilter==null){
mFilter=new ArrayFilter();
}
return mFilter;
}
//寫一個ArrayFilter類繼承Filter
public class ArrayFilter extends Filter{
//執行過濾的方法
@Override
protected FilterResults performFiltering(CharSequence charSequence) {
/*
* 一個帶有首字母約束的陣列過濾器,每一項
* 不是以該首字母開頭的都會被移除該list
* */
//過濾的結果
FilterResults results=new FilterResults();
//原始資料為空時,上鎖,同步複製原始資料
if (mOriginalValues==null){
synchronized (mLock){
mOriginalValues=new ArrayList<>(mAppInfoList);
}
}
//當首字母為空時
if (charSequence==null||charSequence.length()==0){
ArrayList<AppInfoBitmap> list;
synchronized (mLock){
//同步複製一個原始備份資料
list=new ArrayList<>(mOriginalValues);
}
//此時返回的results就是原始的資料,不必進行過濾
results.values=list;
results.count=list.size();
}else {
//轉化為小寫
String prefixString=charSequence.toString().toLowerCase() ;
ArrayList<AppInfoBitmap> values;
//同步複製一個原始備份資料
synchronized (mLock){
values=new ArrayList<>(mOriginalValues);
}
final int count=values.size();
final ArrayList<AppInfoBitmap> newValues =new ArrayList<>();
for (int i = 0; i < count; i++) {
//從ArrayList<AppInfoBitmap>中拿到AppInfoBitmap物件
final AppInfoBitmap value=values.get(i);
/*
* 這個地方是,要進行搜尋的關鍵字,將app的name屬性當作關鍵字來進行篩選
* */
final String valueText=value.getmName().toString().toLowerCase();
/*
* 首字元匹配:valueText.startsWith(prefixString)
* 輸入的關鍵字包含在了某個item中,位置不做考慮
* */
if (valueText.startsWith(prefixString)|| valueText.indexOf(prefixString.toString())!=-1){
newValues.add(value);
}
else {
//處理首字元是空格,去掉text中的空格,然後賦值給字串words
final String[] words=valueText.split(" ");
//獲取除去空格的字串的長度
final int wordCount=words.length;
//Start at index 0,in case valueText starts with space(s)
for (int k = 0; k < wordCount; k++) {
if (words[k].startsWith(prefixString)){
//一旦找到匹配的就break,跳出for迴圈
newValues.add(value);
break;
}
}
}
}
//此時的results就是過濾後的List<AppInfoBitmap>
results.values=newValues;
results.count=newValues.size();
}
//將結果返回
return results;
}
//篩選結果
@Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
//此時,Adapter資料來源就是過濾後的esults
mAppInfoList=(List<AppInfoBitmap>) filterResults.values;
/*
* 資料容器變化------->notifyDataSetInValidated
* 容器中資料發生變化------->notifyDataSetChanged
* */
if (filterResults.count>0){
//這個相當於從mAppInfoList中刪除了一些資料,只是資料的變化,所以使用notifyDataSetChanged()
notifyDataSetChanged();
}else {
//當results.count<=0,此時資料來源就是重新new出來的,說明原始資料來源已經失效了
notifyDataSetInvalidated();
}
}
}
}