Android自定義View實(shí)現(xiàn)字母導(dǎo)航欄
很多的Android入門程序猿來(lái)說(shuō)對(duì)于Android自定義View,可能都是比較恐懼的,但是這又是高手進(jìn)階的必經(jīng)之路,所有準(zhǔn)備在自定義View上面花一些功夫,多寫一些文章。
思路分析:
1、自定義View實(shí)現(xiàn)字母導(dǎo)航欄
2、ListView實(shí)現(xiàn)聯(lián)系人列表
3、字母導(dǎo)航欄滑動(dòng)事件處理
4、字母導(dǎo)航欄與中間字母的聯(lián)動(dòng)
5、字母導(dǎo)航欄與ListView的聯(lián)動(dòng)
效果圖:
首先,我們先甩出主布局文件,方便后面代碼的說(shuō)明
<!--?xml version="1.0" encoding="utf-8"?--> <linearlayout android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android"> <edittext android:background="@drawable/search_border" android:drawableleft="@android:drawable/ic_menu_search" android:layout_height="wrap_content" android:layout_width="match_parent" android:padding="8dp"> <relativelayout android:layout_height="match_parent" android:layout_width="match_parent"> <listview android:divider="@null" android:id="@+id/lv" android:layout_height="match_parent" android:layout_width="match_parent"> <textview android:background="#888888" android:gravity="center" android:id="@+id/tv" android:layout_centerinparent="true" android:layout_height="50dp" android:layout_width="50dp" android:textcolor="#000000" android:textsize="18dp" android:visibility="gone"> <com.handsome.tulin.view.navview android:id="@+id/nv" android:layout_alignparentright="true" android:layout_height="match_parent" android:layout_margin="16dp" android:layout_width="20dp"> </com.handsome.tulin.view.navview></textview></listview></relativelayout> </edittext></linearlayout>
步驟一:分析自定義字母導(dǎo)航欄
思路分析:
1、我們?cè)谑褂玫臅r(shí)候把寬設(shè)置為20dp,高設(shè)置為填充父控件,所以這里獲取的寬度為20dp
2、通過(guò)循環(huán),畫出豎直的字母,每畫一次得重新設(shè)置一下顏色,因?yàn)槲覀冃枰粋€(gè)選中的字母顏色和默認(rèn)不一樣
public class NavView extends View { private Paint textPaint = new Paint(); private String[] s = 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", "#"}; //鼠標(biāo)點(diǎn)擊、滑動(dòng)時(shí)選擇的字母 private int choose = -1; //中間的文本 private TextView tv; public NavView(Context context, AttributeSet attrs) { super(context, attrs); } public NavView(Context context) { super(context); } public NavView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } private void initPaint() { textPaint.setTextSize(20); textPaint.setAntiAlias(true); textPaint.setColor(Color.BLACK); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //畫字母 drawText(canvas); } /** * 畫字母 * * @param canvas */ private void drawText(Canvas canvas) { //獲取View的寬高 int width = getWidth(); int height = getHeight(); //獲取每個(gè)字母的高度 int singleHeight = height / s.length; //畫字母 for (int i = 0; i < s.length; i++) { //畫筆默認(rèn)顏色 initPaint(); //高亮字母顏色 if (choose == i) { textPaint.setColor(Color.RED); } //計(jì)算每個(gè)字母的坐標(biāo) float x = (width - textPaint.measureText(s[i])) / 2; float y = (i + 1) * singleHeight; canvas.drawText(s[i], x, y, textPaint); //重置顏色 textPaint.reset(); } } }
步驟二:ListView實(shí)現(xiàn)聯(lián)系人列表
思路分析:
1、在主Activity中,定義一個(gè)數(shù)據(jù)數(shù)組,使用工具類獲取數(shù)組的第一個(gè)字母,使用Collections根據(jù)第一個(gè)字母進(jìn)行排序,由于工具類有點(diǎn)長(zhǎng),就不貼出來(lái)了。
2、創(chuàng)建一個(gè)ListView子布局,創(chuàng)建一個(gè)Adapter進(jìn)行填充。
主布局:
public class MainActivity extends AppCompatActivity { private TextView tv; private ListView lv; private NavView nv; private List<user> list; private UserAdapter adapter; private String[] name = new String[]{ "潘粵明", "戴軍", "薛之謙", "藍(lán)雨", "任泉", "張杰", "秦俊杰", "陳坤", "田亮", "夏雨", "保劍鋒", "陸毅", "喬振宇", "吉杰", "郭敬明", "巫迪文", "歡子", "井柏然", "左小祖咒", "段奕宏", "毛寧", "樊凡", "湯潮", "山野", "陳龍", "侯勇", "俞思遠(yuǎn)", "馮紹峰", "崔健", "杜淳", "張翰", "彭坦", "柏栩栩", "蒲巴甲", "凌瀟肅", "毛方圓", "武藝", "耿樂(lè)", "錢泳辰"}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initData(); } private void initView() { tv = (TextView) findViewById(R.id.tv); lv = (ListView) findViewById(R.id.lv); nv = (NavView) findViewById(R.id.nv); nv.setTextView(tv); } private void initData() { //初始化數(shù)據(jù) list = new ArrayList<>(); for (int i = 0; i < name.length; i++) { list.add(new User(name[i], CharacterUtils.getFirstSpell(name[i]).toUpperCase())); } //將拼音排序 Collections.sort(list, new Comparator<user>() { @Override public int compare(User lhs, User rhs) { return lhs.getFirstCharacter().compareTo(rhs.getFirstCharacter()); } }); //填充ListView adapter = new UserAdapter(this, list); lv.setAdapter(adapter); } }</user></user>
ListView子布局:
<!--?xml version="1.0" encoding="utf-8"?--> <linearlayout android:background="#ffffff" android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android"> <textview android:background="#DBDBDA" android:id="@+id/tv_firstCharacter" android:layout_height="wrap_content" android:layout_width="match_parent" android:padding="8dp" android:text="A" android:textcolor="#000000" android:textsize="14dp"> <textview android:background="#ffffff" android:id="@+id/tv_name" android:layout_height="wrap_content" android:layout_width="match_parent" android:padding="8dp" android:text="張棟梁" android:textcolor="#2196F3" android:textsize="14dp"> </textview></textview></linearlayout>
Adapter:
public class UserAdapter extends BaseAdapter { private List<user> list; private User user; private LayoutInflater mInflater; private Context context; public UserAdapter(Context context, List<user> list) { this.list = list; mInflater = LayoutInflater.from(context); this.context = 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) { if (convertView == null) { convertView = mInflater.inflate(R.layout.adapter_user, null); } ViewHolder holder = getViewHolder(convertView); user = list.get(position); if (position == 0) { //第一個(gè)數(shù)據(jù)要顯示字母和姓名 holder.tv_firstCharacter.setVisibility(View.VISIBLE); holder.tv_firstCharacter.setText(user.getFirstCharacter()); holder.tv_name.setText(user.getUsername()); } else { //其他數(shù)據(jù)判斷是否為同個(gè)字母,這里使用Ascii碼比較大小 if (CharacterUtils.getCnAscii(list.get(position - 1).getFirstCharacter().charAt(0)) < CharacterUtils.getCnAscii(user.getFirstCharacter().charAt(0))) { //后面字母的值大于前面字母的值,需要顯示字母 holder.tv_firstCharacter.setVisibility(View.VISIBLE); holder.tv_firstCharacter.setText(user.getFirstCharacter()); holder.tv_name.setText(user.getUsername()); } else { //后面字母的值等于前面字母的值,不顯示字母 holder.tv_firstCharacter.setVisibility(View.GONE); holder.tv_name.setText(user.getUsername()); } } return convertView; } /** * 獲得控件管理對(duì)象 * * @param view * @return */ private ViewHolder getViewHolder(View view) { ViewHolder holder = (ViewHolder) view.getTag(); if (holder == null) { holder = new ViewHolder(view); view.setTag(holder); } return holder; } /** * 控件管理類 */ private class ViewHolder { private TextView tv_firstCharacter, tv_name; ViewHolder(View view) { tv_firstCharacter = (TextView) view.findViewById(R.id.tv_firstCharacter); tv_name = (TextView) view.findViewById(R.id.tv_name); } } /** * 通過(guò)字符查找位置 * * @param s * @return */ public int getSelectPosition(String s) { for (int i = 0; i < getCount(); i++) { String firChar = list.get(i).getFirstCharacter(); if (firChar.equals(s)) { return i; } } return -1; } }</user></user>
步驟三:字母導(dǎo)航欄滑動(dòng)事件處理、字母導(dǎo)航欄與中間字母的聯(lián)動(dòng)
思路分析:
1、在自定義View中重寫dispatchTouchEvent處理滑動(dòng)事件,最后返回true。
2、在主Activity傳進(jìn)來(lái)一個(gè)TextView,在我們滑動(dòng)的時(shí)候設(shè)置Text,松開(kāi)的時(shí)候消失Text。設(shè)置Text的時(shí)候需要計(jì)算Text的位置,并且滑過(guò)多的話會(huì)出現(xiàn)數(shù)組越界的問(wèn)題,所以我們?cè)诶锩嫣幚頂?shù)組越界問(wèn)題。
3、最后,提供一個(gè)接口,記錄我們滑到的字母,為了后面可以和ListView聯(lián)動(dòng)。
@Override public boolean dispatchTouchEvent(MotionEvent event) { //計(jì)算選中字母 int index = (int) (event.getY() / getHeight() * s.length); //防止腳標(biāo)越界 if (index >= s.length) { index = s.length - 1; } else if (index < 0) { index = 0; } switch (event.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: setBackgroundColor(Color.GRAY); //選中字母高亮 choose = index; //出現(xiàn)中間文字 tv.setVisibility(VISIBLE); tv.setText(s[choose]); //調(diào)用ListView連動(dòng)接口 if (listener != null) { listener.touchCharacterListener(s[choose]); } //重繪 invalidate(); break; default: setBackgroundColor(Color.TRANSPARENT); //取消選中字母高亮 choose = -1; //隱藏中間文字 tv.setVisibility(GONE); //重繪 invalidate(); break; } return true; } public onTouchCharacterListener listener; public interface onTouchCharacterListener { void touchCharacterListener(String s); } public void setListener(onTouchCharacterListener listener) { this.listener = listener; } /** * 傳進(jìn)來(lái)一個(gè)TextView * * @param tv */ public void setTextView(TextView tv) { this.tv = tv; }
步驟四:字母導(dǎo)航欄和ListView的聯(lián)動(dòng)
思路分析:
1、我們已經(jīng)通過(guò)接口傳遞過(guò)去了一個(gè)選擇的字母,和在adapter寫好了根據(jù)字母查詢position的方法,這個(gè)時(shí)候只要主Activity對(duì)自定義View設(shè)置監(jiān)聽(tīng),判斷即可。
//ListView連動(dòng)接口 nv.setListener(new NavView.onTouchCharacterListener() { @Override public void touchCharacterListener(String s) { int position = adapter.getSelectPosition(s); if (position != -1) { lv.setSelection(position); } } });
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
完美解決android 項(xiàng)目jar包沖突的問(wèn)題
這篇文章主要介紹了完美解決android 項(xiàng)目jar包沖突的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03Android動(dòng)畫之小球擬合動(dòng)畫實(shí)例
這篇文章主要介紹了Android動(dòng)畫之小球擬合動(dòng)畫實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-07-07聊聊GridView實(shí)現(xiàn)拖拽排序及數(shù)據(jù)交互的問(wèn)題
這篇文章主要介紹了聊聊GridView實(shí)現(xiàn)拖拽排序及數(shù)據(jù)交互的問(wèn)題,整體實(shí)現(xiàn)思路是通過(guò)在一個(gè)容器里放置兩個(gè)dragview,DragView里面進(jìn)行View的動(dòng)態(tài)交換以及數(shù)據(jù)交換,具體實(shí)現(xiàn)代碼跟隨小編一起看看吧2021-11-11Android Studio實(shí)現(xiàn)簡(jiǎn)單計(jì)算器功能
這篇文章主要為大家詳細(xì)介紹了Android Studio實(shí)現(xiàn)簡(jiǎn)單計(jì)算器功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-03-03Android自定義View實(shí)現(xiàn)搜索框(SearchView)功能
這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)搜索框SearchView功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11Android實(shí)現(xiàn)動(dòng)態(tài)顯示或隱藏密碼輸入框的內(nèi)容
這篇文章主要介紹了Android實(shí)現(xiàn)動(dòng)態(tài)顯示或隱藏密碼輸入框的內(nèi)容,主要通過(guò)設(shè)置EditText的setTransformationMethod()方法來(lái)實(shí)現(xiàn),需要的朋友可以參考下2014-09-09五分了解Android?Progress?Bar進(jìn)度條加載
這篇文章主要為大家介紹了Android?Progress?Bar進(jìn)度條加載的實(shí)現(xiàn)及屬性示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02