Thursday, 15 June 2017

HB Blog 138: Android Progress View With Water Filling Effect.

In my previous post, I showed how we can use seekbar to create vertical seekbar. For more details you can follow my blog  HB Blog 135: Android Vertical Seekbar Tutorial.
In this post, I will show how to create a progress view that represents water filling effect. Here, progress can be updated using vertical bar and water can gradually filled bottom to top.

There are four custom attributes that can be set in your layout xml:
  • fillColor, color - Sets the color of the fill area. Default is Color.WHITE.
  • strokeColor, color - Sets the color of the bounding circle. Default is Color.BLACK.
  • strokeWidth, float - Sets the thickness of the bounding circle. Default is 1.0.
  • value, integer: 0-100 - Sets the value for the fill area. Default is 0.
Refer the below link for complete sample code:-

Download Sample Code

Have a look on few code snippets,

//CustomFillView.java
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package com.harshalbenake.fillview.views;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import com.harshalbenake.fillview.R;

public class CustomFillView extends View {
    public static final int MIN_VALUE = 0;
    public static final int MAX_VALUE = 100;

    private PointF center = new PointF();
    private RectF circleRect = new RectF();
    private Path segment = new Path();
    private Paint strokePaint = new Paint();
    private Paint fillPaint = new Paint();

    private int radius;

    private int fillColor;
    private int strokeColor;
    private float strokeWidth;
    private int value;

    public CustomFillView(Context context) {
        this(context, null);
    }

    public CustomFillView(Context context, AttributeSet attrs) {
        super(context, attrs);

        TypedArray a = context.getTheme().obtainStyledAttributes(
                attrs,
                R.styleable.CircleFillView,
                0, 0);

        try {
            fillColor = a.getColor(R.styleable.CircleFillView_fillColor, Color.CYAN);
            strokeColor = a.getColor(R.styleable.CircleFillView_strokeColor, Color.BLACK);
            strokeWidth = a.getFloat(R.styleable.CircleFillView_strokeWidth, 1f);
            value = a.getInteger(R.styleable.CircleFillView_value, 0);
            adjustValue(value);
        } finally {
            a.recycle();
        }

        fillPaint.setColor(fillColor);
        strokePaint.setColor(strokeColor);
        strokePaint.setStrokeWidth(strokeWidth);
        strokePaint.setStyle(Paint.Style.STROKE);
    }

    public void setFillColor(int fillColor) {
        this.fillColor = fillColor;
        fillPaint.setColor(fillColor);
        invalidate();
    }

    public int getFillColor() {
        return fillColor;
    }

    public void setStrokeColor(int strokeColor) {
        this.strokeColor = strokeColor;
        strokePaint.setColor(strokeColor);
        invalidate();
    }

    public int getStrokeColor() {
        return strokeColor;
    }

    public void setStrokeWidth(float strokeWidth) {
        this.strokeWidth = strokeWidth;
        strokePaint.setStrokeWidth(strokeWidth);
        invalidate();
    }

    public float getStrokeWidth() {
        return strokeWidth;
    }

    public void setValue(int value) {
        adjustValue(value);
        setPaths();

        invalidate();
    }

    public int getValue() {
        return value;
    }

    private void adjustValue(int value) {
        this.value = Math.min(MAX_VALUE, Math.max(MIN_VALUE, value));
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        center.x = getWidth() / 2;
        center.y = getHeight() / 2;
        radius = Math.min(getWidth(), getHeight()) / 2 - (int) strokeWidth;
        circleRect.set(center.x - radius, center.y - radius, center.x + radius, center.y + radius);

        setPaths();
    }

    private void setPaths() {
        float y = center.y + radius - (2 * radius * value / 100 - 1);
        float x = center.x - (float) Math.sqrt(Math.pow(radius, 2) - Math.pow(y - center.y, 2));

        float angle = (float) Math.toDegrees(Math.atan((center.y - y) / (x - center.x)));
        float startAngle = 180 - angle;
        float sweepAngle = 2 * angle - 180;

        segment.rewind();
        segment.addArc(circleRect, startAngle, sweepAngle);
        segment.close();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.drawPath(segment, fillPaint);
        canvas.drawCircle(center.x, center.y, radius, strokePaint);
    }
}

//MainActivity.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.harshalbenake.fillview;

import android.app.Activity;
import android.os.Bundle;
import android.widget.SeekBar;
import com.harshalbenake.fillview.views.CustomFillView;
import com.harshalbenake.fillview.views.CustomVerticalSeekbar;

public class MainActivity extends Activity {

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

        CustomVerticalSeekbar customVerticalSeekbar = (CustomVerticalSeekbar) findViewById(R.id.customverticalseekbar);
        final CustomFillView customfillview = (CustomFillView) findViewById(R.id.customfillview);
        customVerticalSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean b) {
                customfillview.setValue(progress);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });
    }
}

//activity_main.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:orientation="horizontal"
    android:background="@android:color/white"
    android:layout_height="match_parent">
    <com.harshalbenake.fillview.views.CustomFillView
        android:layout_width="250dp"
        android:layout_margin="16dp"
        android:id="@+id/customfillview"
       android:layout_height="match_parent" />
    <com.harshalbenake.fillview.views.CustomVerticalSeekbar
        android:layout_width="wrap_content"
        android:id="@+id/customverticalseekbar"
        android:layout_height="match_parent" />
</LinearLayout>

No comments:

Post a Comment