Android自定义字母导航栏

前端之家收集整理的这篇文章主要介绍了Android自定义字母导航栏前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

本文实例为大家分享了Android字母导航栏的具体代码,供大家参考,具体内容如下

效果

Android自定义字母导航栏


实现逻辑

明确需求

字母导航栏在实际开发中还是比较多见的,城市选择、名称选择等等可能需要到。 现在要做到的就是在滑动控件过程中可以有内容以及 下标的回调,方便处理其他逻辑!

整理思路

1、确定控件的尺寸,防止内容显示不全。相关的逻辑在onMeasure()方法中处理;
2、绘制显示内容,在按下和抬起不同状态下文字、背景的颜色。相关逻辑在onDraw()方法中;
3、滑动事件的处理以及事件回调。相关逻辑在onTouchEvent()方法中;

动手实现

在需求明确、思路清晰的情况下就要开始动手实现(需要了解自定义View的一些基础API)。核心代码就onDraw()中。在代码中有思路和注释,可以结合代码一起看看。如果有疑惑、优化、错误的地方请在评论区提出,共同进步!

完整代码

  1. /**
  2. * 自定义字母导航栏
  3. *
  4. * 总的来说就四步
  5. * 1、测量控件尺寸{@link #onMeasure(int,int)}
  6. * 2、绘制显示内容(背景以及字符){@link #onDraw(Canvas)}
  7. * 3、处理滑动事件{@link #onTouchEvent(MotionEvent)}
  8. * 4、暴露接口{@link #setOnNavigationScrollerListener(OnNavigationScrollerListener)}
  9. *
  10. * @attr customTextColorDefault //导航栏默认文字颜色
  11. * @attr customTextColorDown //导航栏按下文字颜色
  12. * @attr customBackgroundColorDown //导航栏按下背景颜色
  13. * @attr customLetterDivHeight //导航栏内容高度间隔
  14. * @attr customTextSize //导航栏文字尺寸
  15. * @attr customBackgroundAngle //导航栏背景角度
  16. */
  17. public class CustomLetterNavigationView extends View {
  18. private static final String TAG = "CustomLetterNavigation";
  19. //导航内容
  20. private String[] mNavigationContent;
  21. //导航栏内容间隔
  22. private float mContentDiv;
  23. //导航栏文字大小
  24. private float mContentTextSize;
  25. //导航栏文字颜色
  26. private int mContentTextColor;
  27. //导航栏按下时背景颜色
  28. private int mBackgroundColor;
  29. //导航栏按下时圆角度数
  30. private int mBackGroundAngle = 0;
  31. //导航栏按下时文字颜色
  32. private int mDownContentTextColor;
  33. private TextPaint mTextPaint;
  34. private Paint mPaintBackgrount;
  35. private boolean mEventActionState = false;
  36. private String mCurrentLetter = "";
  37. private OnNavigationScrollerListener mOnNavigationScrollerListener;
  38. private final RectF mRectF = new RectF();
  39. public CustomLetterNavigationView(Context context) {
  40. this(context,null);
  41. }
  42.  
  43. public CustomLetterNavigationView(Context context,@Nullable AttributeSet attrs) {
  44. this(context,attrs,0);
  45. }
  46.  
  47. public CustomLetterNavigationView(Context context,@Nullable AttributeSet attrs,int defStyleAttr) {
  48. super(context,defStyleAttr);
  49. initDefaultData();//初始化默认数据
  50. initAttrs(context,attrs);
  51. }
  52.  
  53. @Override
  54. protected void onDraw(Canvas canvas) {
  55. /**
  56. * <P>
  57. * 这里我们分为两步
  58. *
  59. * 1、绘制背景
  60. * 这里简单,直接调用Canvas的drawRoundRect()方法直接绘制
  61. * 2、绘制显示文本
  62. * 绘制文字首先要定位,定位每个字符的坐标
  63. * X轴简单,宽度的一半
  64. * Y轴坐标通过每个字符的高heightShould乘以已绘制字符的数目
  65. * </P>
  66. */
  67. int mViewWidth = getWidth();
  68. //绘制背景
  69. mRectF.set(0,mViewWidth,getHeight());
  70. if (mEventActionState) {
  71. mTextPaint.setColor(mDownContentTextColor);
  72. mPaintBackgrount.setColor(mBackgroundColor);
  73. canvas.drawRoundRect(mRectF,mBackGroundAngle,mPaintBackgrount);
  74. } else {
  75. mTextPaint.setColor(mContentTextColor);
  76. mPaintBackgrount.setColor(Color.TRANSPARENT);
  77. Drawable mBackground = getBackground();
  78. if (mBackground instanceof ColorDrawable) {
  79. mPaintBackgrount.setColor(((ColorDrawable) mBackground).getColor());
  80. }
  81. canvas.drawRoundRect(mRectF,mPaintBackgrount);
  82. }
  83. //绘制文本
  84. float textX = mViewWidth / 2;
  85. //X轴坐标
  86. int contentLenght = getContentLength();
  87. //Y轴坐标(这里在测量的时候多加入了两个间隔高度要减去,同时还有Padding值)
  88. float heightShould = (getHeight() - mContentDiv * 2 - getPaddingTop() - getPaddingBottom()) / contentLenght;
  89. for (int i = 0; i < contentLenght; i++) {
  90. //计算Y轴的坐标
  91. float startY = ((i + 1) * heightShould) + getPaddingTop();
  92. //绘制文字
  93. canvas.drawText(mNavigationContent[i],textX,startY,mTextPaint);
  94. }
  95. }
  96.  
  97. @Override
  98. public boolean onTouchEvent(MotionEvent event) {
  99. /**
  100. * 这里主要处理手指滑动事件
  101. */
  102. float mEventY = event.getY();
  103. switch (event.getAction()) {
  104. case MotionEvent.ACTION_DOWN:
  105. //手指按下的时候,修改Enent状态、重绘背景、触发回调
  106. mEventActionState = true;
  107. invalidate();
  108. if (mOnNavigationScrollerListener != null) {
  109. mOnNavigationScrollerListener.onDown();
  110. }
  111. scrollCount(mEventY);
  112. break;
  113. case MotionEvent.ACTION_MOVE:
  114. scrollCount(mEventY);
  115. break;
  116. case MotionEvent.ACTION_CANCEL:
  117. case MotionEvent.ACTION_UP:
  118. //手指离开的时候,修改Enent状态、重绘背景、触发回调
  119. mEventActionState = false;
  120. invalidate();
  121. if (mOnNavigationScrollerListener != null) {
  122. mOnNavigationScrollerListener.onUp();
  123. }
  124. break;
  125. }
  126. return true;
  127. }
  128.  
  129. @Override
  130. protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec) {
  131. super.onMeasure(widthMeasureSpec,heightMeasureSpec);
  132. /**
  133. * <p>
  134. * 这里做了简单的适应,其目的就是为了能够正常的显示我们的内容
  135. *
  136. * 不管设置的是真实尺寸或者是包裹内容,都会以内容的最小尺寸为
  137. * 基础,如果设置的控件尺寸大于我们内容的最小尺寸,就使用控件
  138. * 尺寸,反之使用内容的最小尺寸!
  139. * </p>
  140. */
  141. int widhtMode = MeasureSpec.getMode(widthMeasureSpec);
  142. int heightMode = MeasureSpec.getMode(heightMeasureSpec);
  143. //获取控件的尺寸
  144. int actualWidth = MeasureSpec.getSize(widthMeasureSpec);
  145. int actualHeight = MeasureSpec.getSize(heightMeasureSpec);
  146. int contentLegth = getContentLength();
  147. //计算一个文字的尺寸
  148. Rect mRect = measureTextSize();
  149. //内容的最小宽度
  150. float contentWidth = mRect.width() + mContentDiv * 2;
  151. //内容的最小高度
  152. float contentHeight = mRect.height() * contentLegth + mContentDiv * (contentLegth + 3);
  153. if (MeasureSpec.AT_MOST == widhtMode) {
  154. //宽度包裹内容
  155. actualWidth = (int) contentWidth + getPaddingLeft() + getPaddingRight();
  156. } else if (MeasureSpec.EXACTLY == widhtMode) {
  157. //宽度限制
  158. if (actualWidth < contentWidth) {
  159. actualWidth = (int) contentWidth + getPaddingLeft() + getPaddingRight();
  160. }
  161. }
  162. if (MeasureSpec.AT_MOST == heightMode) {
  163. //高度包裹内容
  164. actualHeight = (int) contentHeight + getPaddingTop() + getPaddingBottom();
  165. } else if (MeasureSpec.EXACTLY == widhtMode) {
  166. //高度限制
  167. if (actualHeight < contentHeight) {
  168. actualHeight = (int) contentHeight + getPaddingTop() + getPaddingBottom();
  169. }
  170. }
  171. setMeasuredDimension(actualWidth,actualHeight);
  172. }
  173.  
  174. /**
  175. * 初始化默认数据
  176. */
  177. private void initDefaultData() {
  178. mNavigationContent = 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"};
  179. mContentDiv = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,5,getResources().getDisplayMetrics());
  180. mContentTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,14,getResources().getDisplayMetrics());
  181. mContentTextColor = Color.parseColor("#333333");
  182. mDownContentTextColor = Color.WHITE;
  183. mBackgroundColor = Color.parseColor("#d7d7d7");
  184. mBackGroundAngle = 0;
  185. //绘制文字画笔
  186. mTextPaint = new TextPaint();
  187. mTextPaint.setAntiAlias(true);
  188. mTextPaint.setTextSize(mContentTextSize);
  189. mTextPaint.setColor(mContentTextColor);
  190. mTextPaint.setTextAlign(Paint.Align.CENTER);
  191. //绘制背景画笔
  192. mPaintBackgrount = new Paint();
  193. mPaintBackgrount.setAntiAlias(true);
  194. mPaintBackgrount.setStyle(Paint.Style.FILL);
  195. }
  196.  
  197. /**
  198. * 初始化自定义属性
  199. *
  200. * @param context
  201. * @param attrs
  202. */
  203. private void initAttrs(Context context,AttributeSet attrs) {
  204. TypedArray mTypedArray = context.obtainStyledAttributes(attrs,R.styleable.CustomLetterNavigationView);
  205. mContentTextColor = mTypedArray.getColor(R.styleable.CustomLetterNavigationView_customTextColorDefault,mContentTextColor);
  206. mBackgroundColor = mTypedArray.getColor(R.styleable.CustomLetterNavigationView_customBackgroundColorDown,mBackgroundColor);
  207. mDownContentTextColor = mTypedArray.getColor(R.styleable.CustomLetterNavigationView_customTextColorDown,mDownContentTextColor);
  208. mContentTextSize = mTypedArray.getDimension(R.styleable.CustomLetterNavigationView_customTextSize,mContentTextSize);
  209. mContentDiv = mTypedArray.getFloat(R.styleable.CustomLetterNavigationView_customLetterDivHeight,mContentDiv);
  210. mBackGroundAngle = mTypedArray.getInt(R.styleable.CustomLetterNavigationView_customBackgroundAngle,mBackGroundAngle);
  211. mTypedArray.recycle();
  212. }
  213.  
  214. /**
  215. * 获取内容长度
  216. *
  217. * @return 内容长度
  218. */
  219. private int getContentLength() {
  220. if (mNavigationContent != null) {
  221. return mNavigationContent.length;
  222. }
  223. return 0;
  224. }
  225.  
  226. /**
  227. * 滑动计算
  228. *
  229. * @param mEventY Y轴滑动距离
  230. */
  231. private void scrollCount(float mEventY) {
  232. //滑动的时候利用滑动距离和每一个字符高度进行取整,获取到Index
  233. Rect mRect = measureTextSize();
  234. int index = (int) ((mEventY - getPaddingTop() - getPaddingBottom() - mContentDiv * 2) / (mRect.height() + mContentDiv));
  235. //防止越界
  236. if (index >= 0 && index < getContentLength()) {
  237. String newLetter = mNavigationContent[index];
  238. //防止重复触发回调
  239. if (!mCurrentLetter.equals(newLetter)) {
  240. mCurrentLetter = newLetter;
  241. if (mOnNavigationScrollerListener != null) {
  242. mOnNavigationScrollerListener.onScroll(mCurrentLetter,index);
  243. }
  244. }
  245. }
  246. }
  247.  
  248. /**
  249. * 测量文字的尺寸
  250. *
  251. * @return 一个汉字的矩阵
  252. */
  253. public Rect measureTextSize() {
  254. Rect mRect = new Rect();
  255. if (mTextPaint != null) {
  256. mTextPaint.getTextBounds("田",1,mRect);
  257. }
  258. return mRect;
  259. }
  260.  
  261. /**
  262. * 设置导航栏滑动监听
  263. *
  264. * @param onNavigationScrollerListener
  265. */
  266. public void setOnNavigationScrollerListener(OnNavigationScrollerListener onNavigationScrollerListener) {
  267. this.mOnNavigationScrollerListener = onNavigationScrollerListener;
  268. }
  269.  
  270. /**
  271. * 设置导航栏显示内容
  272. *
  273. * @param content 需要显示内容
  274. */
  275. public void setNavigationContent(String content) {
  276. if (!TextUtils.isEmpty(content)) {
  277. mNavigationContent = null;
  278. mNavigationContent = new String[content.length()];
  279. for (int i = 0; i < content.length(); i++) {
  280. mNavigationContent[i] = String.valueOf(content.charAt(i));
  281. }
  282. }
  283. //需要重新测量
  284. requestLayout();
  285. }
  286.  
  287. public interface OnNavigationScrollerListener {
  288. //按下
  289. void onDown();
  290.  
  291. //滑动
  292. void onScroll(String letter,int position);
  293.  
  294. //离开
  295. void onUp();
  296.  
  297. }
  298. }

自定义属性

  1. <declare-styleable name="CustomLetterNavigationView">
  2. <attr name="customTextColorDefault" format="color" />
  3. <attr name="customTextColorDown" format="color" />
  4. <attr name="customBackgroundColorDown" format="color" />
  5. <attr name="customLetterDivHeight" format="dimension" />
  6. <attr name="customTextSize" format="dimension" />
  7. <attr name="customBackgroundAngle" format="integer" />
  8. </declare-styleable>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

猜你在找的Android相关文章