动态绘制自定义圆弧段

11

我的客户希望在应用程序中使用以下的小部件:
enter image description here
“Text”来自服务器。渐变角度取决于从服务器获取的变量。此外,客户希望渐变动态填充(用户必须看到从0开始填充渐变的过程)。
现在我这样做:我使用两个图像 - 一个是彩色圆圈,第二个是灰色圆圈。我创建一个具有特定角度的圆形片段,并将其应用为掩码到灰色圆圈上,然后将彩色圆圈与新的灰色圆圈(其中切掉了一个扇形)组合起来。
这是我的代码。调用initializeVarsForCompoundImDrawing初始化变量,然后每秒钟多次调用makeCompoundImage,最后调用nullVarsForCompoundImDrawing以释放资源:

private static Bitmap notColoredBitmap;
private static Bitmap coloredBitmap;
private static Bitmap notColoredWithMaskBitmap;
private static Bitmap finalBitmap;
private static Canvas notColoredWithMaskCanvas;
private static Paint paintForMask;
private static Paint smoothPaint;
private static Canvas finalCanvas;
private static RectF rectForMask;

public static void initializeVarsForCompoundImDrawing()
{
    Context context = MainApplication.getContext();
    notColoredBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.not_colored);
    coloredBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.colored);
    
    paintForMask = new Paint(Paint.ANTI_ALIAS_FLAG);
    paintForMask.setStyle(Paint.Style.FILL);
    paintForMask.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
    rectForMask = new RectF(0, 0, notColoredBitmap.getWidth(), notColoredBitmap.getHeight());

    smoothPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
}

public static void nullVarsForCompoundImDrawing()
{
    notColoredBitmap = null;
    coloredBitmap = null;
    
    paintForMask = null;
    rectForMask = null;
    smoothPaint = null;
}

public static void makeCompoundImage(ImageView imageView, int angle)
{
    notColoredWithMaskBitmap = Bitmap.createBitmap(notColoredBitmap.getWidth(), notColoredBitmap.getHeight(), Bitmap.Config.ARGB_8888);
    notColoredWithMaskCanvas = new Canvas(notColoredWithMaskBitmap);
    notColoredWithMaskCanvas.drawBitmap(notColoredBitmap, 0, 0, smoothPaint);
    notColoredWithMaskCanvas.drawArc(rectForMask, 270, angle, true, paintForMask);

    finalBitmap = Bitmap.createBitmap(notColoredBitmap.getWidth(), notColoredBitmap.getHeight(), Bitmap.Config.ARGB_8888);
    finalCanvas = new Canvas(finalBitmap);
    finalCanvas.drawBitmap(coloredBitmap, 0, 0, smoothPaint);
    finalCanvas.drawBitmap(notColoredWithMaskBitmap, 0, 0, smoothPaint);

    imageView.setImageBitmap(finalBitmap);
}

第一个问题:有没有可能改进这段代码以使用更少的资源?
第二个问题:我该如何将文本添加到 finalBitmap 中(现在它是一个 TextView,显示在带图像的 ImageView 的顶部)?

3个回答

8
回答你的问题:是的,这可以更轻松地完成,更加容易:
public class Ring extends View {
    private Bitmap mBack;
    private Paint mPaint;
    private RectF mOval;
    private Paint mTextPaint;

    public Ring(Context context) {
        super(context);
        Resources res = getResources();
        mBack = BitmapFactory.decodeResource(res, R.drawable.back);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        Bitmap ring = BitmapFactory.decodeResource(res, R.drawable.ring);
        mPaint.setShader(new BitmapShader(ring, TileMode.CLAMP, TileMode.CLAMP));
        mOval = new RectF(0, 0, mBack.getWidth(), mBack.getHeight());
        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setTextSize(24);
        mTextPaint.setTextAlign(Align.CENTER);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.translate((getWidth() - mBack.getWidth()) / 2, (getHeight() - mBack.getHeight()) / 2);
        canvas.drawBitmap(mBack, 0, 0, null);
        float angle = 220;
        canvas.drawArc(mOval, -90, angle, true, mPaint);
        canvas.drawText("Text",
            mBack.getWidth() / 2,
            (mBack.getHeight() - mTextPaint.ascent()) / 2,
            mTextPaint);
    }
}

编辑:

以下是另一种解决方案(无居中,无文本内容,只是一个概念)

class Ring extends View {
    private Bitmap back;
    private Bitmap ring;
    private RectF oval;
    private Paint arcPaint;

