Skip to content

Instantly share code, notes, and snippets.

@fullkomnun
Last active July 21, 2017 16:37
Show Gist options
  • Save fullkomnun/5b2addfe5b4cdc1f91caf49632d6fc61 to your computer and use it in GitHub Desktop.
Save fullkomnun/5b2addfe5b4cdc1f91caf49632d6fc61 to your computer and use it in GitHub Desktop.
ColorBarDrawable
package ornoyman.com.colorbardrawable;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import static com.fernandocejas.arrow.checks.Preconditions.checkArgument;
/**
* A drawable that draws a rounded-corners bar with multiple colored sections.
* Either each color is assigned a weight or all given the same weight.
*/
public abstract class ColorBarDrawable extends Drawable {
private final int[] sectionColors;
private final float[] sectionWeights;
protected final Paint backgroundPaint = new Paint();
protected int width;
protected int height;
protected float radius;
protected int left;
protected int top;
protected int right;
protected int bottom;
protected ColorBarDrawable(@NonNull int[] sectionColors, @Nullable float[] sectionWeights) {
checkArgument(sectionColors.length != 0, "at least one color must be defined");
checkArgument(sectionWeights == null || sectionColors.length == sectionWeights.length,
"if defined all colors should be assigned weights");
checkArgument(sectionColors.length == 1 && sectionWeights != null,
"weights cannot be defined for single color");
this.sectionColors = fixColors(sectionColors);
this.sectionWeights = fixWeights(sectionWeights);
}
@NonNull private int[] fixColors(@NonNull int[] sectionColors) {
return sectionColors.length > 1 ? sectionColors
: new int[] { sectionColors[0], sectionColors[0] };
}
@Nullable private float[] fixWeights(@Nullable float[] sectionWeights) {
if (sectionWeights == null) {
return null;
}
float weightSum = 1f;
for (int i = 0; i < sectionWeights.length; i++) {
weightSum -= sectionWeights[i];
}
if (weightSum > 0) {
sectionWeights[sectionWeights.length - 1] += weightSum;
}
return sectionWeights;
}
@Override public void draw(@NonNull Canvas canvas) {
final Rect bounds = getBounds();
left = bounds.left;
top = bounds.top;
right = bounds.right;
bottom = bounds.bottom;
width = bounds.right - left;
height = bounds.bottom - top;
radius = getCornersRadius();
drawLastSection(canvas);
float drawPosition = drawFirstSection(canvas);
for (int i = 1; i < sectionsCount() - 1; i++) {
drawPosition += drawSection(canvas, drawPosition, i);
}
}
protected int sectionsCount() {
return sectionColors.length;
}
protected abstract float getCornersRadius();
private void drawLastSection(Canvas canvas) {
drawSection(canvas, 0, sectionsCount() - 1);
}
private float drawFirstSection(Canvas canvas) {
return drawSection(canvas, getInitialDrawPosition(), 0);
}
protected abstract int getInitialDrawPosition();
protected abstract float drawSection(@NonNull Canvas canvas, float drawPosition, int idx);
protected int getSectionColor(int idx) {
return sectionColors[idx];
}
protected float getSectionWeight(int idx) {
return sectionWeights != null ? sectionWeights[idx] : 1f / sectionsCount();
}
@Override public void setAlpha(@IntRange(from = 0, to = 255) int alpha) {
}
@Override public void setColorFilter(@Nullable ColorFilter colorFilter) {
}
@Override public int getOpacity() {
return PixelFormat.UNKNOWN;
}
public static final class HorizontalColorBarDrawable extends ColorBarDrawable {
public HorizontalColorBarDrawable(@NonNull int[] sectionColors,
@Nullable float[] sectionWeights) {
super(sectionColors, sectionWeights);
}
@Override protected float getCornersRadius() {
return height / 2f;
}
@Override protected int getInitialDrawPosition() {
return left;
}
@Override protected float drawSection(@NonNull Canvas canvas, float drawPosition, int idx) {
backgroundPaint.setColor(getSectionColor(idx));
final float sectionWidth = getSectionWidth(idx);
if (idx == 0) {
drawRoundedLeftRect(canvas, drawPosition, top, drawPosition + sectionWidth, height, radius);
} else if (idx == sectionsCount() - 1) {
drawRoundedRightRect(canvas, width - sectionWidth, top, width, height, radius);
} else {
canvas.drawRect(drawPosition, top, drawPosition + sectionWidth, height, backgroundPaint);
}
return sectionWidth;
}
private float getSectionWidth(int idx) {
return getSectionWeight(idx) * width;
}
private void drawRoundedLeftRect(@NonNull Canvas canvas, float left, float top, float right,
float bottom, float radius) {
canvas.drawCircle(radius, radius, radius, backgroundPaint);
canvas.drawRect(left + radius, top, right, bottom, backgroundPaint);
}
private void drawRoundedRightRect(@NonNull Canvas canvas, float left, float top, float right,
float bottom, float radius) {
canvas.drawCircle(right - radius, radius, radius, backgroundPaint);
canvas.drawRect(left, top, right - radius, bottom, backgroundPaint);
}
}
public static final class VerticalColorBarDrawable extends ColorBarDrawable {
public VerticalColorBarDrawable(@NonNull int[] sectionColors,
@Nullable float[] sectionWeights) {
super(sectionColors, sectionWeights);
}
@Override protected float getCornersRadius() {
return width / 2f;
}
@Override protected int getInitialDrawPosition() {
return top;
}
@Override protected float drawSection(@NonNull Canvas canvas, float drawPosition, int idx) {
backgroundPaint.setColor(getSectionColor(idx));
final float sectionHeight = getSectionHeight(idx);
if (idx == 0) {
drawRoundedTopRect(canvas, left, drawPosition, right, drawPosition + sectionHeight, radius);
} else if (idx == sectionsCount() - 1) {
drawRoundedBottomRect(canvas, left, height - sectionHeight, width, height, radius);
} else {
canvas.drawRect(left, drawPosition, right, drawPosition + sectionHeight, backgroundPaint);
}
return sectionHeight;
}
private float getSectionHeight(int idx) {
return getSectionWeight(idx) * height;
}
private void drawRoundedTopRect(@NonNull Canvas canvas, float left, float top, float right,
float bottom, float radius) {
canvas.drawCircle(radius, radius, radius, backgroundPaint);
canvas.drawRect(left, top + radius, right, bottom, backgroundPaint);
}
private void drawRoundedBottomRect(@NonNull Canvas canvas, float left, float top, float right,
float bottom, float radius) {
canvas.drawCircle(radius, bottom - radius, radius, backgroundPaint);
canvas.drawRect(left, top, right, bottom - radius, backgroundPaint);
}
}
}
package ornoyman.com.colorbardrawable.example;
import android.graphics.Color;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import ornoyman.com.colorbardrawable.ColorBarDrawable;
public class MainActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final View bar = findViewById(R.id.bar);
bar.setBackground(new ColorBarDrawable.HorizontalColorBarDrawable(new int[] { Color.RED, Color.CYAN, Color.GREEN}, new float[] { 0.05f, 0.8f, 0.15f }));
}
}
@fullkomnun
Copy link
Author

Design mode through "isInEditMode" is relevant in the context of custom views but not so much for custom drawables

@mirono
Copy link

mirono commented Apr 25, 2017

In this case maybe the default colors/weights should be set to green/red 60:40 so it will show in design, and the setter will override it on runtime set( [] colors, [] weights ) . or maybe use tools:backgroundDrawable if exists (I don't remember) to set to a predefined custom drawable that will show the 60:40 etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment