Giter Site home page Giter Site logo

finemoodnote's Introduction

随心记中自定义的View:

一、 CircleProgressView

在这个项目里,根据需求自定义了一些View,下面这个是一个自定义圆形进度条、先上一波图:

效果分析:

1、一个背景圆,一个弧度不固定的弧表示进度

2、圆背景灰色,弧形颜色随进度的增加改变

3、圆环类部用文字实时显示进度的百分比

4、环与文字的颜色随着进度的增加而改变

5、进度加上一个动画显示,在一定时间类增加到最终进度

5、监听焦点变化,从失去焦点到重新获取焦点,就重新开始显示动画

代码:

一、测量宽高

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  	//设置控件的宽高相等
    width = getMeasuredWidth();
    setMeasuredDimension(width,width);
  	//文字的大小
    mTextSize = width*1/3;
  	//圆的半径
    mRadio = width/2-20;

}

二、画图

//画字体
Paint mTextPaint = new Paint();
Typeface font = Typeface.create("微软雅黑",Typeface.BOLD);
mTextPaint.setColor(mProgress_color);
mTextPaint.setTypeface(font);
mTextPaint.setTextSize(mTextSize);
mTextPaint.setTextAlign(Paint.Align.LEFT);
mTextPaint.setAntiAlias(true);
//获取字体的高度
Paint.FontMetrics fm = mTextPaint.getFontMetrics();
float textHeight = fm.bottom - fm.top;
//获取字体的宽度
Rect bound = new Rect();
mTextPaint.getTextBounds(mShowText,0,mShowText.length(),bound);
int textBoundsWidth = bound.width();
//开始画字体
canvas.drawText(mShowText, (getMeasuredWidth() - textBoundsWidth)/2
        ,(getMeasuredHeight() + textHeight/2)/2  ,mTextPaint);

//画背景环形
Paint circlePaint = new Paint();
circlePaint.setStrokeWidth(mStrokeWidth);
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint.setColor(ContextCompat.getColor(mContext,R.color.colorTextNormal));
circlePaint.setAntiAlias(true);
canvas.drawCircle(width/2,width/2,mRadio,circlePaint);
//画进度弧
Paint progressPaint = new Paint();
progressPaint.setColor(mProgress_color);
progressPaint.setStyle(Paint.Style.STROKE);
progressPaint.setStrokeWidth(mStrokeWidth);
progressPaint.setAntiAlias(true);

if (mSweepAngle <= mProgressAngle) {
  	//动画效果
    canvas.drawArc(new RectF(20,20,width-20,width-20), mStartAngle, mSweepAngle,false,progressPaint);
    invalidate();
}

四、属性动画控制进度和画笔颜色

ValueAnimator animator = ValueAnimator.ofFloat(0f,1);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator valueAnimator) {
        float value = (float) valueAnimator.getAnimatedValue();
      //弧度缓慢增加到最终进度
        mSweepAngle = mProgressAngle*value;
      //颜色也随子变化 (由红到绿)
        mProgress_color = Color.argb(255
                ,(int)(255*(1-mProgress*value/100))
                ,(int)(255*(mProgress*value/100))
                ,0);
      //文字也随之变化
        mShowText = (int)(mProgress*value) + "%";
    }
});
animator.setInterpolator(new FastOutSlowInInterpolator());
animator.setDuration(2000);
animator.start();

五、需要用到的自己设置的属性,我这里谢了进度与环的宽度,其他的比如说背景圆的颜色等也可以自己设置

TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CircleProgressView);
mProgress = ta.getInt(R.styleable.CircleProgressView_progress,0);
//环的宽度
mStrokeWidth  = ta.getInt(R.styleable.CircleProgressView_strokeWidth,35);

最终在布局中就可以用了

<baoming.views.CircleProgressView
    android:id="@+id/progressView"
    android:layout_width="230dp"
    android:layout_height="wrap_content"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="10dp"
    app:progress="80" />

六、很多时候要动态的设置进度直接加上

public void setProgress(int progress){
    mSweepAngle = 0;
    this.mProgress = progress;
    invalidate();
    }

七、焦点监听

public void setFocusChanges() {
    final int tempProgress  = mProgress;
  	//设置主动获取焦点
    setFocusableInTouchMode(true);
    setFocusable(true);
    requestFocusFromTouch();
    requestFocus();
  	//添加监听
    setOnFocusChangeListener(new OnFocusChangeListener() {
        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            if (hasFocus){
              //获取焦点时进度为当前进度开始动画
                setProgress(tempProgress);
            }else {
               //失去焦点时设置进度为0
                setProgress(0);
            }
        }
    });
}

