Android开发--全国各城市列表并按首字母排序加快速定位

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/smbroe/article/details/43058455

       在很多商业的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<String, Integer> alphaIndexer;
	private ArrayList<CityBean> 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<CityBean> getCityNames() {
		CityDBManager dbManager = new CityDBManager(CityActivity.this);
		dbManager.openDateBase();
		dbManager.closeDatabase();
		SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(CityDBManager.DB_PATH
				+ "/" + CityDBManager.DB_NAME, null);
		ArrayList<CityBean> names = new ArrayList<CityBean>();
		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的布局文件:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_gravity="center"
        android:orientation="vertical" >

        <ListView
            android:id="@+id/city_list"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:divider="@color/lightgray"
            android:dividerHeight="2dp" />
    </LinearLayout>

    <TextView
        android:id="@+id/city_dialog"
        android:layout_width="80.0dip"
        android:layout_height="80.0dip"
        android:layout_gravity="center"
        android:gravity="center"
        android:textColor="@color/myblack"
        android:textSize="30.0sp"
        android:visibility="gone" />

    <com.example.citytest.widget.QuicLocationBar
        android:id="@+id/city_loactionbar"
        android:layout_width="30.0dip"
        android:layout_height="fill_parent"
        android:layout_gravity="right|center"
        android:paddingBottom="2dp" />

</FrameLayout>

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<CityBean> list;
	private HashMap<String, Integer> alphaIndexer;
	private String[] sections;

	public CityAdapter(Context context, List<CityBean> list) {

		this.inflater = LayoutInflater.from(context);
		this.list = list;
		alphaIndexer = new HashMap<String, Integer>();
		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<String, Integer> 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显示的布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/item_city_alpha"
        android:layout_width="fill_parent"
        android:layout_height="25dp"
        android:background="@color/darkgray"
        android:paddingLeft="13dip"
        android:textAppearance="?android:textAppearanceMedium"
        android:textColor="@color/myred" />

    <TextView
        android:id="@+id/item_city_name"
        android:layout_width="wrap_content"
        android:layout_height="35dp"
        android:layout_below="@id/item_city_alpha"
        android:layout_margin="15.0dip"
        android:singleLine="true"
        android:text="name"
        android:textAppearance="?android:textAppearanceMedium"
        android:textColor="#212121" />

</RelativeLayout>
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);
	}

}




展开阅读全文

没有更多推荐了,返回首页