    public Ring(Context context) {
        super(context);
        Resources res = getResources();
        back = BitmapFactory.decodeResource(res, R.drawable.back);
        ring = BitmapFactory.decodeResource(res, R.drawable.ring);
        arcPaint = new Paint();
        arcPaint.setXfermode(new PorterDuffXfermode(Mode.CLEAR));
        oval = new RectF(-1, -1, ring.getWidth()+1, ring.getHeight()+1);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawARGB(0xaa, 0, 255, 0);
        canvas.drawBitmap(back, 0, 0, null);
        canvas.saveLayer(oval, null, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
        canvas.drawBitmap(ring, 0, 0, null);
        float angle = 300;
        canvas.drawArc(oval, angle-90, 360-angle, true, arcPaint);
        canvas.restore();
    }
}

谢谢您的回复。代码确实简单得多,但彩色部分(环)的质量却更差... - smb
你可以将同一位图设置为“背景”和“环”,并设置一个角度。应用角度的部分是“环”。你会注意到,那里的质量比其他部分差,尽管它们是相同的可绘制对象。 - smb
我已经完成了,质量完全相同,但你可以在mPaint上设置setDither为true,看看会发生什么。 - pskink
你可以在这里看到结果:http://picpaste.com/img.png,有什么区别或者你觉得质量变差了吗? - pskink
我的方法比较糟糕,因为代码很多。但是使用它得到的图像更好。 - smb
显示剩余7条评论

6
您可以使用我的项目作为示例 https://github.com/donvigo/CustomProgressControls。它尚未在Github上进行文档记录,但我希望您能理解我的代码 :) 编辑:圆形内的文本不是TextView,而是使用paint绘制的。

3

我一直在做类似的事情——动态进度条,就像你图片上展示的那样,但每秒钟进度条的状态都会改变。里面有一个时间文本,直到进度结束才会保留。我将在此展示我的整个视图,也许对某些人会有用。

public class TickerView extends View {

private Paint mPaint, backPaint;
private RectF mOval;
private Paint mTextPaint;

private int time;
private float progress = 100;

public TickerView(Context context) {
    super(context);

    init();
}

public TickerView(Context context, AttributeSet attrs) {
    super(context, attrs);

    init();
}

public TickerView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);

    init();
}

private void init(){
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setColor(Color.WHITE);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeWidth(UIHelper.getPixel(1));//1dp
    mPaint.setDither(true);                    // set the dither to true
    mPaint.setStrokeJoin(Paint.Join.ROUND);    // set the join to round you want
    mPaint.setStrokeCap(Paint.Cap.ROUND);      // set the paint cap to round too
    mPaint.setPathEffect(new PathEffect());   // set the path effect when they join.


    backPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    backPaint.setColor(ColorManager.roseLine);
    backPaint.setStyle(Paint.Style.STROKE);
    backPaint.setStrokeWidth(UIHelper.getPixel(1));//1dp
    backPaint.setDither(true);                    // set the dither to true
    backPaint.setStrokeJoin(Paint.Join.ROUND);    // set the join to round you want
    backPaint.setStrokeCap(Paint.Cap.ROUND);      // set the paint cap to round too
    backPaint.setPathEffect(new PathEffect());   // set the path effect when they join.

    measure(MeasureSpec.EXACTLY, View.MeasureSpec.EXACTLY);
    mOval = new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight());

    mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mTextPaint.setColor(Color.WHITE);
    mTextPaint.setTypeface(FontSetter.HelveticaNeueLight);
    mTextPaint.setTextSize(UIHelper.getPixel(15));
    mTextPaint.setTextAlign(Paint.Align.CENTER);

    mOval = new RectF(UIHelper.getPixel(1), UIHelper.getPixel(1), getWidth(), getHeight());
}

/**
 *
 * @param persent in range [0..1]
 * @param time - seconds left before progress will be executed
 */
public void setProgress(float persent, int time){
    this.progress = persent;
    this.time = time;
    invalidate();
}

@Override
protected void onDraw(Canvas canvas) {
    canvas.drawText("" + time,
            getWidth() / 2,
            (getHeight() - mTextPaint.ascent()) / 2,
            mTextPaint);

    mOval.right = getWidth() - UIHelper.getPixel(1);
    mOval.bottom = getHeight() - UIHelper.getPixel(1);

    canvas.drawArc(mOval, 0, 360, false, backPaint);

    int angle = (int)( progress * 360 );
    canvas.drawArc(mOval, -90, angle, false, mPaint);
}

}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接