First paste source address: github.com/CB-ysx/CBRa…

Recently need to make a star score control (can adjust the progress, progress color gradient)

As shown in figure:





image





image

At first, I thought of using RatingBar, but I found a problem. It is a little complicated to implement color gradient, and there are several pages that use it. It can’t be written like this. Just this period of time to see HenCoder wrote Android custom view series articles, so I want to try to achieve a scoring control, can achieve pattern replacement, gradient color, progress background, pattern number, size and other parameters of their own control, after several days of struggle, finally completed the control CBRatingBar

First, the effect picture:





image






image






image






image

GIF effect:





image


How to use:

Gradle

  • Add the following code to your project’s build.gradle:
    allprojects {
        repositories {
            maven { url 'https://jitpack.io' }
        }
    }
Copy the code
  • Add this library to your project’s build.gradle:
Dependencies {compile 'com.github. Cb-ysx :CBRatingBar:2.0.1'}Copy the code

Method of use

  • xml
    <com.cb.ratingbar.CBRatingBar
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
Copy the code
    <com.cb.ratingbar.CBRatingBar
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:starSize="20dp"
        app:starCount="5"
        app:starSpace="10dp"
        app:starStrokeWidth="1dp"
        app:starCanTouch="true"
        app:starMaxProgress="120"
        app:starProgress="60"
        app:starShowStroke="true"
        app:starUseGradient="true"
        app:starStartColor="#0000ff"
        app:starEndColor="#00ff00"
        app:starCoverColor="#ff0000"
        app:starFillColor="# 666666"
        app:starPointCount="5"
        app:starStrokeColor="#0f0f0f"
        app:pathData="@string/bird"
        app:pathDataId="@string/bird"/>
Copy the code
  • java
cbRatingBar.setStarSize(20) / / size
        .setStarCount(5) / / the number of
        .setStarSpace(10) / / spacing
        .setStarPointCount(5) // Angle number (n Angle star)
        .setShowStroke(true) // Whether to display a border
        .setStarStrokeColor(Color.parseColor("#00ff00")) // Border color
        .setStarStrokeWidth(5) // Frame size
        .setStarFillColor(Color.parseColor("#00ff00")) // Fill the background color
        .setStarCoverColor(Color.parseColor("#00ff00")) // Fill the progress color
        .setStarMaxProgress(120) // Maximum progress
        .setStarProgress(50) // The progress currently displayed
        .setUseGradient(true) // Whether to use gradient fill (if yes coverColor is invalid)
        .setStartColor(Color.parseColor("# 000000")) // Start color of gradient
        .setEndColor(Color.parseColor("#ffffff")) // The end color of the gradient
        .setCanTouch(true) // Whether to click
        .setPathData(getResources().getString(R.string.pig))// Pass in the data for path
        .setPathDataId(R.string.pig)// Pass in the PATH data ID
        .setDefaultPath()// Set the default path
        .setPath(path)/ / to the path
        .setOnStarTouchListener(new CBRatingBar.OnStarTouchListener() { // Click listen
            @Override
            public void onStarTouch(int touchCount) {
                Toast.makeText(MainActivity.this."Click number" + touchCount + "A star", Toast.LENGTH_SHORT).show(); }});Copy the code

instructions

PathData is the path data in the SVG file, as follows:

<? The XML version = "1.0" standalone = "no"? > <! DOCTYPE SVG PUBLIC "- / / / / W3C DTD SVG 1.1 / / EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" > < SVG XMLNS: xlink = "http://www.w3.org/1999/xlink" style = "" class =" icon "height =" 16 "p - id =" 2384 "t =" 1506306007922 "version =" 1.1" ViewBox = "0 0 1137 1024" width = "17.765625" XMLNS = "http://www.w3.org/2000/svg" > < defs > < style type = "text/CSS" > < / style > </defs> <path d="M800.653373 60.04085 C-92.533023 0-174.90483 42.957261-228.601405 109.913074-53.696576-66.955813-136.068383-109.913074-228.601406-109.913074-161.838292 0-293.037297-131.199004-293.037297 293.037297 0 34.485875 6.284982 67.452386 17.216998 98.202847 82.149461 249.939217 470.047002 495.868793 504.429116 517.265896 34.374702-21.397103 422.272244-267.326679 504.421705-517.265896 10.932015-30.750461 17.216997-63.716972 17.216997-98.202847-0.007412-161.838292-131.206416-293.037297-293.044708-293.037297 - z "fill =" # E24B44 "p - id =" 2385 "> </path> </svg>Copy the code

Path “M800.653373… -293.037297z” This part of data is the pathData to submit to the control.


How to implement

For better scalability, I used the path data to draw the pattern instead of implementing the star drawing alone. Of course, at first I only implemented the star drawing without rounded corners. So I found a star drawing method on the Internet:

