Drawing Basketball with Custom Controls

Keywords: Android Attribute xml Java

For Android developers, custom control sounds like a big thing, but at the same time, it also makes most developers feel timid. It's not difficult for them to understand. As long as they learn and practice, custom control is also very simple. Recently, the NBA is playing a hot game. Today, simply draw a basketball and feel the paper tiger of custom control.

First, the effect map is shown.


2. Take three steps:

1. Custom Properties

2. Custom View

3. Use custom View

Step 1: Customize attributes

At res - > values Next, create a new attrs.xml file (with or without new ones), code as follows:

<resources>

    <declare-styleable name="CustomProgressBar">
        <attr name="firstColor" format="color" />
        <attr name="secondColor" format="color" />
        <attr name="circleWidth" format="dimension" />
        <attr name="speed" format="integer" />
    </declare-styleable>

</resources>

Explain the above:

1. name= "Custom ProgressBar": This name value can be written freely without specific requirements, but it is usually written as the class name of a custom View (described later) for easy identification.

2. <attr name= "first color" format= "color"/>: each represents an attribute, which is used in layout; name is the attribute name, format is the type definition of the attribute, which defines whether the attribute is a color type, a size type, a picture, or other type.

4. Custom View:

package com.dapeng.demo;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

import java.util.Timer;
import java.util.TimerTask;

/**
 * Created by dapeng on 2017/6/13.
 */

public class CustomProgressBar extends View {
    //Background color
    private int mFirstColor;
    //The color of progress
    private int mSecondColor;
    //Width of circle
    private int mCircleWidth;
    //Paint brush
    private Paint mPaint;
    //Current progress
    private int mProgress;
    //speed
    private int mSpeed;

    public CustomProgressBar(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    /**
     * Necessary initialization to get some custom values
     *
     * @param context
     * @param attrs
     * @param defStyle
     */
    public CustomProgressBar(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        //Get the value of a custom property
        TypedArray arrs = context.obtainStyledAttributes(attrs, R.styleable.CustomProgressBar, defStyle, 0);
        mFirstColor = arrs.getColor(R.styleable.CustomProgressBar_firstColor, Color.GREEN);
        mSecondColor = arrs.getColor(R.styleable.CustomProgressBar_secondColor, Color.RED);
        mCircleWidth = arrs.getDimensionPixelSize(R.styleable.CustomProgressBar_circleWidth, 20);
        mSpeed = arrs.getInt(R.styleable.CustomProgressBar_speed, 100);
        arrs.recycle();

        mPaint = new Paint();
        mProgress = 0;

        startDraw();

    }

    /**
     * Drawing with a timer
     */
    private void startDraw() {
        final Timer timer = new Timer();
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                if (mProgress == 360) {
                    timer.cancel();
                    return;
                }
                mProgress++;
                postInvalidate();
            }
        };
        timer.schedule(task, 0, mSpeed);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        float centreX = getWidth() / 2; // x-coordinates of the center of a circle
        float centreY = getHeight() / 2; // y coordinates of the center of a circle
        float radius = centreX - 20;// radius
        mPaint.setStrokeWidth(mCircleWidth); // Set the width of the brush (that is, the width of the circle)
        mPaint.setAntiAlias(false); // Anti-aliased
        mPaint.setStyle(Paint.Style.STROKE); // Hollow
        mPaint.setStrokeCap(Paint.Cap.ROUND); // Set the two ends of the brush line to be round

        mPaint.setColor(mFirstColor); // Set the background color
        canvas.drawCircle(centreX, centreY, radius, mPaint); // Draw a circle
        RectF oval2 = new RectF(centreX - radius, centreY - radius / 2, centreX + radius, centreY + radius / 2); // Used to define the boundaries of the shape and size of an arc
        canvas.drawOval(oval2, mPaint);//Draw an ellipse
        canvas.drawLine(centreX - radius, centreY, centreX + radius, centreY, mPaint);//Draw straight lines

        mPaint.setColor(mSecondColor); // Setting the color of progress
        RectF oval = new RectF(centreX - radius, centreY - radius, centreX + radius, centreY + radius); // Used to define the boundaries of the shape and size of an arc
        canvas.drawArc(oval, 0, mProgress, false, mPaint); // Draw an arc according to schedule
        canvas.drawArc(oval2, 0, mProgress, false, mPaint); // Draw an arc according to schedule
        canvas.drawLine(centreX - radius, centreY, centreX - radius + (radius * 2) / 360 * mProgress, centreY, mPaint);//Draw a straight line according to progress
    }
}

The annotations in the code are clear enough, so let me briefly explain here:

Using this custom View in the xml layout file calls the constructor with two parameters, in which it calls the constructor with three parameters, obtains the value of the custom attribute in the constructor, and then draws the basketball continuously with the timer. If you want to draw basketball into the mobile phone interface, you have to rewrite the onDraw() method, put the action of drawing basketball in onDraw(), and postInvalidate() in startDraw(); the function is to call onDraw() method to redraw constantly.

The code in the Ondraw() method is very simple, but there is a RectF class that I have to introduce to you.


The comment in the source code explains this: RectF holds four float coordinates for a rectangle. A rectangle is represented by four coordinates of its sides (left, top and bottom right). These fields can be accessed directly. Use width() and height() to retrieve the width and height of the rectangle. Note: Most methods do not check whether coordinates are properly aligned (for example, left is less than or equal to right and top is less than or equal to bottom).


As shown in the figure, this class defines a range for the arc to be drawn. Four parameters, left,top,right,bottom, are passed in to determine a rectangle, and then a tangent ellipse is drawn inside the rectangle.

canvas.drawArc(oval, 0, mProgress, false, mPaint); this code is used to draw arcs. The first parameter is RectF type, which is to determine the overall trajectory of the arc to be drawn; the second parameter is the starting point, which is where you want the arc to start (0 represents the starting point at right, and -90 represents the starting point at top); the third parameter is the angle between the two points of the arc. Degree determines the length of the arc; the fourth parameter indicates whether to draw a radius, false means to draw only one arc, true will also draw two radii, that is, a sector; the fifth parameter is the brush.

5. Use custom View in layout

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:dapeng="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.dapeng.demo.CustomProgressBar
        android:id="@+id/CustomProgressBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        dapeng:circleWidth="10dp"
        dapeng:firstColor="#FF000000"
        dapeng:secondColor="#FFA7433C"
        dapeng:speed="30" />
</RelativeLayout>

Note to add this line of code: xmlns:dapeng="http://schemas.android.com/apk/res-auto", otherwise the keyword Dapeng will not work. Of course, you can also change the fields you want.



Posted by collamack on Sat, 22 Jun 2019 16:09:27 -0700