android 画扇形进度条,扇形进度条 可点击可滑动

这个博客介绍了如何在Android中实现一个可点击、可滑动的1/4扇形进度条组件。通过自定义View `PieChartSeekBar`,利用`Canvas`、`Paint`、`Shader`等进行绘制,并实现了触摸事件监听,可以动态改变进度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

224116231d55

扇形进度条.png

package com.wuyzh.test;

import android.content.Context;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.graphics.PorterDuff;

import android.graphics.PorterDuffXfermode;

import android.graphics.RadialGradient;

import android.graphics.RectF;

import android.graphics.Shader;

import android.graphics.Xfermode;

import android.util.AttributeSet;

import android.view.MotionEvent;

import android.view.View;

/***

* 1/4扇形进度条

* */

public class PieChartSeekBar extends View {

//饼状图半径

private float mRadius = DensityUtils.dip2px(getContext(), 320);

private RectF mSectorRectF = new RectF();

//扇形外圈宽度

private float mRadiusOutlineWidth = 5f;

private RectF mIncludeOutLineRectF = new RectF();

//初始画弧所在的角度

private float mCurrentDegree = 135f;

//空白间隙占整个圆形的大小比例

private double mSpacePercent = 0.002f;

//每块扇形占整个圆的大小比例

private double mSectorPercent;

//扇形块个数

private int mSize = 8;

//当前进度

private int mCurrentLevel = 4;

//画笔

private Paint mPaint;

private Xfermode mDstOutXfermode;

private PorterDuff.Mode mDstOutPorterDuffMode = PorterDuff.Mode.DST_OUT;

private Xfermode mSrcOverXfermode;

private PorterDuff.Mode mSrcOverPorterDuffMode = PorterDuff.Mode.SRC_OVER;

//设置监听回调

private OnPieChartSeekBarChangeListener mOnPieChartSeekBarChangeListener;

public PieChartSeekBar(Context context) {

super(context);

init();

}

public PieChartSeekBar(Context context, AttributeSet attrs) {

super(context, attrs);

init();

}

public PieChartSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

init();

}

//初始化画笔和效果动画

private void init() {

mDstOutXfermode = new PorterDuffXfermode(mDstOutPorterDuffMode);

mSrcOverXfermode = new PorterDuffXfermode(mSrcOverPorterDuffMode);

initRectF();

mPaint = new Paint();

mPaint.setAntiAlias(true);

mPaint.setStyle(Paint.Style.FILL);

mSectorPercent = (0.25 - (mSize-1)*mSpacePercent)/mSize;

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

int saveCount = canvas.saveLayer(mIncludeOutLineRectF, mPaint, Canvas.ALL_SAVE_FLAG);

mCurrentDegree = 135;

float pieSweep;

for (int i = 1; i <= mSize; i++){

if (i <= mCurrentLevel){

//画外部进度指示线 颜色:0xFFcca267

pieSweep = (float) (mSectorPercent *360);

mPaint.setShader(null);

mPaint.setColor(0xFFcca267);

canvas.drawArc(mIncludeOutLineRectF, mCurrentDegree, pieSweep, true, mPaint);

//切掉内部圆

mPaint.setColor(Color.BLACK);

mPaint.setXfermode(mDstOutXfermode);

canvas.drawArc(mSectorRectF, mCurrentDegree-0.002f*360, pieSweep+0.003f*360,true, mPaint);

mPaint.setXfermode(null);

//画扇形 颜色:0xFF02DAFD

RadialGradient gradient =new RadialGradient(mRadius,mRadius,mRadius-mRadiusOutlineWidth,new int[]{Color.TRANSPARENT,Color.TRANSPARENT,0xFF02DAFD},new float[]{0,(mRadius-100)/mRadius,1},Shader.TileMode.MIRROR);

mPaint.setShader(gradient);

mPaint.setXfermode(mSrcOverXfermode);

canvas.drawArc(mSectorRectF, mCurrentDegree, pieSweep, true, mPaint);

mPaint.setXfermode(null);

mCurrentDegree = mCurrentDegree + pieSweep;

}else {

//画外部进度指示线 颜色:0xFF65fbe4

pieSweep = (float) (mSectorPercent *360);

mPaint.setShader(null);

mPaint.setColor(0xFF65fbe4);

canvas.drawArc(mIncludeOutLineRectF, mCurrentDegree, pieSweep, true, mPaint);

//切掉内部圆

mPaint.setColor(Color.BLACK);

mPaint.setXfermode(mDstOutXfermode);

canvas.drawArc(mSectorRectF, mCurrentDegree-0.002f*360, pieSweep+0.003f*360,true, mPaint);

mPaint.setXfermode(null);

//画扇形 颜色:0xFFFFFFFF

RadialGradient gradient =new RadialGradient(mRadius,mRadius,mRadius-mRadiusOutlineWidth,new int[]{Color.TRANSPARENT,Color.TRANSPARENT,0xFFFFFFFF},new float[]{0,(mRadius-100)/mRadius,1},Shader.TileMode.MIRROR);

mPaint.setShader(gradient);

mPaint.setXfermode(mSrcOverXfermode);

canvas.drawArc(mSectorRectF, mCurrentDegree, pieSweep, true, mPaint);

mPaint.setXfermode(null);

mCurrentDegree = mCurrentDegree + pieSweep;

}

//画透明间隔

if (i != mSize){

pieSweep = (float) (mSpacePercent * 360);

mPaint.setColor(Color.TRANSPARENT);

canvas.drawArc(mIncludeOutLineRectF, mCurrentDegree, pieSweep, true, mPaint);

mCurrentDegree = mCurrentDegree + pieSweep;

}

}

//切掉内部圆

mPaint.setShader(null);

mPaint.setColor(Color.BLACK);

mPaint.setXfermode(mDstOutXfermode);

canvas.drawCircle(mRadius, mRadius, mRadius-100, mPaint);

mPaint.setXfermode(null);

canvas.restoreToCount(saveCount);

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

int length = (int) (2 * mRadius);

setMeasuredDimension(length, length);

}

