編輯:關於Android編程
我們的手機通訊錄一般都有這樣的效果,如下圖:
OK,這種效果大家都見得多了,基本上所有的android手機通訊錄都有這樣的效果。那我們今天就來看看這個效果該怎麼實現。
整體上來說,左邊是一個ListView,右邊是一個自定義View,但是左邊的ListView和我們平常使用的ListView還有一點點不同,就是在ListView中我對所有的聯系人進行了分組,那麼這種效果的實現最常見的就是兩種思路:
1.使用ExpandableListView來實現這種分組效果
2.使用普通ListView,在構造Adapter時實現SectionIndexer接口,然後在Adapter中做相應的處理
這兩種方式都不難,都屬於普通控件的使用,那麼這裡我們使用第二種方式來實現,第一種方式的實現方法大家可以自行研究,如果你還不熟悉ExpandableListView的使用,可以參考我的另外兩篇博客:
1.使用ExpandableListView實現一個時光軸
2.android開發之ExpandableListView的使用,實現類似QQ好友列表
OK,這是我們左邊ListView的實現思路,右邊這個東東就是我們今天的主角,這裡我通過自定義一個View來實現,View中的A、B......#這些字符我都通過canvas的drawText方法繪制上去。然後重寫onTouchEvent方法來實現事件監聽。
要實現的效果如上圖所示,但是大家看圖片有些地方可能還不太清楚,所以這裡我再強調一下:
1.左邊的ListView對數據進行分組顯示
2.當左邊ListView滑動的時候,右邊滑動控件中的文字顏色能夠跟隨左邊ListView的滑動自動變化
3.當手指在右邊的滑動控件上滑動時,手指滑動到的地方的文字顏色應當發生變化,同時在整個頁面的正中央有一個TextView顯示手指目前按下的文字
4.當手指按下右邊的滑動控件時,右邊的滑動控件背景變為灰色,手指松開後,右邊的滑動控件又變為透明色
無論多大的工程,我們都要將之分解為一個個細小的功能塊分步來實現,那麼這裡我們就先來看看左邊的ListView的分組的實現,這個效果實現之後,我們再來看看右邊的滑動控件該怎麼實現。
首先我需要在布局文件中添加一個ListView,這個很簡單,和普通的ListView一模一樣,我就不貼代碼了,另外,針對ListView中的數據集,我需要自建一個實體類,該實體類如下:
/** * Created by wangsong on 2016/4/24. */ public class User { private int img; private String username; private String pinyin; private String firstLetter; public User() { } public String getFirstLetter() { return firstLetter; } public void setFirstLetter(String firstLetter) { this.firstLetter = firstLetter; } public int getImg() { return img; } public void setImg(int img) { this.img = img; } public String getPinyin() { return pinyin; } public void setPinyin(String pinyin) { this.pinyin = pinyin; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public User(String firstLetter, int img, String pinyin, String username) { this.firstLetter = firstLetter; this.img = img; this.pinyin = pinyin; this.username = username; } }
private void initData() { list = new ArrayList<>(); String[] allUserNames = getResources().getStringArray(R.array.arrUsernames); for (String allUserName : allUserNames) { User user = new User(); user.setUsername(allUserName); String convert = ChineseToPinyinHelper.getInstance().getPinyin(allUserName).toUpperCase(); user.setPinyin(convert); String substring = convert.substring(0, 1); if (substring.matches("[A-Z]")) { user.setFirstLetter(substring); }else{ user.setFirstLetter("#"); } list.add(user); } Collections.sort(list, new Comparator() { @Override public int compare(User lhs, User rhs) { if (lhs.getFirstLetter().contains("#")) { return 1; } else if (rhs.getFirstLetter().contains("#")) { return -1; }else{ return lhs.getFirstLetter().compareTo(rhs.getFirstLetter()); } } }); }
OK,數據源構造好之後,我還需要對List集合進行一個簡單的排序,那麼這個排序是Java中的操作,我這裡就不再贅述。
構造完數據源之後,接著就該是構造ListView的Adapter了,我們來看看這個怎麼做,先來看看源碼:
/** * Created by wangsong on 2016/4/24. */ public class MyAdapter extends BaseAdapter implements SectionIndexer { private Listlist; private Context context; private LayoutInflater inflater; public MyAdapter(Context context, List list) { this.context = context; this.list = list; inflater = LayoutInflater.from(context); } @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.listview_item, null); holder = new ViewHolder(); holder.showLetter = (TextView) convertView.findViewById(R.id.show_letter); holder.username = (TextView) convertView.findViewById(R.id.username); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } User user = list.get(position); holder.username.setText(user.getUsername()); //獲得當前position是屬於哪個分組 int sectionForPosition = getSectionForPosition(position); //獲得該分組第一項的position int positionForSection = getPositionForSection(sectionForPosition); //查看當前position是不是當前item所在分組的第一個item //如果是,則顯示showLetter,否則隱藏 if (position == positionForSection) { holder.showLetter.setVisibility(View.VISIBLE); holder.showLetter.setText(user.getFirstLetter()); } else { holder.showLetter.setVisibility(View.GONE); } return convertView; } @Override public Object[] getSections() { return new Object[0]; } //傳入一個分組值[A....Z],獲得該分組的第一項的position @Override public int getPositionForSection(int sectionIndex) { for (int i = 0; i < list.size(); i++) { if (list.get(i).getFirstLetter().charAt(0) == sectionIndex) { return i; } } return -1; } //傳入一個position,獲得該position所在的分組 @Override public int getSectionForPosition(int position) { return list.get(position).getFirstLetter().charAt(0); } class ViewHolder { TextView username, showLetter; } }
1.getPositionForSection(int sectionIndex)
這個方法接收一個int類型的參數,該參數實際上就是指我們的分組,我們在這裡傳入分組的值【A.....Z】,然後我們在方法中通過自己的計算,返回該分組中第一個item的position。
2.getSectionForPosition(int position)
這個方法接收一個int類型的參數,該參數實際上就是我們的ListView即將要顯示的item的position,我們通過傳入這個position,可以獲得該position的item所屬的分組,然後再將這個分組的值返回。
說了這麼多,大家可能有疑問了,我為什麼要實現這個接口呢?大家來看看我的item的布局文件:
//獲得當前position是屬於哪個分組 int sectionForPosition = getSectionForPosition(position); //獲得該分組第一項的position int positionForSection = getPositionForSection(sectionForPosition); //查看當前position是不是當前item所在分組的第一個item //如果是,則顯示showLetter,否則隱藏 if (position == positionForSection) { holder.showLetter.setVisibility(View.VISIBLE); holder.showLetter.setText(user.getFirstLetter()); } else { holder.showLetter.setVisibility(View.GONE); }
ListView listView = (ListView) findViewById(R.id.lv); MyAdapter adapter = new MyAdapter(this, list); listView.setAdapter(adapter);
右邊這個東東很明顯是一個自定義View,那我們就一起來看看這個自定義View吧。
首先這個自定義控件繼承自View,繼承自View,需要實現它裡邊的構造方法,關於這三個構造方法的解釋大家可以查看我的另一篇博客android自定義View之鐘表誕生記,這裡對於構造方法我不再贅述。在這個自定義View中,我需要首先聲明5個變量,如下:
//當前手指滑動到的位置 private int choosedPosition = -1; //畫文字的畫筆 private Paint paint; //右邊的所有文字 private String[] letters = new String[]{"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", "#"}; //頁面正中央的TextView,用來顯示手指當前滑動到的位置的文本 private TextView textViewDialog; //接口變量,該接口主要用來實現當手指在右邊的滑動控件上滑動時ListView能夠跟著滾動 private UpdateListView updateListView;
public LetterIndexView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); paint = new Paint(); paint.setAntiAlias(true); paint.setTextSize(24); }
准備工作做完之後,接下來就是onDraw了,代碼如下:
@Override protected void onDraw(Canvas canvas) { int perTextHeight = getHeight() / letters.length; for (int i = 0; i < letters.length; i++) { if (i == choosedPosition) { paint.setColor(Color.RED); } else { paint.setColor(Color.BLACK); } canvas.drawText(letters[i], (getWidth() - paint.measureText(letters[i])) / 2, (i + 1) * perTextHeight, paint); } }
繪制完成之後,就是重寫onTouchEvent了,如下:
@Override public boolean onTouchEvent(MotionEvent event) { int perTextHeight = getHeight() / letters.length; float y = event.getY(); int currentPosition = (int) (y / perTextHeight); String letter = letters[currentPosition]; switch (event.getAction()) { case MotionEvent.ACTION_UP: setBackgroundColor(Color.TRANSPARENT); if (textViewDialog != null) { textViewDialog.setVisibility(View.GONE); } break; default: setBackgroundColor(Color.parseColor("#cccccc")); if (currentPosition > -1 && currentPosition < letters.length) { if (textViewDialog != null) { textViewDialog.setVisibility(View.VISIBLE); textViewDialog.setText(letter); } if (updateListView != null) { updateListView.updateListView(letter); } choosedPosition = currentPosition; } break; } invalidate(); return true; }
public void updateLetterIndexView(int currentChar) { for (int i = 0; i < letters.length; i++) { if (currentChar == letters[i].charAt(0)) { choosedPosition = i; invalidate(); break; } } }
TextView textView = (TextView) findViewById(R.id.show_letter_in_center); final LetterIndexView letterIndexView = (LetterIndexView) findViewById(R.id.letter_index_view); letterIndexView.setTextViewDialog(textView); letterIndexView.setUpdateListView(new LetterIndexView.UpdateListView() { @Override public void updateListView(String currentChar) { int positionForSection = adapter.getPositionForSection(currentChar.charAt(0)); listView.setSelection(positionForSection); } }); listView.setOnScrollListener(new AbsListView.OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { int sectionForPosition = adapter.getSectionForPosition(firstVisibleItem); letterIndexView.updateLetterIndexView(sectionForPosition); } });
以上。
(一).前言:今天我們的項目繼續更新,今天我們主要講解消息總線EventBus的基本使用方法,後面一篇我們會從源碼的角度稍微分析一下實現過程。Fa
1.如何改變item的背景色和按下顏色 listview默認情況下,item的背景色是黑色,在用戶點擊時是黃色的。如果需要修改為自定義的背景顏色,一般情況下有三種
一、問題描述使用百度地圖實現如圖所示應用,首先自動定位當前我起始位置(小圓點位置),並跟隨移動不斷自動定位我的當前位置百度Api不同版本使用會有些差異,本例中加入lib如
最近在寫一個應用,想把設置頁面和應用頁面放在一起,這樣就能實現用戶可以實時看到自己的設置對UI的影響,從而更方便的設置用戶喜歡的界面。想了一段時間,發現用slidingD