前阵子又想回忆回忆自定义View,发现自己好久不写都忘记了,或者说根本没记住!(逃
言归正传,通过HenCoder的学习,又对自定义View加深了下印象,自己再写一个简单的View。先看效果图:
效果图看起来蛮有意思的,那就简单分析下。
实现思路
分析元素和效果:
元素:有字(废话),有。。。没了
效果:最后大于小于9的会加一位,并且往上走(其实都是假象)
如果是9变成10 会全部往上走,同理1999也是一样会被2000顶掉
效果实现:
既然是动起来了,那我们就用动画吧,既然用动画了,那就用属性动画吧。
属性动画用在哪呢?
当然是最后变化的那个位置了,我们知道一旦属性动画开始可以让视图不断的执行onDraw方法,最后变成我们想要的位置,也就是9--》10 9在10上面一个看不见的地方,原本呢是10在9下面看不见的位置。
那就开始写LikeView吧
自定义属性:
给个数字不过分吧。。
给个字体颜色不过分吧。。
给个字体大小也不能说过分吧。。
重写onDraw方法(这里就拿1234--》1235变化来说明)
- 画1234,当然不能一个drawText画出来,一个drawText画123,再在后面drawText画4 给4赋上属性动画的改变量yOffset。
- 在1234的下面看不见的位置画一个5,当然是(4的x坐标) 和 (y + 整个视图的高度),y当然是基线了。
- 通过ObjectAnimator设置yOffset就可以让4和5同时移动了形成顶起的假象
主要代码就在onDraw:
@Override
protected void onDraw(Canvas canvas) {
//测量文字大小
float likeNumStrWidth = mNumPaint.measureText(mLikeNumStr);
float correctNumStrWidth = mNumPaint.measureText(mCorrectNumStr);
//确定基线
Paint.FontMetricsInt fontMetricsInt = mNumPaint.getFontMetricsInt();
int centerX = getWidth() / 2;
//文字高度的一半到基线的距离
//top表示基线到文字最上面的位置的距离 是个负值 bottom为基线到最下面的距离,为正值
int dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
int bastLine = getHeight() / 2 + dy;
//index位置后的文字需要变化
float index;
//变化的text
String changeText;
//查找是哪个位置后都是需要改变,比如9999就会返回0,8就返回1,如果是1234返回-1,12399返回3.
int theFirstNotNine = findTheFirstNotNine(mLikeNum);
//每个字符的宽度
float everyCharWidth = mNumPaint.measureText(mLikeNumStr) / mLikeNumStr.length();
if (theFirstNotNine == NO_NINE) {
//没有9证明只需要变化最后一位即可
index = everyCharWidth * (mLikeNumStr.length() - 1);
changeText = mCorrectNumStr.substring(mCorrectNumStr.length() - 1, mCorrectNumStr.length());
canvas.drawText(mLikeNumStr, 0, mLikeNumStr.length() - 1, centerX - likeNumStrWidth / 2, bastLine, mNumPaint);
canvas.drawText(mLikeNumStr, mLikeNumStr.length() - 1, mLikeNumStr.length(), centerX + index- likeNumStrWidth / 2, bastLine + mYOffset, mNumPaint);
} else if (theFirstNotNine > 1) {
//大于1 说明变化的地方在中间位置
index = everyCharWidth * (theFirstNotNine - 1);
//需要变化的文字
changeText = mCorrectNumStr.substring(theFirstNotNine - 1, mCorrectNumStr.length());
//画左半部分不变的部分
canvas.drawText(mLikeNumStr, 0, theFirstNotNine - 1, centerX - likeNumStrWidth / 2, bastLine, mNumPaint);
//画右边变化的部分
canvas.drawText(mLikeNumStr, theFirstNotNine - 1, mLikeNumStr.length(), centerX + index - likeNumStrWidth / 2, bastLine + mYOffset, mNumPaint);
} else {
//说明全部需要变化,包括9999,1999,9
index = 0;
changeText = mCorrectNumStr;
canvas.drawText(mLikeNumStr, centerX - likeNumStrWidth / 2, bastLine + mYOffset, mNumPaint);
}
//把变化的未显示的画出来,如果mYOffset改变为-getHeight()就会显示,原来的就会不显示
canvas.drawText(changeText, centerX + index - correctNumStrWidth / 2, bastLine + mYOffset + getHeight(), mNumPaint);
}
次要代码就在onMeasure,解决wrap_content为match_parent情况。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int mayBeWidth = (int) (mNumPaint.measureText(mCorrectNumStr) + getPaddingLeft() + getPaddingRight());
mWidth = (int) (mNumPaint.measureText(mLikeNumStr) + getPaddingLeft() + getPaddingRight());
//这里有可能原来是9,加1后长度更长,做了下适配,选择更长的当作mWidth
mWidth = Math.max(mWidth, mayBeWidth);
mHeight = (int) (mNumSize + getPaddingTop() + getPaddingBottom());
mWidth = Math.max(mWidth, getSuggestedMinimumWidth());
if (widthMode == AT_MOST && heightMode == AT_MOST) {
//设置默认宽高
setMeasuredDimension(mWidth, mHeight);
} else if (widthMode == AT_MOST) {
setMeasuredDimension(mWidth, heightSize);
} else if (heightMode == AT_MOST) {
setMeasuredDimension(widthSize, mHeight);
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
主要提供思想。
实际源码这里
哪有写的不好的请帮我指正帮我提高。
网友评论