/**

* 初始化绘制弧形所在矩形的四点坐标

**/

private void initRectF() {

mSectorRectF.left = 0 +mRadiusOutlineWidth;

mSectorRectF.top = 0 + mRadiusOutlineWidth;

mSectorRectF.right = 2 * mRadius - mRadiusOutlineWidth;

mSectorRectF.bottom = 2 * mRadius - mRadiusOutlineWidth;

mIncludeOutLineRectF.left = 0;

mIncludeOutLineRectF.top = 0;

mIncludeOutLineRectF.right = 2 * mRadius;

mIncludeOutLineRectF.bottom = 2 * mRadius;

}

private int mTouchPosition;

@Override

public boolean onTouchEvent(MotionEvent event) {

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

mTouchPosition = doOnSpecialTypeClick(event);

if (mTouchPosition == -1){

return false;

}else {

mCurrentLevel = mTouchPosition;

if (mOnPieChartSeekBarChangeListener != null){

mOnPieChartSeekBarChangeListener.onStartTrackingTouch(this);

}

invalidate();

return true;

}

case MotionEvent.ACTION_MOVE:

mTouchPosition = doOnSpecialTypeClick(event);

if (mTouchPosition != -1){

mCurrentLevel = mTouchPosition;

if (mOnPieChartSeekBarChangeListener != null){

mOnPieChartSeekBarChangeListener.onProgressChanged(this,mCurrentLevel,true);

}

invalidate();

}

break;

case MotionEvent.ACTION_UP:

if (mOnPieChartSeekBarChangeListener != null){

mOnPieChartSeekBarChangeListener.onStopTrackingTouch(this);

}

break;

}

return super.onTouchEvent(event);

}

public int getProgress(){

return mCurrentLevel;

}

public void setProgress(int level){

mCurrentLevel = level;

if (mOnPieChartSeekBarChangeListener != null){

mOnPieChartSeekBarChangeListener.onProgressChanged(this,mCurrentLevel,true);

}

invalidate();

}

private int doOnSpecialTypeClick(MotionEvent event) {

float eventX = event.getX();

float eventY = event.getY();

double alfa = 0;

//点击的位置到圆心距离的平方

double distance = Math.pow(eventX - mRadius, 2) + Math.pow(eventY - mRadius, 2);

//判断点击的坐标是否在环内

if (distance < Math.pow(mRadius, 2) && distance > Math.pow(0.72 * mRadius, 2)) {

int which = touchOnWhichPart(event);

switch (which) {

case PART_ONE:

alfa = Math.atan2(eventX - mRadius, mRadius - eventY) * 180 / PI;

break;

case PART_TWO:

alfa = Math.atan2(eventY - mRadius, eventX - mRadius) * 180 / PI + 90;

break;

case PART_THREE:

alfa = Math.atan2(mRadius - eventX, eventY - mRadius) * 180 / PI + 180;

break;

case PART_FOUR:

alfa = Math.atan2(mRadius - eventY, mRadius - eventX) * 180 / PI + 270;

break;

}

}

int position = (int) Math.ceil((alfa-225)/(90.0/mSize));

if (0 <= position&& position <= mSize){

return position;

}else {

return -1;

}

}

/**

* 4 | 1

* -----|-----

* 3 | 2

* 圆被分成四等份,判断点击在园的哪一部分

*/

private static final int PART_ONE = 1;

private static final int PART_TWO = 2;

private static final int PART_THREE = 3;

private static final int PART_FOUR = 4;

//圆周率

private static final float PI = 3.1415f;

private int touchOnWhichPart(MotionEvent event) {

if (event.getX() > mRadius) {

if (event.getY() > mRadius) return PART_TWO;

else return PART_ONE;

} else {

if (event.getY() > mRadius) return PART_THREE;

else return PART_FOUR;

}

}

public void setOnPieChartSeekBarChangeListener(OnPieChartSeekBarChangeListener onPieChartSeekBarChangeListener){

mOnPieChartSeekBarChangeListener = onPieChartSeekBarChangeListener;

}

public interface OnPieChartSeekBarChangeListener {

void onProgressChanged(PieChartSeekBar pieChartSeekBar, int level, boolean isFromUser);

void onStartTrackingTouch(PieChartSeekBar pieChartSeekBar);

void onStopTrackingTouch(PieChartSeekBar pieChartSeekBar);

}

}

自定义属性颜色啥的自己来吧。。。。

使用

xmlns:app="http://schemas.android.com/apk/res-auto"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="@drawable/seat_set_background"

tools:context=".MainActivity">

android:id="@+id/pie_chart_seek_bar"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_centerInParent="true"/>

```![扇形进度条.png](https://upload-images.jianshu.io/upload_images/11008949-a3fee86894cb4717.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值