Android Tricks: A Karma Bar Inspired by YouTube

Jérémie Laval

To represent the two competing “like” and “dislike” tallies on individual videos, YouTube uses an elegant solution—a partially filled bar that indicates both values:

2012-12-21_2355

I wanted to use a similar interface in one of my applications to represent the “given” and “received” counts for an item. To display the values, I created a custom Android control called KarmaMeter. The bar uses the color theme defined in the corresponding section in the Android design documentation.

karma-bar

The code for the control is given below. It’s implemented as a View and supports animating the changes between different values:

public class KarmaMeter : View
{
	const int DefaultHeight = 10;
	const int DefaultWidth = 120;

	double position = 0.5;
	float lastWidth = -1;
	Paint positivePaint;
	Paint negativePaint;

	public KarmaMeter (Context context, IAttributeSet attrs) :
		base (context, attrs)
	{
		Initialize ();
	}

	public KarmaMeter (Context context, IAttributeSet attrs, int defStyle) :
		base (context, attrs, defStyle)
	{
		Initialize ();
	}

	void Initialize ()
	{
		positivePaint = new Paint {
			AntiAlias = true,
			Color = Color.Rgb (0x99, 0xcc, 0),
		};
		positivePaint.SetStyle (Paint.Style.FillAndStroke);

		negativePaint = new Paint {
			AntiAlias = true,
			Color = Color.Rgb (0xff, 0x44, 0x44)
		};
		negativePaint.SetStyle (Paint.Style.FillAndStroke);
	}

	public void SetKarmaBasedOnValues (int totalGiven, int totalGotten, bool animate = true)
	{
		var value = (((totalGiven - totalGotten) / (float)(totalGiven + totalGotten)) + 1) / 2f;
		SetKarmaValue (value, animate);
	}

	public double KarmaValue {
		get {
			return position;
		}
		set {
			position = Math.Max (0f, Math.Min (value, 1f));
			Invalidate ();
		}
	}

	public void SetKarmaValue (double value, bool animate)
	{
		if (!animate) {
			KarmaValue = value;
			return;
		}

		var animator = ValueAnimator.OfFloat ((float)position, (float)Math.Max (0f, Math.Min (value, 1f)));
		animator.SetDuration (500);
		animator.Update += (sender, e) => KarmaValue = (double)e.Animation.AnimatedValue;
		animator.AnimationEnd += (sender, e) => animator.RemoveAllListeners ();
		animator.Start ();
	}

	protected override void OnMeasure (int widthMeasureSpec, int heightMeasureSpec)
	{
		var width = View.MeasureSpec.GetSize (widthMeasureSpec);
		SetMeasuredDimension (width < DefaultWidth ? DefaultWidth : width,
		                      DefaultHeight);
	}

	protected override void OnDraw (Canvas canvas)
	{
		base.OnDraw (canvas);

		float middle = canvas.Width * (float)position;
		canvas.DrawPaint (negativePaint);
		canvas.DrawRect (0,
		                 0,
		                 middle,
		                 canvas.Height,
		                 positivePaint);
	}
}

To set the value displayed by the bar (between `0.0f` and `1.0f`), you can either directly use the property KarmaValue/SetKarmaValue. Or, if you maintain a count of your positive/negatives values, you can use the SetKarmaBasedOnValues method.

Feedback usabilla icon