自定义View之歌词渐变文本控件

编程入门 行业动态 更新时间:2024-10-10 07:26:56

<a href=https://www.elefans.com/category/jswz/34/1771438.html style=自定义View之歌词渐变文本控件"/>

自定义View之歌词渐变文本控件

自定义view之歌词渐变文本控件

前言

本文是自定义view的练习,默认读者掌握了自定义view的知识
本文的参考文章

使用方法:

效果

实现

第一步吧我们要对外提供属性,让用户自由设置,这个一般是自定义view的老套路。在res下的values下新建的attrs.xml里定义好可以自定义的属性:

    <declare-styleable name="LyricTextView"><attr name="text" format="string" /><attr name="text_size" format="dimension" /><attr name="default_color" format="color|reference" /><attr name="changed_color" format="color|reference" /><attr name="progress" format="float" /><attr name="direction"><enum name="left" value="0" /><enum name="right" value="1" /></attr></declare-styleable>

以上属性分别是文本、文本大小、默认颜色、渐变颜色、渐变百分比、渐变方向。不多说啦。然后新建LyricTextView继承view,重写前三个构造,在构造里获取属性:

 public LyricTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);TypedArray t = context.obtainStyledAttributes(attrs, R.styleable.LyricTextView);text = t.getString(R.styleable.LyricTextView_text);if(text == null){text = "";}textSize = t.getDimension(R.styleable.LyricTextView_text_size, sp2px(16));defaultColor = t.getColor(R.styleable.LyricTextView_default_color, DEFAULT_COLOR);changeColor = t.getColor(R.styleable.LyricTextView_changed_color, CHANGED_COLOR);direction = t.getInt(R.styleable.LyricTextView_direction, LEFT);progress = t.getFloat(R.styleable.LyricTextView_progress,0);t.recycle();initPaint();measureText();}

可以看到首先是获取了xml里自定义好的属性,设置给对应变量。然后这个构造方法里还调用了两个方法initPaint(); measureText();
一个是用来初始化画笔的,一个是用来测量文本宽高的。代码如下。

private void initPaint() {paint = new Paint(Paint.ANTI_ALIAS_FLAG);paint.setTextSize(textSize); //必须在measureText前设置}/*** 测量内容文本的宽高,当改变textSize时应重新调用此方法*/private void measureText() {Rect r = new Rect();paint.getTextBounds(text, 0, text.length(), r);//一个坑:rect.width(或者说rect.right-rect.left)得到的值会比实际字长度小一点点,因此这里使用paint.measureText方法获取宽度textHeight = r.bottom - r.top;textWidth = (int) paint.measureText(text, 0, text.length());}

在初始化画笔时,首先要给paint设置好字体大小,因为后面measureText里调用的方法都是在文字大小设置好的前提下获取的才是正确的值。
paint.getTextBounds是测量文字的宽高值然后放入rect里,但是实测发现一个坑:宽度会比实际宽度小一点,这会导致后面颜色渐变到100%时还有一丢丢字体没变色。因此获取文字宽度使用了paint.measureText方法。

接下来是onMeasure方法。先看代码。

 @Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);width = measure(widthMeasureSpec, true);height = measure(heightMeasureSpec, false);setMeasuredDimension(width, height);}private int measure(int measureSpec, boolean isWidth) {int mode = MeasureSpec.getMode(measureSpec);int size = MeasureSpec.getSize(measureSpec);switch (mode) {case MeasureSpec.EXACTLY:break;case MeasureSpec.AT_MOST:case MeasureSpec.UNSPECIFIED:if (isWidth) {size = textWidth;} else {size = textHeight;}break;}return isWidth ? (size + getPaddingLeft() + getPaddingRight()) : (size + getPaddingTop() + getPaddingBottom());}

如果设定了确定值(EXACTLY),那就设置之,如果没设,则控件大小就是文本的大小。

好了,核心部分onDraw来了.
本控件其实知识点就一个,怎么实现字体渐变?
答:画布canvas有一个方法canvas.clipRect(),调用了这个方法后接下来只会在这个区域内画内容,超出这个区域的内容就不画了。那么对于我们歌词渐变,我们先用默认颜色画出全部文本,然后呢,根据变量progress(渐变比例,范围[0,1])和方向direction(确定从左到右渐变还是从右到左)计算出要变色的区域,然后用渐变颜色再画一次文本即可。

