1. 程式人生 > >Android 自定義View實現城市選擇列表

Android 自定義View實現城市選擇列表

使用自定義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));
    }

}