在很多商业的App中都需要进行城市的选择,一般我们会对城市按首字母进行排序,以前的项目中也曾经遇到,今天拿来复习一下。思路如下:
1.首先我们需要一个包含全国各城市的数据库文件或Json,Xml文件都可以,我从网络上搜到了一个数据库.db的文件,该数据库包含了中国各城市的中文名称以及首字母。首先将该数据库文件拷贝到我们的工程目录的res/raw文件夹之下(该文件夹不会被Android编译),当我们访问数据库时首先检查该数据库文件是否存在(一般数据库文件会被Android系统放在/data/data/packagename/databases文件夹之下),如果不存在则将数据库拷贝过来。之后可以按首字母排序查询数据库。
2.对于快速定位的实现:使用QuickLocationBar来进行实现,使用一个HashMap保存首字母和该首字母城市的第一个出现的位置,在OnTouchLetterChangedListener中实现ListView的快速定位。关于QuickLocationBar的实现的具体细节请看我之前的博客:Android开发--根据字母快速定位的侧边栏实现
显示效果
代码如下:
CityActivity类:
package com.example.citytest;
import java.util.ArrayList;
import java.util.HashMap;
import android.app.Activity;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.example.citytest.widget.QuicLocationBar;
import com.example.citytest.widget.QuicLocationBar.OnTouchLetterChangedListener;
public class CityActivity extends Activity {
private ListView mCityLit;
private TextView overlay;
private QuicLocationBar mQuicLocationBar;
private HashMap alphaIndexer;
private ArrayList mCityNames;
@Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_city);
mQuicLocationBar = (QuicLocationBar) findViewById(R.id.city_loactionbar);
mQuicLocationBar
.setOnTouchLitterChangedListener(new LetterListViewListener());
overlay = (TextView) findViewById(R.id.city_dialog);
mCityLit = (ListView) findViewById(R.id.city_list);
mQuicLocationBar.setTextDialog(overlay);
initList();
}
private void initList() {
mCityNames = getCityNames();
CityAdapter adapter = new CityAdapter(CityActivity.this, mCityNames);
mCityLit.setAdapter(adapter);
alphaIndexer = adapter.getCityMap();
mCityLit.setOnItemClickListener(new CityListOnItemClick());
}
private ArrayList getCityNames() {
CityDBManager dbManager = new CityDBManager(CityActivity.this);
dbManager.openDateBase();
dbManager.closeDatabase();
SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(CityDBManager.DB_PATH
+ "/" + CityDBManager.DB_NAME, null);
ArrayList names = new ArrayList();
Cursor cursor = database.rawQuery(
"SELECT * FROM T_City ORDER BY NameSort", null);
if (cursor.moveToFirst()) {
do {
CityBean cityModel = new CityBean();
cityModel.setCityName(cursor.getString(cursor
.getColumnIndex("CityName")));
cityModel.setNameSort(cursor.getString(cursor
.getColumnIndex("NameSort")));
names.add(cityModel);
} while (cursor.moveToNext());
}
database.close();
return names;
}
private class CityListOnItemClick implements OnItemClickListener {
@Override
public void onItemClick(AdapterView> arg0, View arg1, int pos,
long arg3) {
CityBean cityModel = (CityBean) mCityLit.getAdapter()
.getItem(pos);
Toast.makeText(CityActivity.this, cityModel.getCityName(),
Toast.LENGTH_SHORT).show();
}
}
private class LetterListViewListener implements
OnTouchLetterChangedListener {
@Override
public void touchLetterChanged(String s) {
// TODO Auto-generated method stub
if (alphaIndexer.get(s) != null) {
int position = alphaIndexer.get(s);
mCityLit.setSelection(position);
}
}
}
}
Activity的布局文件:
CityAdapter类,ListView适配
package com.example.citytest;
import java.util.HashMap;
import java.util.List;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
/**
* ListView适配
* 提供一个方法来获得 保存首字母和该首字母城市的第一个出现的位置HashMap
* 通过记录前一个首字母与当前首字母是否相同,如果不同加入HashMap中同时显示该字母
* @author acer
*
*/
public class CityAdapter extends BaseAdapter {
private LayoutInflater inflater;
private List list;
private HashMap alphaIndexer;
private String[] sections;
public CityAdapter(Context context, List list) {
this.inflater = LayoutInflater.from(context);
this.list = list;
alphaIndexer = new HashMap();
sections = new String[list.size()];
for (int i = 0; i < list.size(); i++) {
String currentStr = list.get(i).getNameSort();
String previewStr = (i - 1) >= 0 ? list.get(i - 1).getNameSort()
: " ";
if (!previewStr.equals(currentStr)) {//前一个首字母与当前首字母不同时加入HashMap中同时显示该字母
String name = list.get(i).getNameSort();
alphaIndexer.put(name, i);
sections[i] = name;
}
}
}
public HashMap getCityMap(){
return alphaIndexer;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.item_city, null);
holder = new ViewHolder();
holder.alpha = (TextView) convertView
.findViewById(R.id.item_city_alpha);
holder.name = (TextView) convertView
.findViewById(R.id.item_city_name);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.name.setText(list.get(position).getCityName());
String currentStr = list.get(position).getNameSort();
String previewStr = (position - 1) >= 0 ? list.get(position - 1)
.getNameSort() : " ";
if (!previewStr.equals(currentStr)) {
holder.alpha.setVisibility(View.VISIBLE);
holder.alpha.setText(currentStr);
} else {
holder.alpha.setVisibility(View.GONE);
}
return convertView;
}
private class ViewHolder {
TextView alpha;
TextView name;
}
}
CityBean类:
package com.example.citytest;
/**
* 记录每一个城市的中文名称及首字母
* @author acer
*
*/
public class CityBean {
private String name;
private String firstAlpha;
public String getCityName() {
return name;
}
public void setCityName(String cityName) {
name = cityName;
}
public String getNameSort() {
return firstAlpha;
}
public void setNameSort(String nameSort) {
firstAlpha = nameSort;
}
}
ListView中Item显示的布局文件
CityDBManager数据库管理类
package com.example.citytest;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.os.Environment;
/**
* 数据库管理类
* 访问数据库时首先检查该数据库文件是否存在,如果不存在进行拷贝操作
* @author acer
*
*/
public class CityDBManager {
private final int BUFFER_SIZE = 10240;
private static final String PACKAGE_NAME = "com.example.citytest";
public static final String DB_NAME = "china_city_name.db";
public static final String DB_PATH = "/data"
+ Environment.getDataDirectory().getAbsolutePath() + "/"
+ PACKAGE_NAME + "/databases";
private Context mContext;
private SQLiteDatabase database;
public CityDBManager(Context context) {
this.mContext = context;
}
public void openDateBase() {
this.database = this.openDateBase(DB_PATH + "/" + DB_NAME );
}
private SQLiteDatabase openDateBase(String dbFile) {
File file = new File(dbFile);
if (!file.exists()) {
File folder = new File(DB_PATH);
if (!folder.exists()) {
folder.mkdir();
}
InputStream stream = mContext.getResources().openRawResource(
R.raw.china_city_name);
try {
FileOutputStream outputStream = new FileOutputStream(dbFile);
byte[] buffer = new byte[BUFFER_SIZE];
int count = 0;
while ((count = stream.read(buffer)) > 0) {
outputStream.write(buffer, 0, count);
}
outputStream.close();
stream.close();
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(dbFile,
null);
return db;
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return database;
}
public void closeDatabase() {
if (database != null && database.isOpen()) {
this.database.close();
}
}
}
QuickLocationBar快速定位侧边栏
package com.example.citytest.widget;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import com.example.citytest.R;
/**
* 快速定位侧边栏
* 在OnTouchLetterChangedListener中对点击当前的字母做处理
* 需要设置一个TextView来显示当前点击的字母
* @author acer
*
*/
public class QuicLocationBar extends View {
private String characters[] = { "#", "A", "B", "C", "D", "E", "F", "G",
"H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
"U", "V", "W", "X", "Y", "Z" };
private int choose = -1;
private Paint paint = new Paint();
private OnTouchLetterChangedListener mOnTouchLetterChangedListener;
private TextView mTextDialog;
public QuicLocationBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// TODO Auto-generated constructor stub
}
public QuicLocationBar(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
public QuicLocationBar(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public void setOnTouchLitterChangedListener(
OnTouchLetterChangedListener onTouchLetterChangedListener) {
this.mOnTouchLetterChangedListener = onTouchLetterChangedListener;
}
public void setTextDialog(TextView dialog) {
this.mTextDialog = dialog;
}
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
int singleHeight = height / characters.length;
for (int i = 0; i < characters.length; i++) {
// 对paint进行相关的参数设置
paint.setColor(getResources().getColor(R.color.myblack));
paint.setTypeface(Typeface.DEFAULT_BOLD);
paint.setAntiAlias(true);
paint.setTextSize(150*(float) width/320);
if (i == choose) {// choose变量表示当前显示的字符位置,若没有触摸则为-1
paint.setColor(getResources().getColor(R.color.myred));
paint.setFakeBoldText(true);
}
// 计算字符的绘制的位置
float xPos = width / 2 - paint.measureText(characters[i]) / 2;
float yPos = singleHeight * i + singleHeight;
// 在画布上绘制字符
canvas.drawText(characters[i], xPos, yPos, paint);
paint.reset();// 每次绘制完成后不要忘记重制Paint
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
int action = event.getAction();
float y = event.getY();
int c = (int) (y / getHeight() * characters.length);
switch (action) {
case MotionEvent.ACTION_UP:
choose = -1;//
setBackgroundColor(0x0000);
invalidate();
if (mTextDialog != null) {
mTextDialog.setVisibility(View.GONE);
}
break;
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
setBackgroundColor(getResources().getColor(R.color.darkgray));
if (choose != c) {
if (c >= 0 && c < characters.length) {
if (mOnTouchLetterChangedListener != null) {
mOnTouchLetterChangedListener
.touchLetterChanged(characters[c]);
}
if (mTextDialog != null) {
mTextDialog.setText(characters[c]);
mTextDialog.setVisibility(View.VISIBLE);
}
choose = c;
invalidate();
}
}
break;
}
return true;
}
public interface OnTouchLetterChangedListener {
public void touchLetterChanged(String s);
}
}