Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Xamarin - Radial Progress Component Issue

I 've been trying to implement the RadialProgress component (https://components.xamarin.com/view/radialprogress) on my app. I managed to get it on the screen, and change the progress colour but I can't find a way to change the inner colour of the circle.

The RadialProgressView object itself has a BackgroundTintMode field which takes a DuffPorter.Mode but whenever I try to set the background tint mode the app breaks with this message (Message = "no method with name='setBackgroundTintMode' signature='(Landroid/graphics/PorterDuff$Mode;))

Is there even a way to do what I want?

Thanks!

like image 596
Zez3 Avatar asked Jul 14 '15 15:07

Zez3


2 Answers

Yes, it can be done. Although not in a very straight-forward way or even maintainable way.

Firstly, let's dig a little into RadialProgressView's drawing code (as exposed by Xamarin Studio Assembly Browser):

    protected override void OnDraw(Canvas canvas)
    {
        // ... there's more stuff here, but you get the idea
        canvas.DrawCircle(this.bgCx, this.bgCy, this.radius, this.bgCirclePaint);
        canvas.DrawCircle(this.bgCx, this.bgCy, this.innerLineRadius, this.bgBorderPaint);
        canvas.DrawText(this.valueText, this.textX, this.textY, this.textPaint);
    }

We notice some colors here, like bgCirclePaintand bgBorderPaint. If we are able to change the value of these variables, we will be able to change the color with which the ProgressView is painted.

The problem is that RadialProgressView does not expose the fields – they are all private, so simply inheriting from RadialProgressView will not allow us to set them to a new value.

However, we can make use of reflection to change these private fields, like so:

        var textPaintMember = typeof(RadialProgressView).GetField("textPaint", BindingFlags.Instance | BindingFlags.NonPublic);
        textPaintMember.SetValue(Instance, MyNewSuperCoolColorPaint);

By combining the two, we can come up with a new, customizable class like this:

public class CustomizableRadialProgressView : RadialProgressView 
{
    public CustomizableRadialProgressView(Context context) : base(context)
    {
    }

    public void SetTextColor(Color color) 
    {
        var paint = new Paint();
        paint.SetTypeface(Typeface.DefaultBold); 
        paint.Color = color;
        paint.AntiAlias = true;

        var textPaintMember = typeof(RadialProgressView).GetField("textPaint", BindingFlags.Instance | BindingFlags.NonPublic);

        textPaintMember.SetValue(this, paint);
    }

    public void SetCircleColor(Color color) 
    {
        var paint = new Paint();
        paint.SetStyle(Paint.Style.Fill);
        paint.Color = color;
        paint.AntiAlias = true;

        var circlePaintMember = typeof(RadialProgressView).GetField("bgCirclePaint", BindingFlags.Instance | BindingFlags.NonPublic);

        circlePaintMember.SetValue(this, paint);
    }

    public void SetBorderColor(Color color) 
    {
        var paint = new Paint();
        paint.SetStyle(Paint.Style.Stroke);
        paint.Color = color;
        paint.AntiAlias = true;

        var circlePaintMember = typeof(RadialProgressView).GetField("bgBorderPaint", BindingFlags.Instance | BindingFlags.NonPublic);

        circlePaintMember.SetValue(this, paint);
    }


    public void SetProgressPackgroundColor(Color color) 
    {
        var paint = new Paint();
        paint.SetStyle(Paint.Style.Stroke);
        paint.Color = color;
        paint.AntiAlias = true;

        var circlePaintMember = typeof(RadialProgressView).GetField("bgProgressPaint", BindingFlags.Instance | BindingFlags.NonPublic);

        circlePaintMember.SetValue(this, paint);
    }
}

This will get us the result we're after:

2

Note: It is probably wise to notice that we are making improper use of private fields: We're manipulating them from outside of the class they live in. If Xamarin ever decides to change the way RadialProgressViewis implemented, or even only renames one of the private variables, our code will fail at runtime. The better way to approach this problem would probably be to just ask Xamarin to provide the getters/setters you need. But, hey, it's SO much cooler this way ;)

like image 54
Felix Alcala Avatar answered Sep 19 '22 15:09

Felix Alcala


You can try implementing a custom ViewRenderer and access the underlying native Android views to modify them as you want. https://blog.xamarin.com/using-custom-controls-in-xamarin.forms-on-android/

The error you have regarding the "setBackgroundTintMode" method indicates that you may need to update your Xamarin platform to make sure that the latest APIs are available

like image 27
Mina Wissa Avatar answered Sep 21 '22 15:09

Mina Wissa