How to programmatically create or alter a drawable made of lines of different colors

17,537

Solution 1

so this is a custom Drawable you can use:

class LineDrawable extends Drawable {
    private Paint mPaint;

    public LineDrawable() {
        mPaint = new Paint();
        mPaint.setStrokeWidth(3);
    }

    @Override
    public void draw(Canvas canvas) {
        int lvl = getLevel();
        Rect b = getBounds();
        float x = b.width() * lvl / 10000.0f;
        float y = (b.height() - mPaint.getStrokeWidth()) / 2;
        mPaint.setColor(0xffff0000);
        canvas.drawLine(0, y, x, y, mPaint);
        mPaint.setColor(0xff00ff00);
        canvas.drawLine(x, y, b.width(), y, mPaint);
    }

    @Override
    protected boolean onLevelChange(int level) {
        invalidateSelf();
        return true;
    }

    @Override
    public void setAlpha(int alpha) {
    }

    @Override
    public void setColorFilter(ColorFilter cf) {
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }
}

and the test code:

View v = new View(this);
final LineDrawable d = new LineDrawable();
d.setLevel(4000);
v.setBackgroundDrawable(d);
setContentView(v);
OnTouchListener l = new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        int lvl = (int) (10000 * event.getX() / v.getWidth());
        d.setLevel(lvl);
        return true;
    }
};
v.setOnTouchListener(l);

Solution 2

How about using a progress bar? The style of the done and to-do markers can be set either programatically or via xml files. Your code will also be more readable/maintainable because you'll be using the right widget for the job. Your layout will contain:

<ProgressBar
    android:id="@+id/progressBar"
    android:layout_height="3dip"
    android:layout_width="match_parent"
    android:progressDrawable="@drawable/progress_bar" />

You can update the bar from your code using e.g.:

ProgressBar bar = (ProgressBar)findViewById(R.id.progressBar);
bar.setProgress(40);

This example overrides the style of the bar (as directed by the progressDrawable attribute), using a res/drawable/progress_bar.xml file - contents below. This one has extra niceness like gradient shading and rounded corners; adjust as you see fit:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
  <item android:id="@android:id/background">
    <shape>
      <corners android:radius="5dip" />
      <gradient
        android:angle="270"
        android:centerColor="#ff5a5d5a"
        android:centerY="0.5"
        android:endColor="#ff747674"
        android:startColor="#ff9d9e9d" />
    </shape>
  </item>
  <item android:id="@android:id/progress">
    <clip>
      <shape>
        <corners android:radius="5dip" />
          <gradient
            android:angle="0"
            android:endColor="#ff009900"
            android:startColor="#ff000099" />
      </shape>
    </clip>
  </item>
</layer-list>

Credit to http://www.tiemenschut.com/how-to-customize-android-progress-bars/, which gives much more detail on how to customise progress bars.

Share:
17,537
Stéphane
Author by

Stéphane

Updated on June 05, 2022

Comments

  • Stéphane
    Stéphane almost 2 years

    I have to draw a 3dp line to represent a level completion in a quizz game.

    This line must be of 2 colors. For example, if user has completed 40% of the level, the line will be red for the first 40% of the line, the other 60% being grey.

    I have managed to do that with a drawable :

    <?xml version="1.0" encoding="utf-8"?>
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
        <item>
            <shape android:shape="line" >
                <size android:height="3dp" android:width="40dp"/>
                <stroke
                    android:width="1dp"
                    android:color="#FFFC10" />
            </shape>
        </item>
        <item android:left="40dp">
            <shape android:shape="line" >
                <size android:height="3dp" android:width="60dp" />
                <stroke
                    android:width="1dp"
                    android:color="#DDDDDD" />
            </shape>
        </item>
    </layer-list>
    

    And then I display it with an ImageView :

    <ImageView
                    android:id="@+id/row_completion_bar"
                    android:src="@drawable/completion_bar"
                    android:layout_width="100dp"
                    android:layout_height="3dp" />
    

    ... but now, I must of course be able to change this 40%/60% ration depending of the actuel user completion.

    First question: what is the best most efficient way to do it ? Change the drawable at runtime ? or create a new drawable at runtime in Java ?

    Second question: how to do it ? I tried both ways (recreate this drawable in java code / alter the xml drawable at runtime) and didn't succeeded :-(

    Any help will be greatly appreciated.

  • Stéphane
    Stéphane almost 11 years
    Thank you a lot! Do you have any idea on what is the most efficient way: this, or my solution with ImageViews?