Android 自定義View實現仿通訊錄,實現A-Z字母檢索
前言
此篇文章利用StickyListHeadersListView和自定義view的方式實現所需效果,所以此篇文章的重點有兩個方面,一是介紹StickyListHeadersListView的使用,二則為介紹字母檢索的實現方式。其實技術含量不是很高,就算是記錄一下吧
實現思路
本來字母檢索打算用listview展示,但是網上查了一通資料後發現基本上都是使用的自定義view展示,想也對,字母檢索會被頻繁的重新整理,如果用listview的話,感覺太重了。
效果圖

靜態頁.png
實現功能點
1.粘性顯示
2.字母欄顯示的只是主資料中含有的字母項
3.主資料滑動,字母欄跟著滑到相應的字母
4.點選字母欄定位到主資料的那一字母族的第一個資料
實現步驟
1.使用StickyListHeadersListView呈現基本資料
mListView = (StickyListHeadersListView) findViewById(R.id.id); mAdapter = new Adapter(this, datas,letterdata); mListView.setAdapter(mAdapter);
2.ListView的adapter展示
public class Adapter extends BaseAdapter implements StickyListHeadersAdapter { private List<Bean> countries; private LayoutInflater inflater; private String chars;//字母欄中所有字母的字串,為了拿到字母的位置 public AreaInfoAdapter(Context context, List<Bean> countries, List<String> letterdata) { inflater = LayoutInflater.from(context); this.countries = countries; chars = ListToString(letterdata); } @Override public int getCount() { return countries.size(); } @Override public Object getItem(int position) { return countries.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) { holder = new ViewHolder(); convertView = inflater.inflate(R.layout.layout, parent, false); ...findViewById convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } ...對控制元件的賦值 return convertView; } @Override public View getHeaderView(int position, View convertView, ViewGroup parent) { HeaderViewHolder holder; if (convertView == null) { holder = new HeaderViewHolder(); convertView = inflater.inflate(R.layout.item_layout, parent, false); ...findViewById convertView.setTag(holder); } else { holder = (HeaderViewHolder) convertView.getTag(); } ...對控制元件的賦值 return convertView; } //返回header的唯一標識 @Override public long getHeaderId(int position) { //拿到集合中的字母值 String charname = countries.get(position).getChar(); //拿到字母在字母表中的位置 int index = chars.indexOf(charname); return index; } class HeaderViewHolder { TextView charName; } class ViewHolder { TextView name; TextView code; } //獲得集合中字母為letter的一族中第一個資料的position public int getSelectPosition(String letter) { for (int i = 0; i < countries.size(); i++) { if (countries.get(i).getChar().equals(letter)) { return i; } } return 0; } //獲得具體位置的字母,以及字母在顯示字母表中的位置 public int getSectionForPosition(int position) { String charname = countries.get(position).getChar(); return chars.indexOf(charname); } //將集合轉變為字串 public String ListToString(List<String> list) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < list.size(); i++) { sb.append(list.get(i)); } return sb.toString(); } }
3.自定義字母檢索
public class LetterView extends View { //當前手指滑動到的位置 private int choosedPosition = -1; //畫文字的畫筆 private Paint paint; //右邊的所有文字 private List<String> letters; //讓資訊滑到指定位置 private UpdateListView updateListView; //單個字母的高度 private float perTextHeight; //字母的字型大小 private float letterSize; //頁面正中央的TextView,用來顯示手指當前滑動到的位置的文字 //private TextView textViewDialog; public LetterIndexView(Context context) { this(context, null); } public LetterIndexView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public LetterIndexView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.Letter, defStyleAttr, 0); //字母的字型大小 letterSize = a.getDimension(R.styleable.Letter_letter_size, DisplayUtils.sp2px(getContext(), 10.0f)); //每個字母的高 perTextHeight = a.getDimension(R.styleable.Letter_letter_height, DisplayUtils.dp2px(getContext(), 24.0f)); a.recycle(); init(); } public void init() { paint = new Paint(); paint.setAntiAlias(true); paint.setTextSize(letterSize); paint.setTypeface(Typeface.DEFAULT_BOLD); } //測量view的大小,讓其有多大顯示多大 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec);//獲取寬的模式 int heightMode = MeasureSpec.getMode(heightMeasureSpec); //獲取高的模式 int widthSize = MeasureSpec.getSize(widthMeasureSpec);//獲取寬的尺寸 int heightSize = MeasureSpec.getSize(heightMeasureSpec); //獲取高的尺寸 int width = 0; int height; if (widthMode == MeasureSpec.EXACTLY) { //如果match_parent或者具體的值,直接賦值 width = widthSize; } //高度跟寬度處理方式一樣 if (heightMode == MeasureSpec.EXACTLY) { height = heightSize; } else { float textHeight = perTextHeight; height = (int) (getPaddingTop() + textHeight *( letters.size()+1) + getPaddingBottom()); } //儲存測量寬度和測量高度 setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { for (int i = 0; i < letters.size(); i++) { if (i == choosedPosition) { paint.setColor(Color.parseColor("#AD8748")); } else { paint.setColor(Color.parseColor("#adadad")); } //DisplayUtils.dp2px(getContext(), 3.0f) 這段話的含義是 我為了讓字母好點選,手動把字母檢索的欄的寬度增加了,所以畫的時候就不可以居中畫了,增加了一個偏移量 canvas.drawText(letters.get(i), (getWidth() - paint.measureText(letters.get(i))) / 2 + DisplayUtils.dp2px(getContext(), 3.0f), (i + 1) * perTextHeight, paint); } } //根據點選的y值(y的值是根據你自己定義的view的原點為起點的)判斷點的是第幾項 @Override public boolean onTouchEvent(MotionEvent event) { float y = event.getY(); int currentPosition = (int) (y / perTextHeight); if (currentPosition < 0) { currentPosition = 0; } if (currentPosition >= letters.size()) { return true; } String letter = letters.get(currentPosition); switch (event.getAction()) { case MotionEvent.ACTION_UP: //if (textViewDialog != null) { //textViewDialog.setVisibility(View.GONE); //} break; default: //setBackgroundColor(Color.parseColor("#cccccc")); //if (textViewDialog != null) { //textViewDialog.setVisibility(View.VISIBLE); //textViewDialog.setText(letter); //} if (updateListView != null) { updateListView.updateListView(letter); } choosedPosition = currentPosition; break; } invalidate(); return true; } //主ListView呼叫重回letterView public void updateLetterIndexView(int currentChar) { if (currentChar >= 0 && currentChar < letters.size()) { choosedPosition = currentChar; invalidate(); } } public void setUpdateListView(UpdateListView mUpdateListView) { this.updateListView = mUpdateListView; } public void setData(List<String> letters) { this.letters = letters; } //控制主ListView資料滑動到指定的位置 public interface UpdateListView { void updateListView(String letter); } }
4.互動
letterList.setUpdateListView(new LetterIndexView.UpdateListView() { @Override public void updateListView(String currentChar) { int positionForSection = mAdapter.getSelectPosition(currentChar); mListView.setSelection(positionForSection); } }); mListView.setOnStickyHeaderChangedListener(new StickyListHeadersListView.OnStickyHeaderChangedListener() { @Override public void onStickyHeaderChanged(StickyListHeadersListView l, View header, int itemPosition, long headerId) { int sectionForPosition = mAdapter.getSectionForPosition(itemPosition); letterList.updateLetterIndexView(sectionForPosition); } });
資料
android自定義View之仿通訊錄側邊欄滑動,實現A-Z字母檢索
喵印~~~