代码里的引用就是这样了

CircleProgressView progressView = (CircleProgressView) v.findViewById(R.id.progressView);
progressView.setProgress(80);
progressView.setFocusChanges();

二、TimeKeeperView

备忘录需要一个计时器,勉强写了一个计时器的View

还是先看看效果图:

一、功能分析

1、背景是一个灰色的像时钟刻度组成的环

2、还是有表示进度的弧(也是由类似时钟的刻度组成的)

3、里面也有随进度变化的文字

4、最下面有一个旋转动画的图片

5、当倒计时完成的时候改变了View的显示

6、完成时的View显示为一张做摆动动画的图片

7、重整个View 的中心画4个半径的等差关系并且逐渐变大(动画效果)的圆,画笔设置一样的透明度。就成了水波纹效果啦。(很丑还在改进中....)

好了直接上代码:

先看看onDraw()里的代码:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //画布抗锯齿
    canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));
    if (isFinished) {
        drawFinish(canvas);
    } else {
        drawBg(canvas);
        drawProgress(canvas);
        drawTime(canvas);
    }
}

主要是根据进度的状态作画,没有完成就画背景的刻度环、进度的刻度弧、显示进度的文字;计时完成就直接画完成后的状态,也就是水波纹哪个。总的控制就是一个boolean类型的isFinished。

然后就是画画了!谁怕谁

画刻度,这个还是很好理解的 效果是相当于把一个环分成了N等分,但是怎么做呢?

//画背景刻度
private void drawBg(Canvas canvas) {
    Paint scalePaintBg = new Paint();
    scalePaintBg.setAntiAlias(true);
    scalePaintBg.setStyle(Paint.Style.STROKE);
    scalePaintBg.setStrokeWidth(5);
    scalePaintBg.setColor(ContextCompat.getColor(mContext, R.color.colorTextNormal));
  //关键逻辑是每个刻度看成是两个圆点相同,半径不同的圆,每隔一段角度 用大圆的半径减去小圆的半径,一圈后就得到了
  //一圈的刻度,实际上就是按照这些路径画N条直线就形成了环形刻度效果。
  //这样的就用到了正余弦函数,通过大圆半径、小圆半径、和偏移角度,来计算每条刻度的开始坐标和终点坐标
  // for循环,划一圈
    for (double angle = pi; angle < 3 * pi; angle += (2 * pi / 100)) {
        float startX = (float) (mHeight / 2 - mSmallRadio * Math.sin(angle));
        float startY = (float) (mHeight / 2 + mSmallRadio * Math.cos(angle));
        float endX = (float) (mHeight / 2 - mLargeRadio * Math.sin(angle));
        float endY = (float) (mHeight / 2 + mLargeRadio * Math.cos(angle));
        canvas.drawLine(startX, startY, endX, endY, scalePaintBg);
    }
}

背景刻度完成、进度的刻度也一样

//画进度
private void drawProgress(Canvas canvas) {
    Paint scalePaintPro = new Paint();
    scalePaintPro.setAntiAlias(true);
    scalePaintPro.setStyle(Paint.Style.STROKE);
    scalePaintPro.setStrokeWidth(5);
    scalePaintPro.setColor(ContextCompat.getColor(mContext, R.color.colorAccent));
  	//这个弧度就表示进度了
    double sweepAngle = pi + 2 * pi * mProgress / 100;
    for (double angle = pi; angle < sweepAngle; angle += (2 * pi / 100)) {
        float startX = (float) (mHeight / 2 - mSmallRadio * Math.sin(angle));
        float startY = (float) (mHeight / 2 + mSmallRadio * Math.cos(angle));
        float endX = (float) (mHeight / 2 - mLargeRadio * Math.sin(angle));
        float endY = (float) (mHeight / 2 + mLargeRadio * Math.cos(angle));
        canvas.drawLine(startX, startY, endX, endY, scalePaintPro);
    }
}

然后就是画计时的文字:

