Android自定义View自定义view

自定义View - 仿QQ运动步数进度效果

2018-07-06  本文已影响3人  Peakmain

我blog也写过,欢迎各位看我博客,https://blog.csdn.net/qq_24675479
首先看下效果图

效果图.gif

在做这个项目之前先了解下文字获取
我上一篇 自定义View入门 - 自定义TextView 有写过——https://www.jianshu.com/p/7b5e4f5abb74

分析.png

今天详细讲解一下baseLine 基线(参考文章:文淑大神的自定义View之绘图篇(四)http://blog.csdn.net/u012551350/article/details/51361778
FontMetrics

基线.png

top:可绘制的最高高度所在线
bottom:可绘制的最低高度所在线
ascent :系统建议的,绘制单个字符时,字符应当的最高高度所在线
descent:系统建议的,绘制单个字符时,字符应当的最低高度所在线

**获取实例**

Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
Paint.FontMetricsInt fm= mPaint.getFontMetricsInt();

**成员变量**

    float ascent = fontMetrics.ascent;
    float descent = fontMetrics.descent;
    float top = fontMetrics.top;
    float bottom = fontMetrics.bottom;
    float leading = fontMetrics.leading;
这里的ascent,descent,top,bottom,leading指的是到基线baseline的位置

[图片上传失败...(image-4386fb-1530855738868)]

**文字的高度**

     float top = fontMetrics.top + baseLineY;
    float bottom = fontMetrics.bottom + baseLineY;
    //文字高度
    float height= bottom - top; //注意top为负数
    //文字中点y坐标
    float center = (bottom - top) / 2;
>即中线是(bottom-top)/2,实际呢是bottom+top只是因为这里的top是负的(图片上的top到baseline的距离)

**已知中线求baseline**
[图片上传失败...(image-9ea4bc-1530855738868)]
**结论**
baseline=centerY+A-fm.bottom;
>centerY呢实际就是getHeight/2,整体高度的一半,然后求基线的y坐标,实际就是(top-bottom)/2-fontMetrics.bottom;再次强调:**这里的ascent,descent,top,bottom,leading指的是到基线baseline的位置。**最后相加getHeight+(top-bottom)/2-fontMetrics.bottom**

###接下来开始做项目:

 - 第一步自定义属性
一共需要:内部圆环颜色,外部圆环颜色,圆环宽度,文字颜色,文字大小五种

<declare-styleable name="QQStepView">
<attr name="outerColor" format="color" />
<attr name="innerColor" format="color" />
<attr name="borderWidth" format="dimension" />
<attr name="stepTextSize" format="dimension" />
<attr name="stepTextColor" format="color" />
</declare-styleable>


 - 自定义view获取属性,设置属性等

public class QQStepView extends View {
private int mOuterColor = Color.RED;
private int mInnerColor = Color.BLUE;
private int mBorderWidth = 20;
private int mStepTextSize;
private int mStepTextColor;
private Paint mOuterPaint, mInnerPaint, mTextPaint;
private int mStepMax;//总的步数
private int mCurrentStep;//当前步数

public QQStepView(Context context) {
    this(context, null);
}

public QQStepView(Context context, @Nullable AttributeSet attrs) {
    this(context, attrs, 0);
}

public QQStepView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.QQStepView);
    mOuterColor = ta.getColor(R.styleable.QQStepView_outerColor, mOuterColor);
    mInnerColor = ta.getColor(R.styleable.QQStepView_innerColor, mInnerColor);
    mBorderWidth = (int) ta.getDimension(R.styleable.QQStepView_borderWidth, mBorderWidth);
    mStepTextSize = ta.getDimensionPixelOffset(R.styleable.QQStepView_stepTextSize, mStepTextSize);
    mStepTextColor = ta.getColor(R.styleable.QQStepView_stepTextColor, mStepTextColor);
    ta.recycle();
    //内弧
    mOuterPaint = new Paint();
    mOuterPaint.setAntiAlias(true);
    mOuterPaint.setStrokeWidth(mBorderWidth);
    mOuterPaint.setColor(mOuterColor);
    mOuterPaint.setStrokeCap(Paint.Cap.ROUND);//设置下方为圆形
    mOuterPaint.setStyle(Paint.Style.STROKE);//设置内部为空心
    //外弧
    mInnerPaint = new Paint();
    mInnerPaint.setAntiAlias(true);
    mInnerPaint.setStrokeWidth(mBorderWidth);
    mInnerPaint.setColor(mInnerColor);
    mInnerPaint.setStrokeCap(Paint.Cap.ROUND);//设置下方为圆形
    mInnerPaint.setStyle(Paint.Style.STROKE);//设置内部为空心

    //文字
    mTextPaint = new Paint();
    mTextPaint.setAntiAlias(true);
    mTextPaint.setTextSize(mStepTextSize);
    mTextPaint.setColor(mStepTextColor);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    //调用者在布局文件中可能 wrap_content导致宽高不一致
    //确保是正方形
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = MeasureSpec.getSize(heightMeasureSpec);
    setMeasuredDimension(width > height ? height : width, width > height ? height : width);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //1.画外圆弧   边缘没有显示完整
    //RectF recf = new RectF(0, 0, getWidth(), getHeight());

    int center = getWidth() / 2;
    int radius = getWidth() / 2 - mBorderWidth / 2;
    //mBorderWidth/2,mBorderWidth/2,getWidth()-mBorderWidth/2,getWidth()-mBorderWidth/2
    RectF recf = new RectF(center - radius,
            center - radius,
            center + radius,
            center + radius);
    canvas.drawArc(recf, 135, 270, false, mOuterPaint);
    //2.绘制内圆弧
    float sweepAngle = (float) mCurrentStep / mStepMax;
    canvas.drawArc(recf, 135, sweepAngle * 270, false, mInnerPaint);

    //3.绘制文字
    String stepText = mCurrentStep + "";
    Rect rect = new Rect();
    mTextPaint.getTextBounds(stepText,0,stepText.length(),rect);
    int dx = getWidth() / 2 - rect.width() / 2;
    //第一种方式获取高度
    //int dy = getWidth() / 2 + rect.width()/2;
    //第二种表达方式获取高度
    Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt();
    //获取中心(fontMetrics.bottom - fontMetrics.top) / 2
    int dy = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
    int baseLine = getHeight() / 2 + dy;
    canvas.drawText(stepText, dx, baseLine, mTextPaint);

}
//其他,写几个方法让他动起来

public void setStepMax(int mStepMax) {
    this.mStepMax = mStepMax;
}

public void setCurrentStep(int mCurrentStep) {
    this.mCurrentStep = mCurrentStep;
    //不断绘制 onDraw()
    invalidate();
}

}

**在MainActivity中设置动画**

final QQStepView qqStepView = (QQStepView) findViewById(R.id.step_view);
qqStepView.setStepMax(5000);
//属性动画
ValueAnimator animator = ValueAnimator.ofFloat(0, 3000);//0到3000的变化
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float currentStep = (float) animation.getAnimatedValue();//获取当前值
qqStepView.setCurrentStep((int) currentStep);
}
});
animator.setDuration(2000);
animator.start();

上一篇下一篇

猜你喜欢

热点阅读