Android ListView城市列表,按a-z分組字母索引排序
在上一篇中實現了,先自定義資料來源,使Adapter通過實現SectionIndexer介面給ListView分組,並用Collections.sort對資料list進行排序。
但是Collections.sort並不高效,如果資料來源太多,必然會太耗時,所以這篇使用android.widget.Filterable自動篩選城市。
Filterable定義了一種可過濾的行為:
protected FilterResults performFiltering(CharSequence prefix)方法定義過濾條件
protected void publishResults(CharSequence constraint, FilterResults results)釋出篩選過後得到的資料同時更新Adapter更新。
這篇程式碼是在上一篇的基礎上改造的,就多了SearchCityAdapter(使用了Filterable)和city.db以及一些資料庫操作的類。先看一下原始碼結構目錄(去掉了CharacterParser(漢字轉換成拼音的類)和Collections.sort方法):
上幾張執行截圖效果:
先看SearchCityAdapter:
通過public class SearchCityAdapter extends BaseAdapter implements Filterable { private List<City> mAllCities; private List<City> mResultCities; private LayoutInflater mInflater; private Context mContext; // private String mFilterStr; public SearchCityAdapter(Context context, List<City> allCities) { mContext = context; mAllCities = allCities; mResultCities = new ArrayList<City>(); mInflater = LayoutInflater.from(mContext); } @Override public int getCount() { return mResultCities.size(); } @Override public City getItem(int position) { return mResultCities.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = mInflater.inflate(R.layout.search_city_item, null); } TextView provinceTv = (TextView) convertView .findViewById(R.id.search_province); provinceTv.setText(mResultCities.get(position).getProvince()); TextView cityTv = (TextView) convertView .findViewById(R.id.column_title); cityTv.setText(mResultCities.get(position).getCity()); return convertView; } @Override public Filter getFilter() { Filter filter = new Filter() { protected void publishResults(CharSequence constraint, FilterResults results) { mResultCities = (ArrayList<City>) results.values; if (results.count > 0) { notifyDataSetChanged(); } else { notifyDataSetInvalidated(); } } protected FilterResults performFiltering(CharSequence s) { String str = s.toString().toUpperCase(); // mFilterStr = str; FilterResults results = new FilterResults(); ArrayList<City> cityList = new ArrayList<City>(); if (mAllCities != null && mAllCities.size() != 0) { for (City cb : mAllCities) { // 匹配全屏、首字母、和城市名中文 if (cb.getAllFristPY().indexOf(str) > -1 || cb.getAllPY().indexOf(str) > -1 || cb.getCity().indexOf(str) > -1) { cityList.add(cb); } } } results.values = cityList; results.count = cityList.size(); return results; } }; return filter; } }
<pre name="code" class="java"><pre name="code" class="java"> if (mAllCities != null && mAllCities.size() != 0) { for (City cb : mAllCities) { // 匹配全屏、首字母、和城市名中文 if (cb.getAllFristPY().indexOf(str) > -1 || cb.getAllPY().indexOf(str) > -1 || cb.getCity().indexOf(str) > -1) { cityList.add(cb); } } } results.values = cityList; results.count = cityList.size(); return results;
過濾並把結果回傳給results,在
protected void publishResults(CharSequence constraint,
FilterResults results) {
mResultCities = (ArrayList<City>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
中,如果過濾得到的results非空,則呼叫notifyDataSetChanged方法強制呼叫getView來重新整理每個Item的內容。否則呼叫notifyDataSetInvalidated()會重繪View。重新改造一下activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.example.testbase.ClearEditText
android:id="@+id/et_msg_search"
android:layout_width="match_parent"
android:layout_height="48.0dip"
android:background="@drawable/base_edit_input"
android:drawableLeft="@drawable/icon_msg_search"
android:drawablePadding="10dp"
android:hint="@string/city_search_hint"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:singleLine="true" />
<RelativeLayout
android:id="@+id/layout_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/base_bg"
android:orientation="vertical" >
<RelativeLayout
android:id="@+id/city_content_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top"
android:background="@drawable/content_bg"
android:orientation="horizontal" >
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:cacheColorHint="#00000000"
android:scrollbars="none" />
<TextView
android:id="@+id/tv_mid_letter"
android:layout_width="80.0dip"
android:layout_height="80.0dip"
android:layout_centerInParent="true"
android:layout_gravity="center"
android:background="@drawable/letter_mid_view_background"
android:gravity="center"
android:padding="5dip"
android:textColor="@color/base_actionbar_bg"
android:textSize="35.0dip"
android:visibility="invisible" />
<!-- 載入資料庫時間太長 -->
<include
android:id="@+id/citys_list_load"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_centerInParent="true"
android:layout_gravity="center"
layout="@layout/base_load_empty_layout" />
<com.example.testbase.MyLetterSortView
android:id="@+id/right_letter"
android:layout_width="25dip"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:background="@color/transparent" />
</RelativeLayout>
<FrameLayout
android:id="@+id/search_content_container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="@+id/search_list"
style="@style/base_listview_style"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#00000000"
android:focusableInTouchMode="true"
android:visibility="gone" />
<TextView
android:id="@+id/search_empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10.0dip"
android:drawableTop="@drawable/icon_result"
android:drawablePadding="50dp"
android:text="親,貌似沒有找到額……"
android:visibility="gone" />
</FrameLayout>
</RelativeLayout>
</LinearLayout>
就添加了載入城市列表時的Loading檢視和搜尋無結果的TextView。再這樣設定一下就OK了:
mSearchListView.setEmptyView(findViewById(R.id.search_empty));
listView.setEmptyView(findViewById(R.id.citys_list_load));
最後再來看看LetterSortActivity:
public class LetterSortActivity extends Activity implements OnClickListener {
public static final String TAG = LetterSortActivity.class.getSimpleName();
private Context context = LetterSortActivity.this;
private ClearEditText mClearEditText;
private TextView tv_mid_letter;
private ListView listView;
private MyLetterSortView right_letter;
private ItemBeanAdapter mAdapter;
private List<City> mlist = new ArrayList<City>();
private InputMethodManager inputMethodManager;
private View mCityContainer;
private FrameLayout mSearchContainer;
private ListView mSearchListView;
private SearchCityAdapter mSearchCityAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
initView();
setLinstener();
initData();
fillData();
}
protected void initData() {
getDataCity();
mAdapter = new ItemBeanAdapter(this, mlist);
listView.setEmptyView(findViewById(R.id.citys_list_load));
listView.setAdapter(mAdapter);
}
protected void initView() {
inputMethodManager = (InputMethodManager) this
.getSystemService(Context.INPUT_METHOD_SERVICE);
listView = (ListView) findViewById(R.id.list);
mClearEditText = (ClearEditText) findViewById(R.id.et_msg_search);
// 這裡設定中間字母
right_letter = (MyLetterSortView) findViewById(R.id.right_letter);
tv_mid_letter = (TextView) findViewById(R.id.tv_mid_letter);
right_letter.setTextView(tv_mid_letter);
//搜尋
mCityContainer = findViewById(R.id.city_content_container);
mSearchContainer = (FrameLayout) this.findViewById(R.id.search_content_container);
mSearchListView = (ListView) findViewById(R.id.search_list);
mSearchListView.setEmptyView(findViewById(R.id.search_empty));
mSearchContainer.setVisibility(View.GONE);
}
protected void setLinstener() {
// tv_reget_pwd.setOnClickListener(this);
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
T.showShort(getApplicationContext(),
((City) mAdapter.getItem(position)).toString());
}
});
listView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// 隱藏軟鍵盤
if (getWindow().getAttributes().softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) {
if (getCurrentFocus() != null)
inputMethodManager.hideSoftInputFromWindow(
getCurrentFocus().getWindowToken(),
InputMethodManager.HIDE_NOT_ALWAYS);
}
return false;
}
});
// 設定右側觸控監聽
right_letter
.setOnTouchingLetterChangedListener(new OnTouchingLetterChangedListener() {
@Override
public void onTouchingLetterChanged(String s) {
// 該字母首次出現的位置
int position = mAdapter.getPositionForSection(s
.charAt(0));
if (position != -1) {
listView.setSelection(position);
}
}
});
// 根據輸入框輸入值的改變來過濾搜尋
mClearEditText.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
// 當輸入框裡面的值為空,更新為原來的列表,否則為過濾資料列表
filterData2(s.toString());
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
@Override
public void afterTextChanged(Editable s) {
}
});
mSearchListView
.setOnItemClickListener(new android.widget.AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// TODO Auto-generated method stub
// L.i(mSearchCityAdapter.getItem(position).toString());
T.showLong(getApplicationContext(), mSearchCityAdapter
.getItem(position).toString());
}
});
}
protected void fillData() {
// TODO Auto-generated method stub
}
private void getDataCity() {
new Thread(new Runnable() {
@Override
public void run() {
MyCityDBHelper myCityDBHelper = new MyCityDBHelper(context);
mlist = myCityDBHelper.getCityDB().getAllCity();
mHandler.sendEmptyMessage(0);
}
}).start();
// 對list進行排序
// Collections.sort(mlist, new PinyinComparator() {
// });
}
private void filterData2(String filterStr) {
mSearchCityAdapter = new SearchCityAdapter(LetterSortActivity.this,
mlist);
mSearchListView.setAdapter(mSearchCityAdapter);
mSearchListView.setTextFilterEnabled(true);
if (mlist.size() < 1 || TextUtils.isEmpty(filterStr)) {
mCityContainer.setVisibility(View.VISIBLE);
mSearchContainer.setVisibility(View.INVISIBLE);
} else {
mCityContainer.setVisibility(View.INVISIBLE);
mSearchContainer.setVisibility(View.VISIBLE);
mSearchCityAdapter.getFilter().filter(filterStr);
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
//
default:
break;
}
}
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 0:
mAdapter.updateListView(mlist);
break;
default:
break;
}
}
};
}
和上一篇中的程式碼大同小異。就改了兩個地方,一個是初始化資料,上一篇中是自定義的隨機字串,並使用Collections.sort對list進行排序。這一篇中使用了直接從city.db中獲取資料,因為使用的是Cursor c = db.rawQuery("SELECT * from " + CITY_TABLE_NAME+" order by firstpy", null);所以不需要排序。
另一個修改的地方就是filterData()方法了,這裡主要是mSearchCityAdapter.getFilter().filter(filterStr);把過濾條件交給它就OK了。
當然,你仍然可以像上一篇中那樣先篩選資料,再使用Collections.sort排序,最後更新ListView。程式碼如下:
private void filterData(String filterStr) {
List<City> filterDateList = new ArrayList<City>();
if (TextUtils.isEmpty(filterStr)) {
mAdapter.updateListView(mlist);
} else {
filterDateList.clear();
for (City city : mlist) {
String name = city.getCity();
if (name != null) {
if (name.indexOf(filterStr.toString()) != -1
|| CharacterParser.getInstance().getSelling(name)
.startsWith(filterStr.toString())) {
filterDateList.add(city);
}
}
}
Collections.sort(filterDateList, new PinyinComparator() {
});
mAdapter.updateListView(filterDateList);
}
}
這樣的話,你會發現搜尋城市的時候有時會有點遲鈍,這是因為資料有點多……
你還可以這樣嘗試:
查詢資料庫的時候Cursor c = db.rawQuery("SELECT * from " + CITY_TABLE_NAME, null);
不使用order by 。然後再通過Collections.sort排序,哇咔咔,要好幾秒啊才能把城市資料列表加載出來啊,沒辦法,資料有點多。
就寫到這裡吧!