//画时间
private void drawTime(Canvas canvas) {
    Paint timePaint = new Paint();
    timePaint.setColor(ContextCompat.getColor(mContext, android.R.color.black));
    Typeface font = Typeface.create("微软雅黑", Typeface.BOLD);
    timePaint.setTypeface(font);
    timePaint.setTextAlign(Paint.Align.LEFT);
    //获取字体的高度
    Paint.FontMetrics fm = timePaint.getFontMetrics();
    float textHeight = fm.bottom - fm.top;
    //获取字体的宽度
    Rect bound = new Rect();
  //这里我设置了根据计时,是否超过一小时的显示状态,超过一小时就画“小时:分钟:秒钟”,没有超过就画“分钟:秒钟”
    switch (TIME_STATE) {
        case WITH_HOUR:
            timePaint.setTextSize(mHeight / 8);
            break;
        case WITHOUT_HOUR:
            timePaint.setTextSize(mHeight / 7);
            break;
    }
    timePaint.getTextBounds(mTimeText, 0, mTimeText.length(), bound);
    int textBoundsWidth = bound.width();
    //开始画,mTimeText通过set方法传入
    canvas.drawText(mTimeText, (getMeasuredWidth() - textBoundsWidth) / 2
            , (getMeasuredHeight() + textHeight / 2) / 2, timePaint);
}

完成时画的水波纹,有点简陋,将就一下

//画计时完成的水波纹背景
private void drawFinish(Canvas canvas) {
    Paint finishPaint = new Paint();
    finishPaint.setStyle(Paint.Style.FILL);
    finishPaint.setColor(ContextCompat.getColor(mContext, R.color.color_green));
    int x = mHeight / 2;
    int y = mHeight / 2;
 	 //设置透明度
    finishPaint.setAlpha(60);
  //开始画圆
    canvas.drawCircle(x, y, getChildAt(0).getWidth() / 2, finishPaint);
    if (mRadius_1>getChildAt(0).getWidth() / 2) {
        canvas.drawCircle(x, y, mRadius_1, finishPaint);
    }
    if (mRadius_1>getChildAt(0).getWidth() / 2) {
        canvas.drawCircle(x, y, mRadius_2, finishPaint);
    }
    if(mRadius_1>getChildAt(0).getWidth() / 2) {
        canvas.drawCircle(x, y, mRadius_3, finishPaint);
    }
    if(mRadius_1>getChildAt(0).getWidth() / 2) {
        canvas.drawCircle(x, y, mRadius_4, finishPaint);
    }
}

这四个圆的半径关系,以及变化的动画就直接设置在下面了

private void setRadius() {
    ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float value = (float) animation.getAnimatedValue();
            mRadius_1 = (int) ((mHeight / 2) * value);
            mRadius_2 = mRadius_1 - mHeight / 8;
            if (mRadius_1 > mHeight / 8) {
            }
            mRadius_3 = mRadius_2 - mHeight / 8;
            if (mRadius_2 > mHeight / 8) {
            }
            mRadius_4 = mRadius_3 - mHeight / 8;
            if (mRadius_3 > mHeight / 8) {
            }
            invalidate();
        }
    });
    animator.setRepeatCount(ValueAnimator.INFINITE);
    animator.setDuration(2000);
    animator.setInterpolator(new LinearInterpolator());
    animator.start();
}

主要的显示就是上面这些了,如何计时,我是用的RxJava异步实现的具体操作如下

public void startKeepTime() {
  //需要做一下毫秒的转换
    Integer ss = 1000;
    Integer mi = ss * 60;
    Integer hh = mi * 60;
    mTotalTime = Integer.valueOf(hour) * hh
            + Integer.valueOf(min) * mi + Integer.valueOf(second) * ss;
    Observable.create(new ObservableOnSubscribe<Long>() {
        @Override
        public void subscribe(ObservableEmitter<Long> e) throws Exception {
            long currentTime = mTotalTime;
            while (currentTime > 0) {
                Thread.sleep(1000);
             	//递减
                currentTime -= 1000;
                e.onNext(currentTime);
            }
        }
    }).subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Consumer<Long>() {
                @Override
                public void accept(Long second) throws Exception {
                  //接收到的时间为0的时候就表示完成了  
                  if (second == 0) {
                    //完成标记
                        isFinished = true;
                        if (getChildCount()!=0){
                          //移除计时的ImageView
                            removeAllViews();
                        }
                     //添加完成时闹铃的ImageView
                        initFinishImg();
                    //设置水波纹动画4个View的半径
                        setRadius();
                    }
                  //计算进度
                    mProgress = (second * 100 / mTotalTime);
                  //更新计时的文字
                    mTimeText = DateUtil.getTime(second);
                    invalidate();
                }
            });
}

当然还是有很多需要修改的地方,现在先记录下思路..继续码代码去了!

finemoodnote's People

Contributors

telyo avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.