1

I am trying to implement the below progress bar: enter image description here

and the result I am getting is: enter image description here

The problem here is that the lines do not fit in the progress bar as it is clear in the left and right side of the image. The reason is that I am using the progress bar's height and width to draw the lines, but the progress bar has a corner radius which is not considered when getting the height and width.'

I tried adding paddings to the progrss bar, but I don't want any empty space as seen in the first image above.

Here are my main classes:

ProgressDrawable.java

class ProgressDrawable extends Drawable {
private static final int NUM_LINES = 60;

private Paint mPaint = new Paint();
private ProgressBar progressBar;

public ProgressDrawable(Context context, ProgressBar progressBar){
    super();
    init(context);

    this.progressBar = progressBar;
}

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

@Override
public void draw(Canvas canvas) {
    float width = progressBar.getWidth();
    float height = progressBar.getHeight();
    for (int i = 0; i < NUM_LINES; i++) {
        float startX = width * i / NUM_LINES;
        float stopX = startX + 2f * width / NUM_LINES;
        //mPaint.setColor((i + 1) * 10000 / NUM_LINES <= getLevel()? 0xff888888 : 0xCACACA);
        mPaint.setShader((i + 1) * 10000 / NUM_LINES <= getLevel()? new LinearGradient(0, 0, 0, height, Color.parseColor("#77F5FF"), Color.parseColor("#31AFF5"), Shader.TileMode.MIRROR) : new LinearGradient(0, 0, 0, height, Color.parseColor("#D9D9D9"), Color.parseColor("#D9D9D9"), Shader.TileMode.MIRROR));
        canvas.drawLine(startX, height, stopX, 0, mPaint);
    }
}

@Override
public void setAlpha(int alpha) {
}

@Override
public void setColorFilter(ColorFilter cf) {
}

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

private void init(Context context)
{
    Resources resources = context.getResources();
    mPaint = new Paint();
    mPaint.setAntiAlias(true);
    mPaint.setStrokeWidth(resources.getDimension(R.dimen.vertical_divider_width));
}
}

MainActivity.java

public class MainActivity extends AppCompatActivity {

LinearLayout mainLayout;
ProgressBar pb;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mainLayout = findViewById(R.id.mainLayout);

    LinearLayout ll = new LinearLayout(this);
    ll.setOrientation(LinearLayout.VERTICAL);

    mainLayout.addView(ll);

    pb = findViewById(R.id.progressBar);
    Drawable d = new ProgressDrawable(this, pb);
    pb.setProgressDrawable(d);

    SeekBar.OnSeekBarChangeListener l = new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
        }

        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            int newProgress = pb.getMax() * progress / seekBar.getMax();
            Log.d(MainActivity.class.getSimpleName(), "onProgressChanged " + newProgress);
            pb.setProgress(newProgress);
        }
    };

    startProgress();
}
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/mainLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" android:orientation="vertical" android:gravity="center">

<ProgressBar
        android:id="@+id/progressBar"
        android:layout_width="600dp"
        android:layout_height="50dp"
        android:max="60"
        style="?android:attr/progressBarStyleHorizontal"
        android:background="@drawable/rounded_layout"/>
</LinearLayout>

rounded_layout.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android"
   android:shape="rectangle">
   <solid android:color="#D9D9D9"/>
   <corners android:radius="15dp" />
</shape>

Any help will be highly appreciated.

Thanks in advance.

Davi
  • 1,031
  • 12
  • 21
  • 1
    This problem is very similar to one where I posted an [answer](https://stackoverflow.com/questions/54097662/how-to-create-a-rectangle-with-rounded-corners-with-different-sections-in-differ/54098235#54098235) recently - you can use `Canvas.clipPath()` when drawing. Maybe reading the other post will help you with your task – Bö macht Blau Jan 11 '19 at 18:39
  • I tried this but it is not working, canvas.clipPath(addRoundedRectPath) will work fine when drawing a rectangle, but I am drawing a line. – Davi Jan 11 '19 at 19:47
  • IMO drawing lines (and preventing that they are drawn in the wrong area) is possible as well, see for example [this](https://stackoverflow.com/a/54024305/5015207) (and sorry that I always direct to my own snippets, but these are the ones I know how to find) – Bö macht Blau Jan 11 '19 at 19:53
  • I solved it, many thanks. I had to draw the clipped rectangle and then make sure the lines are drawn inside that rectangle, and no need for the rounded_layout.xml as the rectangle is rounded. Thank you again. – Davi Jan 11 '19 at 20:01

1 Answers1

1

Thanks for @0X0nousugar's answer to this question.

Clipping the canvas was the right way, but my main mistake is that I was drawing the lines with canvas the progress bar's rounded shape is done in xml. My solution was to remove the rounded_layout.xml as background of the preogress bar, draw a clipped rectangle and then make sure to draw the lines inside it.

Below is the code snippet:

@Override
public void draw(Canvas canvas) {
    mPaint = new Paint();
    mPaint.setAntiAlias(true);
    // get the height and width of the progress bar
    float width = progressBar.getWidth();
    float height = progressBar.getHeight();
    Path rectPath = new Path();
    RectF rect = new RectF(0, 0, 0,0);
    // set the size of the rectangle to the size of the progress bar
    rect.set(0, 0, width, height);
    // set the corner radius (mine is 15) to have a rounded rectangle
    rectPath.addRoundRect(rect, 15, 15, Path.Direction.CW);
    // set the color for the rectangle
    mPaint.setColor(Color.parseColor("#D9D9D9"));
    // clip and draw the rectangle
    canvas.clipPath(rectPath);
    canvas.drawRect(rect, mPaint);
    // a loop to create the lines inside the rectangle, one at a time
    for (int i = 0; i < NUM_LINES; i++) {
        float startX = width * i / NUM_LINES;
        float stopX = startX + 2f * width / NUM_LINES;
        mPaint.setShader((i + 1) * 10000 / NUM_LINES <= getLevel()? 
                         new LinearGradient(0, 0, 0, height, 
                         Color.parseColor("#77F5FF"), Color.parseColor("#31AFF5"), 
                         Shader.TileMode.MIRROR) : new LinearGradient(0, 0, 0, 
                         height, Color.parseColor("#D9D9D9"), 
                         Color.parseColor("#D9D9D9"), Shader.TileMode.MIRROR));
                         canvas.drawLine(startX, height, stopX, 0, mPaint);
    }
}

Note: Make sure that all the lines are drawn with in the rectangle.

Davi
  • 1,031
  • 12
  • 21