Android自定义字母导航栏
程序员文章站
2022-09-02 09:27:56
本文实例为大家分享了android字母导航栏的具体代码,供大家参考,具体内容如下
效果
实现逻辑
明确需求
字母导航栏在实际开发中还是比较多见的,城市选择、名称选择等等...
本文实例为大家分享了android字母导航栏的具体代码,供大家参考,具体内容如下
效果
实现逻辑
明确需求
字母导航栏在实际开发中还是比较多见的,城市选择、名称选择等等可能需要到。 现在要做到的就是在滑动控件过程中可以有内容以及 下标的回调,方便处理其他逻辑!
整理思路
1、确定控件的尺寸,防止内容显示不全。相关的逻辑在onmeasure()方法中处理;
2、绘制显示的内容,在按下和抬起不同状态下文字、背景的颜色。相关逻辑在ondraw()方法中;
3、滑动事件的处理以及事件回调。相关逻辑在ontouchevent()方法中;
动手实现
在需求明确、思路清晰的情况下就要开始动手实现(需要了解自定义view的一些基础api)。核心代码就ondraw()中。在代码中有思路和注释,可以结合代码一起看看。如果有疑惑、优化、错误的地方请在评论区提出,共同进步!
完整代码
/** * 自定义字母导航栏 * * 总的来说就四步 * 1、测量控件尺寸{@link #onmeasure(int, int)} * 2、绘制显示内容(背景以及字符){@link #ondraw(canvas)} * 3、处理滑动事件{@link #ontouchevent(motionevent)} * 4、暴露接口{@link #setonnavigationscrollerlistener(onnavigationscrollerlistener)} * * @attr customtextcolordefault //导航栏默认文字颜色 * @attr customtextcolordown //导航栏按下文字颜色 * @attr custombackgroundcolordown //导航栏按下背景颜色 * @attr customletterdivheight //导航栏内容高度间隔 * @attr customtextsize //导航栏文字尺寸 * @attr custombackgroundangle //导航栏背景角度 */ public class customletternavigationview extends view { private static final string tag = "customletternavigation"; //导航内容 private string[] mnavigationcontent; //导航栏内容间隔 private float mcontentdiv; //导航栏文字大小 private float mcontenttextsize; //导航栏文字颜色 private int mcontenttextcolor; //导航栏按下时背景颜色 private int mbackgroundcolor; //导航栏按下时圆角度数 private int mbackgroundangle = 0; //导航栏按下时文字颜色 private int mdowncontenttextcolor; private textpaint mtextpaint; private paint mpaintbackgrount; private boolean meventactionstate = false; private string mcurrentletter = ""; private onnavigationscrollerlistener monnavigationscrollerlistener; private final rectf mrectf = new rectf(); public customletternavigationview(context context) { this(context, null); } public customletternavigationview(context context, @nullable attributeset attrs) { this(context, attrs, 0); } public customletternavigationview(context context, @nullable attributeset attrs, int defstyleattr) { super(context, attrs, defstyleattr); initdefaultdata();//初始化默认数据 initattrs(context, attrs); } @override protected void ondraw(canvas canvas) { /** * <p> * 这里我们分为两步 * * 1、绘制背景 * 这里简单,直接调用canvas的drawroundrect()方法直接绘制 * 2、绘制显示文本 * 绘制文字首先要定位,定位每个字符的坐标 * x轴简单,宽度的一半 * y轴坐标通过每个字符的高heightshould乘以已绘制字符的数目 * </p> */ int mviewwidth = getwidth(); //绘制背景 mrectf.set(0, 0, mviewwidth, getheight()); if (meventactionstate) { mtextpaint.setcolor(mdowncontenttextcolor); mpaintbackgrount.setcolor(mbackgroundcolor); canvas.drawroundrect(mrectf, mbackgroundangle, mbackgroundangle, mpaintbackgrount); } else { mtextpaint.setcolor(mcontenttextcolor); mpaintbackgrount.setcolor(color.transparent); drawable mbackground = getbackground(); if (mbackground instanceof colordrawable) { mpaintbackgrount.setcolor(((colordrawable) mbackground).getcolor()); } canvas.drawroundrect(mrectf, mbackgroundangle, mbackgroundangle, mpaintbackgrount); } //绘制文本 float textx = mviewwidth / 2; //x轴坐标 int contentlenght = getcontentlength(); //y轴坐标(这里在测量的时候多加入了两个间隔高度要减去,同时还有padding值) float heightshould = (getheight() - mcontentdiv * 2 - getpaddingtop() - getpaddingbottom()) / contentlenght; for (int i = 0; i < contentlenght; i++) { //计算y轴的坐标 float starty = ((i + 1) * heightshould) + getpaddingtop(); //绘制文字 canvas.drawtext(mnavigationcontent[i], textx, starty, mtextpaint); } } @override public boolean ontouchevent(motionevent event) { /** * 这里主要处理手指滑动事件 */ float meventy = event.gety(); switch (event.getaction()) { case motionevent.action_down: //手指按下的时候,修改enent状态、重绘背景、触发回调 meventactionstate = true; invalidate(); if (monnavigationscrollerlistener != null) { monnavigationscrollerlistener.ondown(); } scrollcount(meventy); break; case motionevent.action_move: scrollcount(meventy); break; case motionevent.action_cancel: case motionevent.action_up: //手指离开的时候,修改enent状态、重绘背景、触发回调 meventactionstate = false; invalidate(); if (monnavigationscrollerlistener != null) { monnavigationscrollerlistener.onup(); } break; } return true; } @override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { super.onmeasure(widthmeasurespec, heightmeasurespec); /** * <p> * 这里做了简单的适应,其目的就是为了能够正常的显示我们的内容 * * 不管设置的是真实尺寸或者是包裹内容,都会以内容的最小尺寸为 * 基础,如果设置的控件尺寸大于我们内容的最小尺寸,就使用控件 * 尺寸,反之使用内容的最小尺寸! * </p> */ int widhtmode = measurespec.getmode(widthmeasurespec); int heightmode = measurespec.getmode(heightmeasurespec); //获取控件的尺寸 int actualwidth = measurespec.getsize(widthmeasurespec); int actualheight = measurespec.getsize(heightmeasurespec); int contentlegth = getcontentlength(); //计算一个文字的尺寸 rect mrect = measuretextsize(); //内容的最小宽度 float contentwidth = mrect.width() + mcontentdiv * 2; //内容的最小高度 float contentheight = mrect.height() * contentlegth + mcontentdiv * (contentlegth + 3); if (measurespec.at_most == widhtmode) { //宽度包裹内容 actualwidth = (int) contentwidth + getpaddingleft() + getpaddingright(); } else if (measurespec.exactly == widhtmode) { //宽度限制 if (actualwidth < contentwidth) { actualwidth = (int) contentwidth + getpaddingleft() + getpaddingright(); } } if (measurespec.at_most == heightmode) { //高度包裹内容 actualheight = (int) contentheight + getpaddingtop() + getpaddingbottom(); } else if (measurespec.exactly == widhtmode) { //高度限制 if (actualheight < contentheight) { actualheight = (int) contentheight + getpaddingtop() + getpaddingbottom(); } } setmeasureddimension(actualwidth, actualheight); } /** * 初始化默认数据 */ private void initdefaultdata() { 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"}; mcontentdiv = typedvalue.applydimension(typedvalue.complex_unit_dip, 5, getresources().getdisplaymetrics()); mcontenttextsize = typedvalue.applydimension(typedvalue.complex_unit_sp, 14, getresources().getdisplaymetrics()); mcontenttextcolor = color.parsecolor("#333333"); mdowncontenttextcolor = color.white; mbackgroundcolor = color.parsecolor("#d7d7d7"); mbackgroundangle = 0; //绘制文字画笔 mtextpaint = new textpaint(); mtextpaint.setantialias(true); mtextpaint.settextsize(mcontenttextsize); mtextpaint.setcolor(mcontenttextcolor); mtextpaint.settextalign(paint.align.center); //绘制背景画笔 mpaintbackgrount = new paint(); mpaintbackgrount.setantialias(true); mpaintbackgrount.setstyle(paint.style.fill); } /** * 初始化自定义属性 * * @param context * @param attrs */ private void initattrs(context context, attributeset attrs) { typedarray mtypedarray = context.obtainstyledattributes(attrs, r.styleable.customletternavigationview); mcontenttextcolor = mtypedarray.getcolor(r.styleable.customletternavigationview_customtextcolordefault, mcontenttextcolor); mbackgroundcolor = mtypedarray.getcolor(r.styleable.customletternavigationview_custombackgroundcolordown, mbackgroundcolor); mdowncontenttextcolor = mtypedarray.getcolor(r.styleable.customletternavigationview_customtextcolordown, mdowncontenttextcolor); mcontenttextsize = mtypedarray.getdimension(r.styleable.customletternavigationview_customtextsize, mcontenttextsize); mcontentdiv = mtypedarray.getfloat(r.styleable.customletternavigationview_customletterdivheight, mcontentdiv); mbackgroundangle = mtypedarray.getint(r.styleable.customletternavigationview_custombackgroundangle, mbackgroundangle); mtypedarray.recycle(); } /** * 获取内容长度 * * @return 内容长度 */ private int getcontentlength() { if (mnavigationcontent != null) { return mnavigationcontent.length; } return 0; } /** * 滑动计算 * * @param meventy y轴滑动距离 */ private void scrollcount(float meventy) { //滑动的时候利用滑动距离和每一个字符高度进行取整,获取到index rect mrect = measuretextsize(); int index = (int) ((meventy - getpaddingtop() - getpaddingbottom() - mcontentdiv * 2) / (mrect.height() + mcontentdiv)); //防止越界 if (index >= 0 && index < getcontentlength()) { string newletter = mnavigationcontent[index]; //防止重复触发回调 if (!mcurrentletter.equals(newletter)) { mcurrentletter = newletter; if (monnavigationscrollerlistener != null) { monnavigationscrollerlistener.onscroll(mcurrentletter, index); } } } } /** * 测量文字的尺寸 * * @return 一个汉字的矩阵 */ public rect measuretextsize() { rect mrect = new rect(); if (mtextpaint != null) { mtextpaint.gettextbounds("田", 0, 1, mrect); } return mrect; } /** * 设置导航栏滑动监听 * * @param onnavigationscrollerlistener */ public void setonnavigationscrollerlistener(onnavigationscrollerlistener onnavigationscrollerlistener) { this.monnavigationscrollerlistener = onnavigationscrollerlistener; } /** * 设置导航栏显示内容 * * @param content 需要显示的内容 */ public void setnavigationcontent(string content) { if (!textutils.isempty(content)) { mnavigationcontent = null; mnavigationcontent = new string[content.length()]; for (int i = 0; i < content.length(); i++) { mnavigationcontent[i] = string.valueof(content.charat(i)); } } //需要重新测量 requestlayout(); } public interface onnavigationscrollerlistener { //按下 void ondown(); //滑动 void onscroll(string letter, int position); //离开 void onup(); } }
自定义属性
<declare-styleable name="customletternavigationview"> <attr name="customtextcolordefault" format="color" /> <attr name="customtextcolordown" format="color" /> <attr name="custombackgroundcolordown" format="color" /> <attr name="customletterdivheight" format="dimension" /> <attr name="customtextsize" format="dimension" /> <attr name="custombackgroundangle" format="integer" /> </declare-styleable>
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。