Android仿微信聯(lián)系人列表字母?jìng)?cè)滑控件
仿微信聯(lián)系人列表字母?jìng)?cè)滑控件, 側(cè)滑控件參考了以下博客:
Android實(shí)現(xiàn)ListView的A-Z字母排序和過(guò)濾搜索功能
首先分析一下字母?jìng)?cè)滑控件應(yīng)該如何實(shí)現(xiàn),根據(jù)側(cè)滑控件的高度和字母的數(shù)量來(lái)平均計(jì)算每個(gè)字母應(yīng)該占據(jù)的高度。
在View的onDraw()方法下繪制每一個(gè)字母
protected void onDraw(Canvas canvas) { super.onDraw(canvas); int height = getHeight();// 獲取對(duì)應(yīng)高度 int width = getWidth(); // 獲取對(duì)應(yīng)寬度 int singleHeight = height / getData().size();// 獲取每一個(gè)字母的高度 for (int i = 0; i < getData().size(); i++) { mPaint.setColor(getLetterColor());//繪制字母的顏色 mPaint.setTypeface(Typeface.DEFAULT); mPaint.setAntiAlias(true); mPaint.setTextSize(singleHeight); // 如果是選中的狀態(tài) if (i == mPosition) { mPaint.setColor(getLetterPressedColor()); mPaint.setFakeBoldText(true); } // x坐標(biāo)等于總體寬度中間的位置減去字符串寬度的一半. float xPos = width / 2 - mPaint.measureText(getData().get(i)) / 2; float yPos = singleHeight * i + singleHeight; canvas.drawText(getData().get(i), xPos, yPos, mPaint); mPaint.reset();// 重置畫(huà)筆 } }
然后再看一下觸控事件的攔截處理
@Override public boolean dispatchTouchEvent(MotionEvent event) { final int action = event.getAction(); final float y = event.getY();// 點(diǎn)擊y坐標(biāo) final int lastPosition = mPosition;//記錄上一次選中字母的位置 final int position = (int) (y / getHeight() * getData().size());// 點(diǎn)擊y坐標(biāo)所占總高度的比例*b數(shù)組的長(zhǎng)度就等于點(diǎn)擊b中的個(gè)數(shù). switch (action) { //當(dāng)手指離開(kāi) case MotionEvent.ACTION_UP: //設(shè)置側(cè)滑控件的背景色 setBackgroundColor(getBackgroundNormalColor()); mPosition = -1; invalidate(); if (getOnTouchLetterListener() != null) { //回調(diào)事件,告知當(dāng)前手指已經(jīng)離開(kāi)當(dāng)前區(qū)域 getOnTouchLetterListener().onTouchOutside(); } break; default: //更改當(dāng)字母為選中狀態(tài)時(shí)控件的背景色 setBackgroundColor(getBackgroundPressedColor()); //如果選中字母的位置不等于上一次選中的位置 if (lastPosition != position) { if (position >= 0 && position < getData().size()) { if (getOnTouchLetterListener() != null) { //回調(diào)事件,返回當(dāng)前選中的字母 getOnTouchLetterListener().onTouchLetter(getData().get(position)); } mPosition = position; invalidate(); } } break; } return true; } public interface OnTouchLetterListener { /** * 當(dāng)接觸到某個(gè)key的時(shí)候會(huì)調(diào)用; * @param s */ void onTouchLetter(String s); /** * 當(dāng)離開(kāi)控件可觸摸區(qū)域時(shí)會(huì)調(diào)用; */ void onTouchOutside(); }
側(cè)滑控件完成后, 再分析一下分組界面是怎么實(shí)現(xiàn)的,不同分組由不同的字母作為標(biāo)題,用ListView就可以實(shí)現(xiàn),ListView里使用的Adapter里面有一個(gè)方法getItemViewType()方法用于區(qū)分返回多種類型的View,這里我們就兩種, 一個(gè)是標(biāo)題,一個(gè)是聯(lián)系人信息;頂部里那些新的朋友、群聊等可以用ListView的addHeaderView()實(shí)現(xiàn)。但是用最SDK自帶的BaseAdapter實(shí)現(xiàn)分組的話也不方便,實(shí)際上我們可以進(jìn)一步包裝;
首先看一下最基本的Adapter封裝:
public abstract class SimpleAdapter<T> extends BaseAdapter { protected Context mContext; protected List<T> mData; public SimpleAdapter(){} public SimpleAdapter(Context context, List<T> data){ init(context, data); } public void init(Context context, List<T> data){ this.mContext = context; this.mData = data; } @Override public int getCount() { return mData.size(); } @Override public T getItem(int position) { if(checkPositionIsOutOfRange(position)){ return null; } return mData.get(position); } @Override public long getItemId(int position) { return position; } @Override public abstract View getView(int position, View convertView, ViewGroup parent); public void refresh(List<T> data){ if(data == null){ this.mData.clear(); }else{ this.mData = data; } notifyDataSetChanged(); } public boolean checkPositionIsOutOfRange(int position){ if(0 <= position && position < mData.size()){ return false; } return true; } public Context getContext(){ return mContext; } public List<T> getData(){ return mData; } }
這個(gè)SimpleAdapter實(shí)現(xiàn)了數(shù)據(jù)基于List的最基本方法的實(shí)現(xiàn),使得每次繼承BaseAdapter不用再實(shí)現(xiàn)一些基本的方法,接下來(lái)再看一下用于更好實(shí)現(xiàn)分組的Adapter的進(jìn)一步封裝:
public abstract class SortAdapter<K extends SortKey, V, VH_G extends ViewHolder, VH_C extends ViewHolder> extends SimpleAdapter<Object> { public final static int VIEW_TYPE_GROUP = 0; public final static int VIEW_TYPE_CHILD = 1; private HashMap<SortKey, Integer> mKeyIndex = new HashMap<>(); public SortAdapter(Context context, Map<K, List<V>> map) { init(context, convertMapToList(map)); } public SortAdapter(Context context, List<Object> list) { init(context, list); } /** * 轉(zhuǎn)換分組數(shù)據(jù)為L(zhǎng)ist,并且更新鍵值的索引 * @param map * @return */ public List<Object> convertMapToList(Map<K, List<V>> map) { List<Object> mData = new ArrayList<>(); mKeyIndex.clear(); for (Map.Entry<K, List<V>> entry : map.entrySet()) { mData.add(entry.getKey()); mKeyIndex.put(entry.getKey(), mData.size() - 1); for (V v : entry.getValue()) { mData.add(v); } } return mData; } public void refresh(Map<K, List<V>> map) { super.refresh(convertMapToList(map)); } @Override public void refresh(List<Object> data) { super.refresh(data); mKeyIndex.clear(); } /** * 得到鍵值的索引值 * @param k * @return */ public int getKeyIndex(K k){ Integer integer = mKeyIndex.get(k); if(integer == null){ return getKeyIndexFromList(k); } return integer; } public int getKeyIndexFromList(K k){ for(int i = 0; i < getCount(); i++){ Object obj = getItem(i); if(obj != null && obj instanceof SortKey){ if(obj.equals(k)){ mKeyIndex.put(k, i); return i; } } } return -1; } @Override public int getItemViewType(int position) { Object obj = getItem(position); if (obj instanceof SortKey) { return VIEW_TYPE_GROUP; } return VIEW_TYPE_CHILD; } @Override public int getViewTypeCount() { return 2; } @Override public int getCount() { return mData.size(); } @Override public Object getItem(int position) { if (0 <= position && position < mData.size()) { return mData.get(position); } return null; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { int viewType = getItemViewType(position); View view = null; Object obj = getItem(position); switch (viewType) { case VIEW_TYPE_GROUP: view = getGroupView((K)obj, position, convertView, parent); break; case VIEW_TYPE_CHILD: view = getChildView((V)obj, position, convertView, parent); break; } return view; } public View getGroupView(K key, int position, View convertView, ViewGroup parent){ VH_G vh; if(convertView == null){ convertView = LayoutInflater.from(mContext).inflate(getGroupLayoutId(), null); vh = onCreateGroupViewHolder(convertView, parent); convertView.setTag(vh); }else{ vh = (VH_G)convertView.getTag(); } onBindGroupViewHolder(vh, key, position); return convertView; } public View getChildView(V value, int position, View convertView, ViewGroup parent){ VH_C vh; if(convertView == null){ convertView = LayoutInflater.from(mContext).inflate(getChildLayoutId(), null); vh = onCreateChildViewHolder(convertView, parent); convertView.setTag(vh); }else{ vh = (VH_C)convertView.getTag(); } onBindChildViewHolder(vh, value, position); return convertView; } public abstract @LayoutRes int getGroupLayoutId(); public abstract VH_G onCreateGroupViewHolder(View convertView, ViewGroup parent); public abstract void onBindGroupViewHolder(VH_G vh, K key, int position); public abstract @LayoutRes int getChildLayoutId(); public abstract VH_C onCreateChildViewHolder(View convertView, ViewGroup parent); public abstract void onBindChildViewHolder(VH_C vh, V value, int position); public interface SortKey { } public static class ViewHolder{ public View mParent; public ViewHolder(View parent){ mParent = parent; } public View findViewById(@IdRes int id){ return mParent.findViewById(id); } }
本項(xiàng)目Github地址(基于AndroidStudio構(gòu)建):
https://github.com/yuhengye/LetterSort
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android仿微信單擊拍照長(zhǎng)按錄像功能實(shí)例代碼
這篇文章主要介紹了Android仿微信單擊拍照長(zhǎng)按錄像功能實(shí)例代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-04-04Android自定義View實(shí)現(xiàn)圓形環(huán)繞效果
這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)圓形環(huán)繞效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01Android獲取和讀取短信驗(yàn)證碼的實(shí)現(xiàn)方法
這篇文章主要介紹了Android獲取和讀取短信驗(yàn)證碼的實(shí)現(xiàn)方法,文章內(nèi)容介紹了如何讀取剛收到的短信的相關(guān)內(nèi)容,以及Android獲取短信驗(yàn)證碼的方法,感興趣的小伙伴們可以參考一下2016-03-03Flutter實(shí)現(xiàn)不同縮放動(dòng)畫(huà)效果詳解
這篇文章主要為大家詳細(xì)介紹了Flutter利用不同組件(ScaleTransition、SizeTransition、AnimatedSize和AnimatedBuilder)實(shí)現(xiàn)不同縮放動(dòng)畫(huà)效果,感興趣的可以動(dòng)手嘗試一下2022-06-06詳解Android開(kāi)啟OTG功能/USB?Host?API功能
這篇文章主要介紹了Android開(kāi)啟OTG功能/USB?Host?API功能,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07