Canvas draw, draw a line drawLine, draw a circle drawCircle
val RADIUS = 50f.px class TestView(context:Context? ,attrs:AttributeSet):View(context,attrs) { private val parint = Paint(Paint.ANTI_ALIAS_FLAG) override fun onDraw(canvas: Canvas) { canvas.drawLine(100f,100f,200f,200f,parint) canvas.drawCircle(width/2f,200f, RADIUS,parint); }}Copy the code
Results the following
dp2px
Resources. GetSystem ().getxxx. There is a simpler way to getResources
class Utils { public static float dp2px(float value) { return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, Resources.getSystem().getDisplayMetrics()); }}Copy the code
You can continue to simplify extensions.kt by taking advantage of kotlin’s extension properties
val Float.px
get() = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
this,
Resources.getSystem().displayMetrics
)
Copy the code
And then when you use it, you write dp directly
val RADIUS = 100f.px
Copy the code
Path draw, draw circles, draw squares
Synthetic filling
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {path.reset() // Draw a circle path.addCircle(width/2f,200f, RADIUS, Path, Direction CCW) / / painting side Path. AddRect (width / 2 f - the RADIUS, 200 f, width / 2 f + RADIUS, 200 f + 2 * the RADIUS, the Path, Direction. The CCW)} override fun onDraw(canvas: Canvas) { canvas.drawPath(path,parint) }Copy the code
Reverse, intersecting hollow out
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {path.reset() // Draw a circle path.addCircle(width/2f,200f, RADIUS, Path, Direction CCW) / / painting side Path. AddRect (width / 2 f - the RADIUS, 200 f, width / 2 f + RADIUS, 200 f + 2 * the RADIUS, the Path, Direction. The CW)}Copy the code
fillType
EVEN_ODD, intersecting, odd filling even hollowing
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {path.reset() // Draw a circle path.addCircle(width/2f,200f, RADIUS, Path, Direction CCW) / / painting side Path. AddRect (width / 2 f - the RADIUS, 200 f, width / 2 f + RADIUS, 200 f + 2 * the RADIUS, the Path, Direction. The CW) Path.addcircle (width/2f,200f, RADIUS*1.5f, path.direction.ccw) // Intersects odd to fill even to leave empty path.fillType = path.filltype. EVEN_ODD; }Copy the code
INVERSE_EVEN_ODD reverse, even fill, odd hollow
path.fillType = Path.FillType.EVEN_ODD;
Copy the code
PathMeasure
Fill in the Path object for specific calculation (e.g. graph perimeter) as follows. According to our calculation, the square is 400F perimeter and the printed result is 1100, indicating that the coefficient of dp to PX is 2.75 times
var pathMeasure:PathMeasure path.addRect(width/2f- RADIUS,200f,width/2f+ RADIUS,200f+2*RADIUS,Path.Direction.CW) PathMeasure = pathMeasure (Path,false) log. d(TAG, "onSizeChanged: "+pathMeasure.length)// 100F *4 * 2.75 = 1100Copy the code
Draw a dashboard
-
First draw a circle with an opening of 120 degrees
- You can use canvas.drawArc as follows and draw a 150dp radius fan
Canvas. DrawArc (width/f - 150 f. 2 px, height / 2-150 - f px, width/f + 150 f. 2 px, height/f + 150 f. 2 px, + OPEN_ANGLE / 2360-90 OPEN_ANGLE,false,paint)Copy the code
- In order to add the scale effect later, we use path to draw, which is equivalent to the effect above
path.addArc(width/2f-CIRCLE_RADIUS,height/2-CIRCLE_RADIUS,width/2f+CIRCLE_RADIUS,height/2f+CIRCLE_RADIUS, 90 + OPEN_ANGLE/2,360- OPEN_ANGLE) canvas. DrawPath (path,paint);Copy the code
-
And then I’m going to set my brush style to hollow with a border
-
Scale the dial
- First define the width and height of the scale
- Then add the dotted scale effect
- We draw 20 scales for the dial, need to calculate the scale interval, use PathMeasure to measure
Val pathMessure = PathMeasure(path,false) pathEffect = PathDashPathEffect(dash,(pathmessure.length-dash_width)/20,0f, ROTATE)Copy the code
- Finally, the second time to draw, draw the scale effect
SetPathEffect (pathEffect) canvas. DrawPath (path,paint); paint.pathEffect = null;Copy the code
-
Draw a pointer
- The coordinates of the pointer need to be calculated by trigonometric functions combining Angle and pointer length
-
The complete code is as follows
Const val OPEN_ANGLE = 120F // The sector radius val CIRCLE_RADIUS = 150f.px val LENGTH = 120F.px //3.1 Dash width = 2F.px val DASH_LENGTH = 10f.px class DashboardView (context: Context? ,attrs: AttributeSet): View(context,attrs){ private val paint = Paint(Paint.ANTI_ALIAS_FLAG) private val dash = Path(); private val path = Path(); private lateinit var pathEffect: PathDashPathEffect; StrokeWidth = 3f.px paint. Style = paint. Style.STROKE //3.2 Add scale Effect rectangle scale bar dash.addRect(0f,0f, DASH_WIDTH, DASH_LENGTH,Path.Direction.CCW) } @RequiresApi(VERSION_CODES.LOLLIPOP) override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {//3.3 Set the calibration interval to 20 intervals path.reset() path.addArc(width/2f-CIRCLE_RADIUS,height/2-CIRCLE_RADIUS,width/2f+CIRCLE_RADIUS,height/2f+CIRCLE_RADIUS, 90 + OPEN_ANGLE/2,360- OPEN_ANGLE) val pathMessure = PathMeasure(path,false) pathEffect = PathDashPathEffect (dash, (pathMessure. Length - DASH_WIDTH) / 20, 0 f, ROTATE) } @RequiresApi(VERSION_CODES.LOLLIPOP) override fun onDraw(canvas: Canvas) { //1. // Canvas. DrawArc (width/2f-150f.px,height/2-150f.px,width/2f+150f.px,height/2f+150f.px, width/2f+150f.px,height/2f+150f.px, OPEN_ANGLE/2,360- OPEN_ANGLE,false,paint) canvas. DrawPath (path,paint); SetPathEffect (pathEffect) canvas. DrawPath (path,paint); paint.pathEffect = null; DrawLine (width / 2f, height / 2f, width / 2f + LENGTH * cos(markToRadians(5)).tofloat (), drawLine(width / 2f, height / 2f, width / 2f + LENGTH * cos(markToRadians(5)).tofloat (), drawLine(width / 2f, height / 2f, width / 2f + LENGTH * cos(markToRadians(5)).tofloat (), height / 2f + LENGTH * sin(markToRadians(5)).toFloat(), paint) } private fun markToRadians(mark: Int) = Math.toRadians((90 + OPEN_ANGLE / 2f + (360 - OPEN_ANGLE) / 20f * mark).toDouble()) }Copy the code
The effect that
Draw the pie chart
- Draw the sector with drawArc()
- Move the sector with canvas.translate (), and save and restore the position with canvas.save () and canvas.restore ()
- Use the trigonometric functions cosine and sine to calculate the offset
Drawing process
- First define four sectors and their colors
- Then the for loop draws them in turn
- Finally, a sector is offset
- The complete code is as follows
//1. Private val RADIUS = 25f. Px private val ANGLES = 25f, 25f, 25f 60f) private val COLORS = listOf(Color.parseColor("#C2185B"), Color.parseColor("#00ACC1"), Color.parseColor("#558B2F"), Color.parseColor("#5D4037")) private val OFFSET_LENGTH = 20f.px class PieView (context: Context? ,attrs: AttributeSet): View(context,attrs){ private val paint = Paint(Paint.ANTI_ALIAS_FLAG) @RequiresApi(VERSION_CODES.LOLLIPOP) override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {} @requiresAPI (version_codes.lollipop) override fun onDraw(canvas: canvas) {var startAngle = 0f; //2 for loop draw for ((index, Angle)in ANGLES. WithIndex ()){paint. Color = COLORS[index]; If (index==1){canvas.save() canvas.translate() (OFFSET_LENGTH*Math.cos(Math.toRadians((startAngle+angle/2).toDouble()))).toFloat(), (OFFSET_LENGTH*Math.sin(Math.toRadians((startAngle+angle/2).toDouble()))).toFloat() ) } canvas.drawArc(width/2f-RADIUS,height/2-RADIUS,width/2f+RADIUS,height/2f+RADIUS, startAngle,angle,true,paint) startAngle += angle; //3.2 Reset canvas if (index==1){canvas.restore()}}}}Copy the code
Results the following