自定义View实现字母导航控件

阅读: 评论:0

自定义View实现字母导航控件

自定义View实现字母导航控件

PS:如果不能严格内化自己,就没有足够的心理能量进行深度工作。

今天分享一个以前实现的通讯录字母导航控件,下面自定义一个类似通讯录的字母导航 View,可以知道需要自定义的几个要素,如绘制字母指示器、绘制文字、触摸监听、坐标计算等,自定义完成之后能够达到的功能如下:

  • 完成列表数据与字母之间的相互联动;
  • 支持布局文件属性配置;
  • 在布局文件中能够配置相关属性,如字母颜色、字母字体大小、字母指示器颜色等属性;

主要内容如下:

  1. 自定义属性
  2. Measure测量
  3. 坐标计算
  4. 绘制
  5. 显示效果
自定义属性

在 value 下面创建 l ,在里面配置需要自定义的属性,具体如下:

<?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>

然后在相应的构造方法中获取这些属性并进行相关属性的设置,具体如下:

public LetterView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);//获取属性TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.LetterView);int letterTextColor = Color(R.styleable.LetterView_letterTextColor, Color.RED);int letterTextBackgroundColor = Color(R.styleable.LetterView_letterTextBackgroundColor, Color.WHITE);int letterIndicatorColor = Color(R.styleable.LetterView_letterIndicatorColor, Color.parseColor("#333333"));float letterTextSize = Dimension(R.styleable.LetterView_letterTextSize, 12);enableIndicator = Boolean(R.styleable.LetterView_letterEnableIndicator, true);//默认设置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);le();
}
Measure测量

要想精确的控制自定义的尺寸以及坐标,必须要测量出当前自定义 View 的宽高,然后才可以通过测量到的尺寸计算相关坐标,具体测量过程就是继承 View 重写 omMeasure() 方法完成测量,关键代码如下:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {Measure(widthMeasureSpec, heightMeasureSpec);//获取宽高的尺寸大小int widthSize = Size(widthMeasureSpec);int heightSize = Size(heightMeasureSpec);//wrap_content默认宽高@SuppressLint("DrawAllocation") Rect mRect = new Rect();TextBounds("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; //界面调整mItemHeight = averageItemHeight + mOffset;
}
坐标计算

自定义 View 实际上就是在 View 上找到合适的位置,将自定义的元素有序的绘制出来即可,绘制过程最困难的就是如何根据具体需求计算合适的左边,至于绘制都是 API 的调用,只要坐标位置计算好了,自定义 View 绘制这一块应该就没有问题了,下面的图示主要是标注了字母指示器绘制的中心位置坐标的计算以及文字绘制的起点位置计算,绘制过程中要保证文字在指示器中心位置,参考如下:

绘制

自定义 View 的绘制操作都是在 onDraw() 方法中进行的,这里主要使用到圆的绘制以及文字的绘制,具体就是 drawCircle() 和 drawText() 方法的使用,为避免文字被遮挡,需现绘制字母指示器,然后再绘制字母,代码参考如下:

@Override
protected void onDraw(Canvas canvas) {//获取字母宽高@SuppressLint("DrawAllocation") Rect rect = new Rect();TextBounds("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 的基本绘制结束了,现在使用自定义的 View 界面能够显示出来了,只是还没有添加相关的事件操作,下面将在 View 的触摸事件里实现相关逻辑。

Touch事件处理

为了判断手指当前所在位置对应的是哪一个字母,需要获取当前触摸的坐标位置来计算字母索引,重新 onTouchEvent() 方法,监听 MotionEvent.ACTION_DOWN、MotionEvent.ACTION_MOVE 来计算索引位置,监听 MotionEvent.ACTION_UP 将获得结果回调出去,具体参考如下:

@Override
public boolean onTouchEvent(MotionEvent event) {switch (Action()) {case MotionEvent.ACTION_DOWN:case MotionEvent.ACTION_MOVE:isTouch = true;int y = (int) Y();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) {LetterListener(letters[mTouchIndex - 1]);}invalidate();break;case MotionEvent.ACTION_UP:isTouch = false;if (mOnLetterChangeListener != null && mTouchIndex > 0) {LetterDismissListener();}break;}return true;
}

到此为止,View 的自定义关键部分基本完成。

数据组装

字母导航的基本思路是将某个需要与字母匹配的字段转换为对应的字母,然后按照该字段对数据进行排序,最终使得通过某个数据字段的首字母就可以批匹配到相同首字母的数据了,这里将汉字转化为拼音使用的是 pinyin4j-2.5.0.jar ,然后对数据项按照首字母进行排序将数据展示到出来即可,汉字装换为拼音如下:


//汉字转换为拼音
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 = CharArray();for (char aCharArray : charArray) {if (Character.isSpaceChar(aCharArray)) {continue;}try {String[] pinyinArr = HanyuPinyinStringArray(aCharArray, format);if (pinyinArr != null) {builder.append(pinyinArr[0]);} else {builder.append(aCharArray);}} catch (BadHanyuPinyinOutputFormatCombination badHanyuPinyinOutputFormatCombination) {badHanyuPinyinOutputFormatCombination.printStackTrace();builder.append(aCharArray);}}String();
}

至于数据排序使用 Comparator 接口即可,这里就不在赘述了,具体获取文末源码链接查看。

显示效果

显示效果如下:

在公众号躬行之回复关键字【MLetterView】查看源码。

本文发布于:2024-02-01 17:21:49,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170678039038232.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:自定义   控件   字母   View
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23