Android : Semi Circle Progress Bar

前端 未结 6 1565
离开以前
离开以前 2020-12-02 08:02

I want semi circle progress bar in background of image. just like below image.

\"enter

6条回答
  •  無奈伤痛
    2020-12-02 08:28

    This can be implemented by clipping a canvas containing an image at an angle (By drawing an arc).

    You can use an image something like this

    enter image description here

    And clip that image by drawing an arc.

    Here is how you can do it.

    //Convert the progress in range of 0 to 100 to angle in range of 0 180. Easy math.
    float angle = (progress * 180) / 100;
    mClippingPath.reset();
    //Define a rectangle containing the image
    RectF oval = new RectF(mPivotX, mPivotY, mPivotX + mBitmap.getWidth(), mPivotY + mBitmap.getHeight());
    //Move the current position to center of rect
    mClippingPath.moveTo(oval.centerX(), oval.centerY());
    //Draw an arc from center to given angle
    mClippingPath.addArc(oval, 180, angle);
    //Draw a line from end of arc to center
    mClippingPath.lineTo(oval.centerX(), oval.centerY());
    

    And once you get the path, you can use clipPath function to clip the canvas in that path.

    canvas.clipPath(mClippingPath);
    

    Here is the Complete code

    SemiCircleProgressBarView.java

    import android.app.Activity;
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Path;
    import android.graphics.RectF;
    import android.util.AttributeSet;
    import android.util.DisplayMetrics;
    import android.view.View;
    
    
    
    public class SemiCircleProgressBarView extends View {
    
        private Path mClippingPath;
        private Context mContext;
        private Bitmap mBitmap;
        private float mPivotX;
        private float mPivotY;
    
        public SemiCircleProgressBarView(Context context) {
            super(context);
            mContext = context;
            initilizeImage();
        }
    
        public SemiCircleProgressBarView(Context context, AttributeSet attrs) {
            super(context, attrs);
            mContext = context;
            initilizeImage();
        }
    
        private void initilizeImage() {
            mClippingPath = new Path();
    
            //Top left coordinates of image. Give appropriate values depending on the position you wnat image to be placed
            mPivotX = getScreenGridUnit();
            mPivotY = 0;
    
            //Adjust the image size to support different screen sizes
            Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.circle);
            int imageWidth = (int) (getScreenGridUnit() * 30);
            int imageHeight = (int) (getScreenGridUnit() * 30);
            mBitmap = Bitmap.createScaledBitmap(bitmap, imageWidth, imageHeight, false);
        }
    
        public void setClipping(float progress) {
    
            //Convert the progress in range of 0 to 100 to angle in range of 0 180. Easy math.
            float angle = (progress * 180) / 100;
            mClippingPath.reset();
            //Define a rectangle containing the image
            RectF oval = new RectF(mPivotX, mPivotY, mPivotX + mBitmap.getWidth(), mPivotY + mBitmap.getHeight());
            //Move the current position to center of rect
            mClippingPath.moveTo(oval.centerX(), oval.centerY());
            //Draw an arc from center to given angle
            mClippingPath.addArc(oval, 180, angle);
            //Draw a line from end of arc to center
            mClippingPath.lineTo(oval.centerX(), oval.centerY());
            //Redraw the canvas
            invalidate();
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            //Clip the canvas
            canvas.clipPath(mClippingPath);
            canvas.drawBitmap(mBitmap, mPivotX, mPivotY, null);
    
        }
    
        private float getScreenGridUnit() {
            DisplayMetrics metrics = new DisplayMetrics();
            ((Activity)mContext).getWindowManager().getDefaultDisplay().getMetrics(metrics);
            return metrics.widthPixels / 32;
        }
    
    }
    

    And using it in any activity is very easy.

    activity_main.xml

    
    
        
    
       
    

    Note that clipPath function doesn't work if the hardware acceleration is turned on. You can turn off the hardware acceleration only for that view.

       //Turn off hardware accleration
      semiCircleProgressBarView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    

    MainActivity.java

    public class MainActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            SemiCircleProgressBarView semiCircleProgressBarView = (SemiCircleProgressBarView) findViewById(R.id.progress);
            semiCircleProgressBarView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    
            semiCircleProgressBarView.setClipping(70);
        }
    
    }  
    

    As and when the progress changes you can set the progressbar by calling function,

    semiCircleProgressBarView.setClipping(progress);
    

    Ex: semiCircleProgressBarView.setClipping(50); //50% progress

    enter image description here

    semiCircleProgressBarView.setClipping(70); //70% progress
    

    enter image description here

    You can use your own Image to match the requirements. Hope it helps!!

    Edit : To move the semi circle to bottom of the screen, change mPivotY value. Something like this

    //In `SemiCircleProgressBarView.java`
    //We don't get the canvas width and height initially, set `mPivoyY` inside `onWindowFocusChanged` since `getHeight` returns proper results by that time
            public void onWindowFocusChanged(boolean hasWindowFocus) {
                super.onWindowFocusChanged(hasWindowFocus);
    
                mPivotX = getScreenGridUnit();
                mPivotY = getHeight() - (mBitmap.getHeight() / 2);
            }
    

提交回复
热议问题