android 自定义view之水波球

最近项目中需要实现动态水波球的效果,在网上看了一些都不太理想。就自己简单写了一个ProgressCircleView,期待大神拍砖!我这里继承的TextView,主要是方便在中间显示当前值。


首先有图有真相:

3.0及其之后版本的效果                  3.0之前版本的效果

效果差异主要取决于如下的代码,因为3.0以后google默认开启了硬件加速导致canvas.clipPath(path);没有效果,所以在这里只能先区分处理。高版本的画了一个前景来处理,如有更好的办法还请指教。

protected void onDraw(Canvas canvas) {
		
		if (mHeight == 0) {
			// 初始化
			init();
			// 开始波动
			start();
		}
		
		if (android.os.Build.VERSION.SDK_INT > 10) {
			// 3.0 或更高版本
			drawForegroundCircle(canvas);
		} else {
			// 3.0以下版本
			clipCircle(canvas);
		}
		
		
		super.onDraw(canvas);
	}


主要的是这个方法用Path.rQuadTo去画赛贝尔曲线(Path.rQuadTo和Path.quadTo的区别网上有很多这里就不再赘述了):

/**
	 * 画出水波线
	 * */
	private Path dealPath() {
		Path path = new Path();
		path.moveTo(mStartX, mStartY);
		
		for (int i = 0 ; i < mXs.length ; i++) {
			path.rQuadTo(mXs[i], mYs[i], mXs[i]<<1, 0);
			
		}
		path.rQuadTo(mXs[0], mYs[0], mXs[0]<<1, 0);
		path.rQuadTo(mXs[1], mYs[1], mXs[1]<<1, 0);
		path.lineTo(mWidth, mHeight);
		path.lineTo(0, mHeight);
		path.lineTo(mStartX, mStartY);
		path.close();
		
		return path;
	}

在Activity中使用的方法:

pcv0 = (ProgressCircleView) findViewById(R.id.progressCircleView4);
        
        pcv0.setProgress(0.5f);
        pcv0.setText("50%");

已下是ProgressCircleView的完整代码:

/**
 * 水波纹进度球
 * */
public class ProgressCircleView extends TextView {
	
	private int mHeight;	//控件宽高
	private int mWidth;	//
	
	private int mStartX;	//起始位置坐标
	private int mStartY;	//
	
	private Paint mPaint;	// 边框画笔
	private Paint mQuadPaint;	// 水波纹画笔
	private Paint mForePaint;	// 前景色画笔
	
	private int[] mXs;
	private int[] mYs;
	
	private int mStrokeWidth = 4;	// 边框宽度
	private int mTranslationUnit;	// 位移单位
	
	/** 水波纹总宽度 */
	private int mTotle;
	/** 当前进度值 */
	private float mProgress = 0.5f;
	/** 当前进度对应的颜色值 */
	private int mCurProgressColor = 0xFF00BE3C;
	/** 渐变颜色 */
	private int[] mProgressColors = new int[]{Color.RED, Color.BLUE, Color.CYAN, Color.GREEN};

	public ProgressCircleView(Context context) {
		super(context);
		
	}

	public ProgressCircleView(Context context, AttributeSet attrs) {
		super(context, attrs);
		
	}

	public ProgressCircleView(Context context, AttributeSet attrs,
			int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		
	}
	
	/**
	 * 设置进度值
	 * @param progress float 取值范围:0.1 - 1.0
	 * */
	public void setProgress(float progress) {
		this.mProgress = 1 - progress;
		mCurProgressColor = interpRectColor(mProgressColors, progress); // 得到当前色值
		
		mHeight = 0;
		invalidate();
	}
	
	/**
	 * 初始化 得到一些数据
	 * */
	private void init() {
		mHeight = getHeight();
		mWidth = getWidth();
		
		mStartX = mStrokeWidth;
		mStartY = (int) (mHeight * mProgress);
		
		mPaint = new Paint();
		mPaint.setAntiAlias(true);
		mPaint.setColor(Color.parseColor("#EEEEEE"));
		mPaint.setStyle(Paint.Style.FILL);//设置为空心
		
		mQuadPaint = new Paint();
		mQuadPaint.setAntiAlias(true);
		mQuadPaint.setColor(mCurProgressColor);
		mQuadPaint.setStyle(Paint.Style.FILL);//设置实心
		
		mForePaint = new Paint();
		mForePaint.setAntiAlias(true);
		mForePaint.setColor(Color.WHITE);
		mForePaint.setStyle(Paint.Style.FILL);//设置实心

		mPaint.setStrokeWidth(mStrokeWidth);
		
		
		
		// 波纹坐标
		mTotle = 0;
		float scale = mProgress > 0.5f ? (1 - mProgress) * 2 : mProgress * 2;
		float[] xFs = new float[]{0.35f, 0.2f, 0.45f, 0.6f};
		float[] yFs = new float[]{0.15f, -0.12f, 0.2f, -0.25f};
		mXs = new int[xFs.length];
		mYs = new int[yFs.length];
		for (int i = 0 ; i < xFs.length ; i++) {
			mXs[i] = (int) (mWidth * xFs[i]);
			mYs[i] = (int) (mHeight * yFs[i] * scale);
			
			mTotle += mXs[i];
		}
		mTotle += mXs[0];
		mTotle += mXs[1];
		
		mTranslationUnit = mWidth / 50;
	}
	