先放上代码。

 @Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);drawText(canvas, direction, progress);}private void drawText(Canvas canvas, int direction, float progress) {int startX;int endX;int realWidth = (getMeasuredWidth() - getPaddingLeft() - getPaddingRight());int realHeight = (getMeasuredHeight() - getPaddingTop() - getPaddingBottom());int textLeft = getPaddingLeft() + realWidth / 2 - textWidth / 2;   //文本在控件中的起始x位置int textRight = getPaddingLeft() + realWidth / 2 + textWidth / 2;   // 文本在控件中的结束x位置int textBottom = getPaddingTop() + realHeight / 2 + textHeight / 2;  //文本在控件中的结束y位置if(progress < 0 ){progress = 0;}if(progress > 1 ){progress = 1;}int changedWidth = (int) (textWidth * progress);if (direction == LEFT) {startX = textLeft;endX = textLeft + changedWidth;} else {startX = textRight - changedWidth;endX = textRight;}//画正常的文字内容paint.setTextSize(textSize);Paint.FontMetrics fontMetrics = paint.getFontMetrics();canvas.save();paint.setColor(defaultColor);canvas.drawText(text, textLeft, textBottom, paint);canvas.restore();//画渐变部分的文字canvas.save(Canvas.CLIP_SAVE_FLAG);paint.setColor(changeColor);canvas.clipRect(startX, 0, endX, getMeasuredHeight());canvas.drawText(text, textLeft, textBottom, paint);canvas.restore();}

哇,贼简单。一些位置的计算,细心读一下应该能懂。如果你没理解,那可以去看看开头的参考文章,咱们的鸿洋大大写的。

小优化

我们先直接看看目前的效果吧:

   <com.example.lyrictextview.LyricTextViewandroid:id="@+id/lyric"android:layout_width="match_parent"android:layout_height="60dp"android:background="#44000000"app:changed_color="#ff0000"app:default_color="#000000"app:direction="left"app:progress="0.7"app:text="按时大大的飒飒的"app:text_size="26sp" />

破费特,效果可以
那我们在用wrap_content看看。

  <com.example.lyrictextview.LyricTextViewandroid:id="@+id/lyric"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="#44000000"app:changed_color="#ff0000"app:default_color="#000000"app:direction="left"app:progress="0.7"app:text="按时大大的飒飒的"app:text_size="26sp" />

好了,问题来了,文字下面有一小部分没显示呀。
对于这个问题,看图(图片来自他人博客):


上图是android中文本绘制的各种线,我们平时绘制一个文本时呢,就是从baseLine最左端开始绘制的(上图中红点)。关于这个文本绘制的细节,已经有很多完整分析的文章了。
看上面这张图,字母g、j、p都是会超出baseLine的,这也就是为什么当我们宽高为wrap_content时文字下面一小部分不显示的原因了。
解决方法1:如果你不需要非常精细,直接给控件设置个默认padding就可以,就几个dp差不多就能把底部显示出来。
解决方法2:
1)修改measureText:

private void measureText() {Rect r = new Rect();paint.getTextBounds(text, 0, text.length(), r);//一个坑:rect.width会比实际字长度小一点点
//        textHeight = r.bottom - r.top;Paint.FontMetrics fontMetrics = paint.getFontMetrics();textHeight = (int) (-fontMetrics.ascent + fontMetrics.descent);textWidth = (int) paint.measureText(text, 0, text.length());}

Paint.FontMetric里包含了文本绘制的各种线。因为绘制基线是baseLine,基线为0,坐标轴向下为正,故在其之上的是负数。
2)修改onDraw绘制部分:

//画正常的文字内容paint.setTextSize(textSize);Paint.FontMetrics fontMetrics = paint.getFontMetrics();canvas.save();paint.setColor(defaultColor);
//        canvas.drawText(text, textLeft, textBottom, paint);canvas.drawText(text, textLeft, textBottom - fontMetrics.descent, paint);canvas.restore();//画渐变部分的文字canvas.save(Canvas.CLIP_SAVE_FLAG);paint.setColor(changeColor);canvas.clipRect(startX, 0, endX, getMeasuredHeight());
//        canvas.drawText(text, textLeft, textBottom , paint);canvas.drawText(text, textLeft, textBottom - fontMetrics.descent, paint);canvas.restore();

好了,就是绘制文字时向上偏移一定距离,即fontMetrics.descent
这样,当控件宽高为wrap_content时文字也能显示完整啦

最后再贴上对外公开的setter、getter方法:

/以下settre getter供外部设置属性,别忘记invalidate();//ps:若要使用属性动画控制progress,前提得有progress的setter getterpublic void setProgress(float progress) {this.progress = progress;invalidate();}public float getProgress() {return progress;}public void setTextSize(float size) {textSize = size;initPaint();measureText();requestLayout();//wrap_content情况下文字大小改变后需重新onMeausreinvalidate();}public float getTextSize() {return textSize;}public int getDirection() {return direction;}public void setDirection(int direction) {this.direction = direction;invalidate();}public String getText() {return text;}public void setText(String text) {this.text = text;requestLayout();  //wrap_content情况下文字长度改变后需重新onMeausreinvalidate();}public int getDefaultColor() {return defaultColor;}public void setDefaultColor(int defaultColor) {this.defaultColor = defaultColor;invalidate();}public void setAll(float progress, String text, float textSize, int defaultColor, int changeColor, int direction) {this.progress = progress;this.text = text;this.textSize = textSize;this.defaultColor = defaultColor;this.changeColor = changeColor;this.direction = direction == LEFT ? LEFT : RIGHT;initPaint();measureText();requestLayout();invalidate();}public int getChangeColor() {return changeColor;}public void setChangeColor(int changeColor) {this.changeColor = changeColor;invalidate();}//工具private float dp2px(int dp) {return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());}private float sp2px(int sp) {return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());}

这是一个简单的小控件,写它主要是打算写一个仿今日头条指示器的控件~过些天应该会写吧~~~
源码

更多推荐

自定义View之歌词渐变文本控件

本文发布于:2024-02-25 18:04:17,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1699844.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:自定义   控件   文本   歌词   View

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!