/** * @return */ private path getStarPath(int dx) {path path = new path (); float radius; If (starPointCount % 2 == 0) {radius = starSize * (cos(360.0 / starPointCount / 2) / 2 - sin(360.0 / starPointCount / 2 2) * sin(90-360.0 / starPointCount)/cos(90-360.0 / starPointCount); } else {radius = starSize * sin(360.0 / starPointCount / 4) / 2 / sin(180-360.0 / starPointCount / 2-360.0 / starPointCount / 4); } for (int i = 0; i < starPointCount; ++i) { if (i == 0) { path.moveTo(starSize * cos(360 / starPointCount * i) / 2, starSize * sin(360 / starPointCount * i) / 2 + dx); } else {path.lineTo(starSize * cos(360.0 / starPointCount * I) / 2, Sine (360.0 / starPointCount * I) / 2 + dx); } path.lineTo(radius * cos(360.0 / starPointCount * I + 360.0 / starPointCount / 2), Sine (360.0 / starPointCount * I + 360.0 / starPointCount / 2) + dx); } path.close(); return path; }Copy the code

This code can be used to get the path of the star. As for why this calculation is done, I won’t go into it (it’s important to get the project). This code can draw the pattern as follows:





image

How to draw multiple stars? Here we use dx. Dx is the offset of the star (the width of the star + the distance between the two stars), and we use this offset to get the path of the star in different positions.

canvas.translate(dx, dy);
canvas.rotate(-90);

int x = 0;
for (int i = 0; i < starCount; ++i) {
    Path path = getStarPath(x);
    canvas.drawPath(patpaint);
    x += (starSize + starSpace);
}

canvas.rotate(90);
canvas.translate(-dx, -dy);
Copy the code

StarCount is the number of stars. StarSize is the size of the star. StarSpace is the distance between two stars

At this point, the stars drawn are filled with color, but there is no progress bar, let alone gradient progress bar effect. The drawing effect is shown as follows:





image

The next step is to implement the progress bar. First, implement the solid-color progress bar. Originally, it was very simple to draw the progress, but because the stars are not regular patterns (although they are regular, but they are irregular compared to rectangles and circles, haha, don’t mock me), Plus for extensibility (other truly irregular patterns may be used), there is another way to fill in irregular shapes. The code is as follows:

/ / will be plotted on a star star star = Bitmap. The Bitmap createBitmap (width, starSizeBitmap. Config. ARGB_8888); Canvas starCanvas = neCanvas(star); drawStar(starCanvas, starFillPaint); / / on the star fill color Bitmap finalStar = Bitmap. CreateBitmap (width, starSizeBitmap. Config. ARGB_8888); Canvas canvas = neCanvas(finalStar); canvas.save(); canvas.clipRect(0, 0, dx, starSize); canvas.drawRect(0, 0, widthstarSize, starCoverPaint); canvas.restore(); canvas.drawBitmap(star, 0, 0, starPaint);Copy the code

Create a Bitmap with width(calculated from the number and spacing of stars) and height (starSize), and then draw the stars on the Bitmap with the unselected background color. The result is a map with no progress at all. Next comes the drawing progress, we need to create another Bitmap, then use the clipRect method to crop the rectangular area with the height of starSize and width of dx(progress), then fill the color of progress (either solid color or gradient color), and finally draw the Bitmap with stars on the canvas. Using the PorterDuffXfermode brush, you can get the star effect filled with progress, as shown in the picture:





image

Set brushes:

starPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
Copy the code

HenCoder Android Development Advanced: Custom View 1-2 Paint. I have to say that these articles are very good, and for me, they are very rewarding.

This completes the first version of CBRatingBar. In app, it is convenient and the effect is not bad, but there is only one pattern, there is no way to customize, so I have the second version, although the development of version 2.0.0 only added more custom path data, but the process is also quite bumpy.

Research on extracting PATH data from PNG images — fruitless; Studying path data in SVG images — somewhat promising; So I began to learn SVG syntax, and realized converting pathData in SVG into path data in Android drawing by myself. When it came to drawing arcs, these commands got stuck. I also read some SVG data online, and found that it was quite bad, some used ‘, ‘to divide, and some used Spaces, and I felt that my algorithm could not recognize them well. I struggled with it for a while. Finally, I found the RichPath library on Github, which has an algorithm to extract path from SVG, so I used it, thanks to the algorithm provided by Tarek360.

With this, the rest is much simpler, add several more methods, pass in pathData data, change the path method of obtaining stars before, can be extracted from pathData, so that you can customize the pattern, the specific code is as follows:

/**
* 初始化path
*
* @return
*/
private void initPath() {
    if (pathData != null && !"".equals(pathData.trim().replace(" ", ""))) {
        mPath = PathParserCompat.createPathFromPathData(pathData);
        isSelfPath = true;
    } else if (pathDataId != -1) {
        mPath = PathParserCompat.createPathFromPathData(getResources().getString(pathDataId));
        isSelfPath = true;
    } else {
        isSelfPath = false;
    }
    if (isSelfPath) {
        resizePath(mPath, starSize, starSize);
    }
}
Copy the code

The pathData is the raw data, and the PathParserCompat is used to convert data to a path. Since the extracted path is the original size of SVG, we need to reduce it to the size we set, i.e. ResizePath:

public void resizePath(Path path, float width, float height) {
    RectF bounds = new RectF(0, 0, width, height);
    RectF src = new RectF();
    path.computeBounds(src, true);
    Matrix resizeMatrix = new Matrix();
    resizeMatrix.setRectToRect(src, bounds, Matrix.ScaleToFit.FILL);
    path.transform(resizeMatrix);
}
Copy the code

So we can easily use our own patterns to draw. Finally, paste the source code address: github.com/CB-ysx/CBRa… Welcome to star ~

For SVG syntax, see the W3C School