Android自定義View實(shí)現(xiàn)字母導(dǎo)航欄的代碼
思路分析:
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)
效果圖:

首先,我們先甩出主布局文件,方便后面代碼的說明
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/search_border" android:drawableLeft="@android:drawable/ic_menu_search" android:padding="8dp" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@+id/lv" android:layout_width="match_parent" android:layout_height="match_parent" android:divider="@null" /> <TextView android:id="@+id/tv" android:layout_width="50dp" android:layout_height="50dp" android:layout_centerInParent="true" android:background="#888888" android:gravity="center" android:textColor="#000000" android:textSize="18dp" android:visibility="gone" /> <com.handsome.tulin.View.NavView android:id="@+id/nv" android:layout_width="20dp" android:layout_height="match_parent" android:layout_alignParentRight="true" android:layout_margin="16dp" /> </RelativeLayout> </LinearLayout>
步驟一:分析自定義字母導(dǎo)航欄
思路分析:
1、我們在使用的時(shí)候把寬設(shè)置為20dp,高設(shè)置為填充父控件,所以這里獲取的寬度為20dp
2、通過循環(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)長,就不貼出來了。
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)", "馮紹峰", "崔健",
"杜淳", "張翰", "彭坦", "柏栩栩", "蒲巴甲", "凌瀟肅", "毛方圓", "武藝", "耿樂", "錢泳辰"};
@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);
}
}
ListView子布局:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffffff" android:orientation="vertical"> <TextView android:id="@+id/tv_firstCharacter" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#DBDBDA" android:padding="8dp" android:text="A" android:textColor="#000000" android:textSize="14dp" /> <TextView android:id="@+id/tv_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#ffffff" android:padding="8dp" android:text="張棟梁" android:textColor="#2196F3" android:textSize="14dp" /> </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;
}
/**
* 獲得控件管理對象
*
* @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);
}
}
/**
* 通過字符查找位置
*
* @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;
}
}
步驟三:字母導(dǎo)航欄滑動(dòng)事件處理、字母導(dǎo)航欄與中間字母的聯(lián)動(dòng)
思路分析:
1、在自定義View中重寫dispatchTouchEvent處理滑動(dòng)事件,最后返回true。
2、在主Activity傳進(jìn)來一個(gè)TextView,在我們滑動(dòng)的時(shí)候設(shè)置Text,松開的時(shí)候消失Text。設(shè)置Text的時(shí)候需要計(jì)算Text的位置,并且滑過多的話會(huì)出現(xiàn)數(shù)組越界的問題,所以我們在里面處理數(shù)組越界問題。
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)來一個(gè)TextView
*
* @param tv
*/
public void setTextView(TextView tv) {
this.tv = tv;
}
步驟四:字母導(dǎo)航欄和ListView的聯(lián)動(dòng)
思路分析:
1、我們已經(jīng)通過接口傳遞過去了一個(gè)選擇的字母,和在adapter寫好了根據(jù)字母查詢position的方法,這個(gè)時(shí)候只要主Activity對自定義View設(shè)置監(jiān)聽,判斷即可。
//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);
}
}
});
以上所述是小編給大家介紹的Android自定義View實(shí)現(xiàn)字母導(dǎo)航欄的代碼,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
Android中PopupWindow響應(yīng)返回鍵并關(guān)閉的2種方法
這篇文章主要介紹了Android中PopupWindow響應(yīng)返回鍵并關(guān)閉的2種方法,本文講解了最簡單的方法、最通用的方法,需要的朋友可以參考下2015-04-04
Android studio 引用aar 進(jìn)行java開發(fā)的操作步驟
這篇文章主要介紹了Android studio 引用aar 進(jìn)行java開發(fā)的操作步驟,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09
Android 高德地圖之poi搜索功能的實(shí)現(xiàn)代碼
這篇文章主要介紹了android 高德地圖之poi搜索功能的實(shí)現(xiàn)代碼,在實(shí)現(xiàn)此功能時(shí)遇到很多問題,在文章都給大家提到,需要的朋友可以參考下2017-08-08
Android 通過cmake的方式接入opencv的方法步驟
這篇文章主要介紹了Android 通過cmake的方式接入opencv的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
Android?Navigation重建Fragment問題分析及解決
這篇文章主要介紹了Android?Navigation重建Fragment問題分析及解決,文章通過圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09
android自定義View實(shí)現(xiàn)簡單五子棋游戲
這篇文章主要為大家詳細(xì)介紹了android自定義View實(shí)現(xiàn)簡單五子棋游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05

