Android implements a progress bar with indicators

Time:2021-9-17
catalogue
  • background
  • Open dry
    • Customize the process of view (according to the above section)
    • measure
    • draw
  • 1. Indicator drawing
    • 2. Text drawing
      • summary

        background

        When we see the effect of UI design for us, our habitual idea is to see if Google provides us with corresponding controls or whether we can find some suitable wheels on the Internet and use them directly. However, sometimes unfortunately, there is no such wheel for us to use directly. Moreover, when the UI and products need such effects, we can only draw them ourselves. Today, I’ll take you to simply implement a progress bar with indicators to help you understand the drawing process of defining view.

        Effect realization diagram

        As the saying goes, if there is no effect picture, it is tantamount to playing that game. Therefore, according to the Convention, mapping:

        Open dry

        When we decide to draw by ourselves, we first need to observe, analyze and disassemble the requirements and effects. It is the so-called turning big things into small ones and small things into nothing.

        1. Observation: the rendering includes progress bar, rounded indicator with triangular arrow, progress value and progress animation
        2. Analysis: through observation, we can divide the whole progress bar into four parts: indicator, progress bar, progress value and animation
        3. Disassembly: therefore, we can draw the components of the above parts separately in turn. Finally, the overall association is to achieve the effect of the above figure

        Customize the process of view (according to the above section)

        As we all know, the drawing process consists of three parts: custom attributes, measurement, draw and layout. However, what we are talking about today is the progress bar view, not the viewlayout, so we won’t talk about the layout part today.

        Get from custom properties and code

        attribute explain
        app:hpb_centerPadding=”5dp” Distance between indicator and progress bar
        app:hpb_progressBarBackgroundColor=”@color/colorPrimary” Progress bar background color
        app:hpb_progressBarForegroundColor=”@color/colorAccent” Progress bar foreground
        app:hpb_progressBarHeight=”2dp” Indicator bottompadding
        app:hpb_textBottomPadding=”5dp” Distance between indicator and progress bar
        app:hpb_textLeftPadding=”5dp” Indicator leftpadding
        app:hpb_textRightPadding=”5dp” Indicator rightpadding
        app:hpb_textTopPadding=”5dp” Indicator toppadding
        app:hpb_textSize=”12sp” Indicator text size
        app:hpb_textColor=”#FFFFFF” Indicator text color
        app:hpb_progress=”20″ Progress value
        //Get custom properties
            TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.HorizontalProgressBar);
            mTextSize = ta.getDimension(R.styleable.HorizontalProgressBar_hpb_textSize, Utils.sp2px(context, 12));
            mTextColor = ta.getColor(R.styleable.HorizontalProgressBar_hpb_textColor, Color.parseColor("#FFFFFF"));
            currentProgress = ta.getFloat(R.styleable.HorizontalProgressBar_hpb_progress, 0);
            mTextLeftPadding = (int) ta.getDimension(R.styleable.HorizontalProgressBar_hpb_textLeftPadding, Utils.sp2px(context, 5));
            mTextRightPadding = (int) ta.getDimension(R.styleable.HorizontalProgressBar_hpb_textRightPadding, Utils.sp2px(context, 5));
            mTextTopPadding = (int) ta.getDimension(R.styleable.HorizontalProgressBar_hpb_textTopPadding, Utils.sp2px(context, 5));
            mTextBottomPadding = (int) ta.getDimension(R.styleable.HorizontalProgressBar_hpb_textBottomPadding, Utils.sp2px(context, 5));
            mCenterPadding = (int) ta.getDimension(R.styleable.HorizontalProgressBar_hpb_centerPadding, Utils.sp2px(context, 5));
            mProgressHeight = (int) ta.getDimension(R.styleable.HorizontalProgressBar_hpb_progressBarHeight, Utils.sp2px(context, 2));
            mBackgroundColor = ta.getColor(R.styleable.HorizontalProgressBar_hpb_progressBarBackgroundColor, Color.parseColor("#E8E8E8"));
            mForegroundColor = ta.getColor(R.styleable.HorizontalProgressBar_hpb_progressBarForegroundColor, Color.parseColor("#912CEE"));
            ta.recycle();
        
            //Progress bar background color brush initialization
            mBackgroundPaint = new Paint();
            mBackgroundPaint.setColor(mBackgroundColor);
            mBackgroundPaint.setStrokeCap(Paint.Cap.ROUND);
            mBackgroundPaint.setStrokeWidth(mProgressHeight);
            mBackgroundPaint.setAntiAlias(true);
        
            //Pointer path brush initialization
            mPathPaint = new Paint();
            mPathPaint.setColor(mForegroundColor);
            mPathPaint.setStrokeCap(Paint.Cap.ROUND);
            mPathPaint.setPathEffect(new CornerPathEffect(Utils.dp2px(getContext(), 2)));
            mPathPaint.setAntiAlias(true);
        
            //Progress bar foreground brush initialization
            mProgressPaint = new Paint();
            mProgressPaint.setColor(mForegroundColor);
            mProgressPaint.setStrokeWidth(mProgressHeight);
            mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
            mProgressPaint.setAntiAlias(true);
        
            //Progress value copybrush initialization
            mTextPaint = new Paint();
            mTextPaint.setColor(mTextColor);
            mTextPaint.setTextSize(mTextSize);
            mTextPaint.setStyle(Paint.Style.FILL);
        
            //Indicator path initialization
            mPath = new Path();

        measure

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            int width = MeasureSpec.getSize(widthMeasureSpec);
        
            int heightMode = MeasureSpec.getMode(heightMeasureSpec);
            int height = MeasureSpec.getSize(heightMeasureSpec);
        
            setMeasuredDimension(measureWidth(widthMode, width), measureHeight(heightMode, height));
        }
        
        /**
         *Measure width
         *
         * @param mode
         * @param width
         * @return
         */
        private int measureWidth(int mode, int width) {
            switch (mode) {
                case MeasureSpec.UNSPECIFIED:
                case MeasureSpec.AT_MOST:
                case MeasureSpec.EXACTLY:
                    mWidth = width;
                    break;
            }
            return mWidth;
        }
        
        /**
         *Measuring height
         *
         * @param mode
         * @param height
         * @return
         */
        private int measureHeight(int mode, int height) {
            switch (mode) {
                case MeasureSpec.UNSPECIFIED:
                case MeasureSpec.AT_MOST:
                case MeasureSpec.EXACTLY:
                    mHeight = height;
                    break;
            }
            return mHeight;
        }
        
        //Text measurement
        private void measureText(String text) {
            Rect rect = new Rect();
            mTextPaint.getTextBounds(text, 0, text.length(), rect);
            mTextWidth = rect.width();
            mTextHeight = rect.height();
        }

        draw

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            mIndicatorWidth = mTextLeftPadding + mTextWidth + mTextRightPadding;
            mIndicatorHeight = mTextTopPadding + mTextHeight + mTextBottomPadding;
        
            float backgroundProgressBarStartX = (float) (1.0 * mIndicatorWidth / 2);
            float backgroundProgressBarEndX = mWidth - mIndicatorWidth / 2;
        
            float foregroundProgress = (float) (1.0 * (mWidth - mIndicatorWidth) * currentProgress / 100);
        
            float foregroundProgressBarStartX = (float) (1.0 * mIndicatorWidth / 2);
            float foregroundProgressBarEndX = foregroundProgressBarStartX + foregroundProgress;
        
            Log.e(TAG, "backgroundProgressBarStartX----" + backgroundProgressBarStartX
                    + "----backgroundProgressBarEndX----" + backgroundProgressBarEndX
                    + "----foregroundProgress----" + foregroundProgress
                    + "----foregroundProgressBarStartX----" + foregroundProgressBarStartX
                    + "----foregroundProgressBarEndX----" + foregroundProgressBarEndX
            );
        
            float progressX = foregroundProgress;
            float progressY = (float) (mCenterPadding + mIndicatorHeight * 1.5);
        
        
            //Progress background
            canvas.drawLine(backgroundProgressBarStartX, progressY, backgroundProgressBarEndX, progressY, mBackgroundPaint);
        
            //Progress foreground
            canvas.drawLine(foregroundProgressBarStartX, progressY, foregroundProgressBarEndX, progressY, mProgressPaint);
        
            //Indicator
            drawPath(canvas, progressX, 45);
        
            //Writing
            drawText(canvas, mTextProgress, progressX);
        }

        1. Indicator drawing

        Draw through path to draw the rectangle and lower triangle

        /**
         *Draw indicator
         *
         * @param canvas
         * @param progressX
         */
        private void drawPath(Canvas canvas, float progressX, float rotateAngle) {
            mPath.reset();
        
            mPath.moveTo(progressX, 0); //1
            mPath.lineTo(progressX + mIndicatorWidth, 0); //2
            mPath.lineTo(progressX + mIndicatorWidth, mIndicatorHeight); //3
            mPath.lineTo((float) (progressX + mIndicatorWidth * 0.75), mIndicatorHeight); //4
            mPath.lineTo((float) (progressX + mIndicatorWidth * 0.5), (float) (mIndicatorHeight * 1.5)); //5
            mPath.lineTo((float) (progressX + mIndicatorWidth * 0.25), mIndicatorHeight); //6
            mPath.lineTo(progressX, mIndicatorHeight);//7
            mPath.close();
        
            canvas.drawPath(mPath, mPathPaint);
        }

        2. Text drawing

        The text drawing involves the measurement, which has been mentioned in the above measurement, but the baseline acquisition is involved in the drawing, as shown below

        /**
         *Calculates the distance from the baseline to the centerline when you draw text
         *
         * @param p
         *@ return the distance between the baseline and the center
         */
        public float getBaseline(Paint p) {
            Paint.FontMetrics fontMetrics = p.getFontMetrics();
            return (fontMetrics.descent - fontMetrics.ascent) / 2 - fontMetrics.descent;
        }
        
        /**
         *Draw text
         *
         * @param canvas
         * @param text
         * @param progressX
         */
        private void drawText(Canvas canvas, String text, float progressX) {
            float baseline = getBaseline(mTextPaint);
            float textY = mTextTopPadding + mTextHeight / 2 + baseline;
            canvas.drawText(text, progressX + mTextTopPadding, textY, mTextPaint);
        }

        summary

        The above complete code has been uploaded to GitHub and GitHub address. If necessary, you can go up and have a look, download it for direct use, or import it through jcenter. Since the current status of jcenter has stopped maintenance, it will be moved to Maven in the future

        The above is the details of the Android implementation of the progress bar with indicators. For more information about the Android progress bar, please pay attention to other relevant articles of developeppaer!