	@SuppressLint("DrawAllocation") @Override
	protected void onDraw(Canvas canvas) {
		
		if (mHeight == 0) {
			if (mWidth == 0) {
				// 开始波动
				start();
			}
			// 初始化
			init();
		}
		
		if (android.os.Build.VERSION.SDK_INT > 10) {
			// 3.0 或更高版本
			drawForegroundCircle(canvas);
		} else {
			// 3.0以下版本
			clipCircle(canvas);
		}
		
		
		super.onDraw(canvas);
	}
	
	private void clipCircle(Canvas canvas) {
		canvas.save();
		Path path = new Path();
		path.addCircle(mWidth / 2, mHeight / 2, mWidth / 2 - (mStrokeWidth>>1), Direction.CW);
		canvas.clipPath(path);
		canvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2, mPaint);
		canvas.drawPath(dealPath(), mQuadPaint);
//		canvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2 - mStrokeWidth, mPaint);
		canvas.restore();
	}
	
	private void drawForegroundCircle(Canvas canvas) {
		Path path = new Path();
        float radius = mWidth / 2f;
        float panding = mStrokeWidth / 2f;

        path = new Path();
        path.moveTo(0, radius);
        path.lineTo(0, 0);
        path.lineTo(mWidth, 0);
        path.lineTo(mWidth, mHeight);
        path.lineTo(0, mHeight);
        path.lineTo(0, radius);
        //圆弧左边中间起点是180,旋转360度
        path.arcTo(new RectF(panding, panding, mWidth - panding, mHeight - panding), 180, -359, true);
        path.close();
        
        canvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2, mPaint);
        canvas.drawPath(dealPath(), mQuadPaint);
//        canvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2 - mStrokeWidth, mPaint);
        canvas.drawPath(path, mForePaint);
	}
	
	/**
	 * 画出水波线
	 * */
	private Path dealPath() {
		Path path = new Path();
		path.moveTo(mStartX, mStartY);
		
		for (int i = 0 ; i < mXs.length ; i++) {
			path.rQuadTo(mXs[i], mYs[i], mXs[i]<<1, 0);
			
		}
		path.rQuadTo(mXs[0], mYs[0], mXs[0]<<1, 0);
		path.rQuadTo(mXs[1], mYs[1], mXs[1]<<1, 0);
		path.lineTo(mWidth, mHeight);
		path.lineTo(0, mHeight);
		path.lineTo(mStartX, mStartY);
		path.close();
		
		return path;
	}
	
	/**
	 * 开始波动效果
	 * */
	private void start() {
		mStartX -= mTranslationUnit;
		new Handler().postDelayed(new Runnable() {
			@Override
			public void run() {
				start();
			}
		}, 20);
		invalidate();
		if (-mStartX >= mTotle + mWidth + 1.2f * mStrokeWidth) {
			mStartX = mStrokeWidth;
		}
	}
	
	/** 
     * 获取渐变颜色 
     * @param colors 
     * @param x 
     * @return 
     */  
    private int interpRectColor(int[] colors, float p) {
    	if (p == 0) {
    		return colors[0];
    	}
    	
    	int size = colors.length - 1;
    	float cp, up;
        int a, r, g, b, cur;
        up = 1f / size;
        cur = (int) (p / up);
        cp = (p - cur * up) / up;
        if (cp == 0) {
        	cur--;
        	cp = 1;
        }
        
        a = ave(Color.alpha(colors[cur]), Color.alpha(colors[cur+1]), cp);  
        r = ave(Color.red(colors[cur]), Color.red(colors[cur+1]), cp);  
        g = ave(Color.green(colors[cur]), Color.green(colors[cur+1]), cp);  
        b = ave(Color.blue(colors[cur]), Color.blue(colors[cur+1]), cp);  
        return Color.argb(a, r, g, b);  
    } 
	
	private int ave(int s, int d, float p) {  
        return s + Math.round(p * (d - s));  
    }
	
	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		mHeight = 0;
		invalidate();
	}

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值