Android實(shí)現(xiàn)字母導(dǎo)航控件的示例代碼
今天分享一個(gè)以前實(shí)現(xiàn)的通訊錄字母導(dǎo)航控件,下面自定義一個(gè)類似通訊錄的字母導(dǎo)航 View,可以知道需要自定義的幾個(gè)要素,如繪制字母指示器、繪制文字、觸摸監(jiān)聽、坐標(biāo)計(jì)算等,自定義完成之后能夠達(dá)到的功能如下:
- 完成列表數(shù)據(jù)與字母之間的相互聯(lián)動(dòng);
- 支持布局文件屬性配置;
- 在布局文件中能夠配置相關(guān)屬性,如字母顏色、字母字體大小、字母指示器顏色等屬性。
主要內(nèi)容如下:
- 自定義屬性
- Measure測量
- 坐標(biāo)計(jì)算
- 繪制
- 顯示效果
自定義屬性
在 value 下面創(chuàng)建 attr.xml ,在里面配置需要自定義的屬性,具體如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="LetterView">
<!--字母顏色-->
<attr name="letterTextColor" format="color" />
<!--字母字體大小-->
<attr name="letterTextSize" format="dimension" />
<!--整體背景-->
<attr name="letterTextBackgroundColor" format="color" />
<!--是否啟用指示器-->
<attr name="letterEnableIndicator" format="boolean" />
<!--指示器顏色-->
<attr name="letterIndicatorColor" format="color" />
</declare-styleable>
</resources>然后在相應(yīng)的構(gòu)造方法中獲取這些屬性并進(jìn)行相關(guān)屬性的設(shè)置,具體如下:
public LetterView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
//獲取屬性
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.LetterView);
int letterTextColor = array.getColor(R.styleable.LetterView_letterTextColor, Color.RED);
int letterTextBackgroundColor = array.getColor(R.styleable.LetterView_letterTextBackgroundColor, Color.WHITE);
int letterIndicatorColor = array.getColor(R.styleable.LetterView_letterIndicatorColor, Color.parseColor("#333333"));
float letterTextSize = array.getDimension(R.styleable.LetterView_letterTextSize, 12);
enableIndicator = array.getBoolean(R.styleable.LetterView_letterEnableIndicator, true);
//默認(rèn)設(shè)置
mContext = context;
mLetterPaint = new Paint();
mLetterPaint.setTextSize(letterTextSize);
mLetterPaint.setColor(letterTextColor);
mLetterPaint.setAntiAlias(true);
mLetterIndicatorPaint = new Paint();
mLetterIndicatorPaint.setStyle(Paint.Style.FILL);
mLetterIndicatorPaint.setColor(letterIndicatorColor);
mLetterIndicatorPaint.setAntiAlias(true);
setBackgroundColor(letterTextBackgroundColor);
array.recycle();
}Measure測量
要想精確的控制自定義的尺寸以及坐標(biāo),必須要測量出當(dāng)前自定義 View 的寬高,然后才可以通過測量到的尺寸計(jì)算相關(guān)坐標(biāo),具體測量過程就是繼承 View 重寫 omMeasure() 方法完成測量,關(guān)鍵代碼如下:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//獲取寬高的尺寸大小
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
//wrap_content默認(rèn)寬高
@SuppressLint("DrawAllocation") Rect mRect = new Rect();
mLetterPaint.getTextBounds("A", 0, 1, mRect);
mWidth = mRect.width() + dpToPx(mContext, 12);
int mHeight = (mRect.height() + dpToPx(mContext, 5)) * letters.length;
if (getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT &&
getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) {
setMeasuredDimension(mWidth, mHeight);
} else if (getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT) {
setMeasuredDimension(mWidth, heightSize);
} else if (getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) {
setMeasuredDimension(widthSize, mHeight);
}
mWidth = getMeasuredWidth();
int averageItemHeight = getMeasuredHeight() / 28;
int mOffset = averageItemHeight / 30; //界面調(diào)整
mItemHeight = averageItemHeight + mOffset;
}坐標(biāo)計(jì)算
自定義 View 實(shí)際上就是在 View 上找到合適的位置,將自定義的元素有序的繪制出來即可,繪制過程最困難的就是如何根據(jù)具體需求計(jì)算合適的左邊,至于繪制都是 API 的調(diào)用,只要坐標(biāo)位置計(jì)算好了,自定義 View 繪制這一塊應(yīng)該就沒有問題了,下面的圖示主要是標(biāo)注了字母指示器繪制的中心位置坐標(biāo)的計(jì)算以及文字繪制的起點(diǎn)位置計(jì)算,繪制過程中要保證文字在指示器中心位置,參考如下:

繪制
自定義 View 的繪制操作都是在 onDraw() 方法中進(jìn)行的,這里主要使用到圓的繪制以及文字的繪制,具體就是 drawCircle() 和 drawText() 方法的使用,為避免文字被遮擋,需繪制字母指示器,然后再繪制字母,代碼參考如下:
@Override
protected void onDraw(Canvas canvas) {
//獲取字母寬高
@SuppressLint("DrawAllocation") Rect rect = new Rect();
mLetterPaint.getTextBounds("A", 0, 1, rect);
int letterWidth = rect.width();
int letterHeight = rect.height();
//繪制指示器
if (enableIndicator){
for (int i = 1; i < letters.length + 1; i++) {
if (mTouchIndex == i) {
canvas.drawCircle(0.5f * mWidth, i * mItemHeight - 0.5f * mItemHeight, 0.5f * mItemHeight, mLetterIndicatorPaint);
}
}
}
//繪制字母
for (int i = 1; i < letters.length + 1; i++) {
canvas.drawText(letters[i - 1], (mWidth - letterWidth) / 2, mItemHeight * i - 0.5f * mItemHeight + letterHeight / 2, mLetterPaint);
}
}到此為止,可以說 View 的基本繪制結(jié)束了,現(xiàn)在使用自定義的 View 界面能夠顯示出來了,只是還沒有添加相關(guān)的事件操作,下面將在 View 的觸摸事件里實(shí)現(xiàn)相關(guān)邏輯。
Touch事件處理
為了判斷手指當(dāng)前所在位置對(duì)應(yīng)的是哪一個(gè)字母,需要獲取當(dāng)前觸摸的坐標(biāo)位置來計(jì)算字母索引,重新 onTouchEvent() 方法,監(jiān)聽 MotionEvent.ACTION_DOWN、MotionEvent.ACTION_MOVE 來計(jì)算索引位置,監(jiān)聽 MotionEvent.ACTION_UP 將獲得結(jié)果回調(diào)出去,具體參考如下:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
isTouch = true;
int y = (int) event.getY();
Log.i("onTouchEvent","--y->" + y + "-y-dp-->" + DensityUtil.px2dp(getContext(), y));
int index = y / mItemHeight;
if (index != mTouchIndex && index < 28 && index > 0) {
mTouchIndex = index;
Log.i("onTouchEvent","--mTouchIndex->" + mTouchIndex + "--position->" + mTouchIndex);
}
if (mOnLetterChangeListener != null && mTouchIndex > 0) {
mOnLetterChangeListener.onLetterListener(letters[mTouchIndex - 1]);
}
invalidate();
break;
case MotionEvent.ACTION_UP:
isTouch = false;
if (mOnLetterChangeListener != null && mTouchIndex > 0) {
mOnLetterChangeListener.onLetterDismissListener();
}
break;
}
return true;
}到此為止,View 的自定義關(guān)鍵部分基本完成。
數(shù)據(jù)組裝
字母導(dǎo)航的基本思路是將某個(gè)需要與字母匹配的字段轉(zhuǎn)換為對(duì)應(yīng)的字母,然后按照該字段對(duì)數(shù)據(jù)進(jìn)行排序,最終使得通過某個(gè)數(shù)據(jù)字段的首字母就可以批匹配到相同首字母的數(shù)據(jù)了,這里將漢字轉(zhuǎn)化為拼音使用的是 pinyin4j-2.5.0.jar ,然后對(duì)數(shù)據(jù)項(xiàng)按照首字母進(jìn)行排序?qū)?shù)據(jù)展示到出來即可,漢字裝換為拼音如下:
//漢字轉(zhuǎn)換為拼音
public static String getChineseToPinyin(String chinese) {
StringBuilder builder = new StringBuilder();
HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
format.setCaseType(HanyuPinyinCaseType.UPPERCASE);
format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
char[] charArray = chinese.toCharArray();
for (char aCharArray : charArray) {
if (Character.isSpaceChar(aCharArray)) {
continue;
}
try {
String[] pinyinArr = PinyinHelper.toHanyuPinyinStringArray(aCharArray, format);
if (pinyinArr != null) {
builder.append(pinyinArr[0]);
} else {
builder.append(aCharArray);
}
} catch (BadHanyuPinyinOutputFormatCombination badHanyuPinyinOutputFormatCombination) {
badHanyuPinyinOutputFormatCombination.printStackTrace();
builder.append(aCharArray);
}
}
return builder.toString();
}至于數(shù)據(jù)排序使用 Comparator 接口即可
顯示效果
顯示效果如下:

到此這篇關(guān)于Android實(shí)現(xiàn)字母導(dǎo)航控件的示例代碼的文章就介紹到這了,更多相關(guān)Android字母導(dǎo)航控件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android實(shí)戰(zhàn)APP啟動(dòng)速度優(yōu)化
本篇文章給大家通過實(shí)戰(zhàn)總結(jié)了Android開發(fā)APP啟動(dòng)速度優(yōu)化的方法以及需要注意的地方,有需要的朋友可以參考下。2018-05-05
Android中Viewpager禁止滑動(dòng)的實(shí)現(xiàn)
有時(shí)候在開發(fā)中會(huì)遇到一些特別的要求,如在ViewPager中嵌入ListView,或者再嵌入一個(gè)ViewPager,那么在滑動(dòng)的時(shí)候就會(huì)造成被嵌入的XXView不能滑動(dòng)了,那么就把最外層的ViewPager禁止滑動(dòng)吧,本文就介紹了Android中Viewpager禁止滑動(dòng)的實(shí)現(xiàn)方法,需要的朋友可以參考。2017-05-05
Android 6.0指紋識(shí)別App開發(fā)案例
這篇文章主要為大家詳細(xì)介紹了Android 6.0 指紋識(shí)別App開發(fā)案例,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09
Android開發(fā)仿IOS滑動(dòng)開關(guān)實(shí)現(xiàn)代碼
這篇文章主要介紹了 android開發(fā)仿IOS滑動(dòng)開關(guān)實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-05-05
Flutter中跨組件數(shù)據(jù)傳遞的方法總結(jié)
Flutter中的數(shù)據(jù)傳遞一般包括:父->子,子->父,父->父,也就是說嵌套時(shí)的傳遞以及跨頁面的傳遞,本文整理了三種我們通常使用的方法,需要的可以參考一下2023-06-06
Android Webview滑進(jìn)出屏幕閃爍的解決方法
這篇文章主要給大家介紹了關(guān)于Android Webview滑進(jìn)出屏幕閃爍的解決方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)各位Android開發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03
Android使用Item Swipemenulistview實(shí)現(xiàn)仿QQ側(cè)滑刪除功能
大家都用過QQ,肯定有人好奇QQ滑動(dòng)刪除Item的效果是怎樣實(shí)現(xiàn)的,其實(shí)我們使用Swipemenulistview就可以簡單的實(shí)現(xiàn)。這篇文章主要介紹了Android使用ItemSwipemenulistview實(shí)現(xiàn)仿QQ側(cè)滑刪除功能,需要的朋友可以參考下2017-02-02

