Android 自定義View實現城市選擇列表
阿新 • • 發佈:2019-01-01
使用自定義View的方法,實現一個城市選擇列表
手指滑動時
自定義View實現側邊欄,並提供回撥介面
/**
* Created by shixi_tianrui1 on 16-9-18.
* 城市選擇列表側邊欄
*/
public class LetterSideBar extends View {
// 字母列表
private List<String> mLetters = new ArrayList<>();
private OnTouchLetterListener mOnTouchLetterListener;
private TextView mOverLayTextView; // 顯示的字母
private int mWidth;
private int mHeight;
private int mEachLetterHeight;
private int mEachLetterWidth;
private Paint mPaint;
private boolean maskShown; // 是否顯示背景
private int mSelectedIndex = -1; // 已選擇的字母
public LetterSideBar(Context context) {
this (context, null);
}
public LetterSideBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LetterSideBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
// for test
for (int i = 0; i < 26; i++) {
char letter = (char) ('A' + i);
Log.v("LOG", letter + "");
mLetters.add(String.valueOf(letter));
}
}
/**
* 測量並繪製所有的大寫字母
*
* @param canvas 系統畫布
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mWidth = getWidth();
mHeight = getHeight();
mPaint.setAntiAlias(true); // 抗鋸齒
mPaint.setTextSize(getResources().getDimension(R.dimen.side_bar_letter));
if (mLetters.size() > 0) {
mEachLetterHeight = mHeight / mLetters.size();
} else {
mEachLetterHeight = mHeight;
}
// 繪製所有顯示的字母
mEachLetterWidth = (int) (mWidth / 2 - mPaint.measureText(mLetters.get(0)) / 2);
// Log.v("LOG", "eachLetterWidth:" + mEachLetterWidth);
for (int i = 0; i < mLetters.size(); i++) {
mPaint.setColor(Color.RED);
if (mSelectedIndex == i && mSelectedIndex != -1) {
mPaint.setColor(Color.CYAN); // 設定選中時的顏色
}
canvas.drawText(mLetters.get(i), mEachLetterWidth, i * mEachLetterHeight + mEachLetterHeight, mPaint);
}
}
/**
* 處理觸控事件
*/
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
int action = event.getAction();
float y = event.getY();
// 獲取點選的字母
int letterIndex = (int) ((y / mHeight) * mLetters.size());
switch (action) {
// 選中字母高亮
case MotionEvent.ACTION_DOWN:
L.d("Letter is : " + mLetters.get(letterIndex));
selectedLetter(letterIndex);
break;
// 在字母範圍內,仍視為選中字母
case MotionEvent.ACTION_MOVE:
selectedLetter(letterIndex);
break;
case MotionEvent.ACTION_UP:
mSelectedIndex = -1;
invalidate();
hideOverLayText();
break;
}
return true;
}
public void setLetterList(List<String> list) {
this.mLetters = list;
}
public void setOnTouchLetterListener(OnTouchLetterListener onTouchLetterListener) {
this.mOnTouchLetterListener = onTouchLetterListener;
}
/**
* 通過此介面設定ListView的position
*/
public interface OnTouchLetterListener {
void onLetterSelected(String letter);
}
/**
* 高亮顯示選中的字母,並顯示mask
*/
private void selectedLetter(int letterIndex) {
if (letterIndex >= 0 && letterIndex < mLetters.size()) {
// 顯示遮罩
maskShown = true;
if (mOnTouchLetterListener != null) {
mOnTouchLetterListener.onLetterSelected(mLetters.get(letterIndex));
}
mSelectedIndex = letterIndex;
// 通知重繪
invalidate();
// 顯示字母
showOverLayText();
}
}
/**
* 顯示選擇的字母
*/
public void showOverLayText() {
if (mOverLayTextView != null) {
mOverLayTextView.setVisibility(View.VISIBLE);
mOverLayTextView.setText(mLetters.get(mSelectedIndex));
}
}
public void hideOverLayText() {
if (mOverLayTextView != null) {
mOverLayTextView.setVisibility(View.GONE);
}
}
public TextView getOverLayTextView() {
return mOverLayTextView;
}
public void setOverLayTextView(TextView overLayTextView) {
this.mOverLayTextView = overLayTextView;
}
}
CityAdapter 給ListView新增城市資料
/**
* Created by shixi_tianrui1 on 16-9-27.
*/
public class CityAdapter extends BaseAdapter {
// ListView Type
private static final int TYPE_CITY_ITEM = 1;
private static final int TYPE_SEARCH_VIEW = 0;
private static final int TYPE_VIEW_COUNT = 2; // 所有的View型別
private Context mContext;
private List<CityBean> mCities;
private HashMap<CityBean, Integer> mLetterPos = new LinkedHashMap<>();
public CityAdapter(Context context, List<CityBean> cities) {
mContext = context;
mCities = cities;
mCities.add(0, new CityBean()); // 新增一個空的CityBean
// record city's pinyin bound
mLetterPos.put(cities.get(0), 1);
for (int i = 1; i < mCities.size(); i++) {
CityBean prev = mCities.get(i - 1);
CityBean cur = mCities.get(i);
if (!TextUtils.equals(Util.getFirstLetter(prev.getPinyin())
, Util.getFirstLetter(cur.getPinyin()))) {
mLetterPos.put(cur, i);
}
}
L.d(mLetterPos.toString());
}
@Override
public int getCount() {
return mCities.size();
}
@Override
public Object getItem(int i) {
return mCities.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View convertView, ViewGroup viewGroup) {
int viewType = getItemViewType(i);
switch (viewType) {
case TYPE_SEARCH_VIEW: // 搜尋框
convertView = LayoutInflater.from(mContext).inflate(R.layout.item_search, viewGroup, false);
break;
case TYPE_CITY_ITEM: // 城市Item
ViewHolder holder;
if (convertView == null || convertView.getTag() == null) {
holder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(mContext);
convertView = inflater.inflate(R.layout.item_city, viewGroup, false);
holder.mTvCity = (TextView) convertView.findViewById(R.id.id_tv_city_name);
holder.mTvLetter = (TextView) convertView.findViewById(R.id.id_tv_letter);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
CityBean curCity = mCities.get(i);
if (mLetterPos.containsKey(curCity)) {
holder.mTvLetter.setVisibility(View.VISIBLE);
String letter = Util.getFirstLetter(curCity.getPinyin());
if (!TextUtils.isEmpty(letter)) {
holder.mTvLetter.setText(letter.toUpperCase());
}
} else {
holder.mTvLetter.setVisibility(View.GONE);
}
holder.mTvCity.setText(curCity.getName());
break;
}
return convertView;
}
@Override
public int getItemViewType(int position) {
// L.d("getItemViewType : " + position);
return position >= TYPE_VIEW_COUNT - 1 ? TYPE_CITY_ITEM : TYPE_SEARCH_VIEW;
}
private class ViewHolder {
private TextView mTvLetter;
private TextView mTvCity;
}
public int getPosition(String letter) {
int position = -1;
Set<CityBean> set = mLetterPos.keySet();
Iterator<CityBean> it = set.iterator();
CityBean city = null;
while (it.hasNext()) {
city = it.next();
if (TextUtils.equals(city.getFirstLetter(), letter.toLowerCase())) {
return mLetterPos.get(city);
}
}
return -1;
}
}
Controller層處理回撥
/**
* Created by shixi_tianrui1 on 16-9-25.
*/
public class CityPickController implements LetterSideBar.OnTouchLetterListener {
private View mRootView;
private TextView mTvMask;
private LetterSideBar mLsSidebar;
private ListView mLvCityList;
private Context mContext;
private CityAdapter mAdapter;
private List<CityBean> mCities = new ArrayList<>();
public CityPickController(Context context, View root) {
mRootView = root;
mContext = context;
initView();
mCities = DBManager.getInstance(mContext).getAllCities();
mAdapter = new CityAdapter(mContext, mCities);
mLvCityList.setAdapter(mAdapter);
}
private void initView() {
mTvMask = (TextView) mRootView.findViewById(R.id.id_tv_mask);
mLsSidebar = (LetterSideBar) mRootView.findViewById(R.id.id_ls_sidebar);
mLvCityList = (ListView) mRootView.findViewById(R.id.id_lv_citys);
mLsSidebar.setOverLayTextView(mTvMask);
mLsSidebar.setOnTouchLetterListener(this);
}
/**
* 處理選擇字母時的回撥
*
* @param letter
*/
@Override
public void onLetterSelected(String letter) {
int position = mAdapter.getPosition(letter);
if (position != -1)
mLvCityList.setSelection(position);
}
}
MainActivity
public class MainActivity extends AppCompatActivity {
private CityPickController mController;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mController = new CityPickController(this, findViewById(android.R.id.content));
}
}