Drawing views in android

A guide to drawing in android

May 23, 2018 · 3 min read

Introduction

Android has provided us with many powerful componentized model for building your UI. All of these models are based on fundamental layout classes: View and ViewGroup. We can create our own view by simply subclass the widget or layout and override its methods. We you want to know more about custom view I would you to read this article by Roman Danylyk.

Here we will be creating a view which will let us draw on canvas.

We will be creating something like this but bit simpler. Before jumping to the code try out sample app.

I hav e also created a library for android draw. Check out the github repo.

DrawView

Let’s jump directly towards code. First we create a class DrawView which is the subclass of view and initialize paint and path object.

Here comes the important part. We will now override onTouchEvent and create three functions for ACTION_UP, ACTION_DOWN and ACTION_MOVE.

when ACTION_DOWN event is called we will move path to x & y and update the value of mCurX and mCurY.

when ACTION_MOVE is called we will make a quadratic curve. Using quadTo helps to make smooth line on curve.

When ACTION_UP is called we will make a line to mCurX and mCurY.

At the end draw path on canvas in onDraw() function.

HURRAY! We have completed our drawing view. Our whole code will look something like this.tps://writings.jlelse.de

Bonus

To clear your canvas reset path and call invalidate().

Now you can add your view to xml

To check github repo for more features like change strokeWidth, change strokeColor, change Alpha, erase, redo, undo. https://github.com/divyanshub024/AndroidDraw

Also try out the sample app.

If you liked this article make sure to 👏 it below, and follow me on twitter!

Источник

Exploring Android Canvas Drawing— For Shapes, Bitmaps andCustom views.

Jan 9, 2019 · 6 min read

Would you like to

  1. Create your own drawing (UI) on the screen OR create custom views ?
  2. Modify existing views and customize their look and feel ?
  3. Draw any shape, view or just bitmaps ?
  4. Create something which isn’t already available ?

The power of Android for free hand drawing on pen and paper !

Android Canvas gives you exactly that. Just dive in and create your own magic.

If you know the basics and directly want to view the code, find the entire source code here on GitHub.

So what exactly is Android Canvas ?

The Android framework APIs provides a set of 2D d r awing APIs that allow you to render your own custom graphics onto a canvas or to modify existing Views to customize their look and feel. Basically, Canvas is a class in Android that performs 2D drawing onto the screen of different objects.

Your mobile screen is your canvas

Just consider you mobile screen as a blank paper and draw on it. You need to define anchor points and shapes so as to draw on the screen. Remember the school level graphs ? Something very similar.

Define X & Y coordinates and the shape you want.

Create your own custom view class

Just create custom view class. Since you want to draw your own UI , extend View class to get the lifecycle of the basic view hierarchy.

Define a paint object with default colors and styling

Create and initialise the paint object in your constructor only. Most of the times, our basic settings don’t change. We can then use this paint object every where else in the code and only change properties we want.

The magic methods : onDraw() and invalidate()

All of canvas drawing will happen in onDraw method. Whenever you want to draw any custom objects , you set the paint styling, call default draw.. API methods. All these internally call onDraw.

Get your canvas instance in onDraw and save it for drawing.

Every time, you draw something new on the canvas , you need to refresh it. Your entire canvas is re-drawn. And hence you need to perform minimal operations in onDraw().

To tell the view, that is has to refresh use invalidate() method.

Remember our paint object is initialised in constructor so that we don’t create it again and again on draw. OnDraw gets called every single time you want to change anything on the UI. So it’s an expensive call. We don’t want to do anything extra than required on onDraw method.

Drawing basics

A variety of basic draw API’s are available on the canvas object. We can use these basic API;s to create our own custom shapes and figures. some common ones are :

Читайте также:  Эта папка доступна только для чтения android

  • drawCircle
  • drawLine
  • drawOval
  • drawPoints
  • drawText
  • drawRect
  • drawPath

Draw Line

You define the two points with their x, y coordinates and draw path between them.

Draw Circle

The simplest shape. You just need to specify the x coordinate, y coordinate on the screen and the radius. Also set any paint color if you want.

Draw Rectangle

Create a rectangle with x, y, height, width.

Draw Square

Create a rectangle object, with the required coordinates, with the same width and height.

Getting tougher : Draw Triangle ()

Triangle is basically three vertices connected with a line. You need to find those three vertices and draw a line between them.

Below we draw an equilateral triangle

Update Canvas

If you follow the MVP / MVVM / etc other architectural pattern, you might want to refresh your canvas from other layers. Just get the canvas object , do all your business logic for drawing, and then run invalidate.

View or SurfaceView ?

If you want to know more about multi-threading

[Use View : If your application does not require a significant amount of processing or frame-rate speed (perhaps for a chess game, a snake game, or another slowly-animated application). In the same thread as your UI Activity, wherein you create a custom View component in your layout, call invalidate() and then handle the onDraw() callback.

Use SurfaceView — If you have high computation or so the application doesn’t to wait until the system’s View hierarchy is ready to draw and want to run in a separate thread, wherein you manage a SurfaceView and perform draws to the Canvas as fast as your thread is capable (you do not need to request invalidate() )]

You can deep dive into the code here on GitHub and check out details there.

In the next article, we will learn more on handling touch events on Canvas like touch, click, long press, etc.

Thats’ it. Thank you for reading. Please let me know what you liked in the article and what would you like to know more.

Источник

Drawing views in android

DrawingView For Android

DrawingView allows the user to draw with different brushes and provides some features.

  • Provide multible brushes
  • Drawing on top of images
  • Undo/redo operations
  • Zooming in/out & translation
  • Preview the selected brush in BrushView
  • Custom background color
  • Export a drawing as a Bitmap

Add the following to your layout file:

brush_size value should be between 0 and 1, otherwise an exception will be thrown.

You can use the BrushSettings to:

  • Change the brush size
  • Change the brush color
  • Change the selected brush

Enable undo and redo functionality:

Undo and redo are disabled by default. You can enable them by calling:

And here is an example of a complete implementation:

And remember to update your undo button when you call clear() or setBackgroundImage():

Drawing on images:

You can draw on top of an image by calling the following method:

But please remember that calling this method clears any previous drawings if any, and if the image is larger than the view it will be scaled down.

How to get your drawing?

In most casses you want to use this method to get your drawing:

But if you are only intersted in the drawing without the background color and image you can use:

When the DrawingView is in the Zoom Mode it does not draw anything, touch events are used to zoom and move the drawing. The following code show you how to enter and exit the Zoom Mode

You can use the BrushView to show a preview of the selected brush. Add the following to your layout file:

Источник

How to create custom views in android?

Before diving into the process of creating a custom view, It would be worth stating why we may need to create custom views.

  • Uniqueness: Create something that cannot be done by ordinary views.
  • Optimisation: A lot of times we tend to add multiple views or constraints to create the desired view that can be optimized drastically in terms of draw, measure or layout time.

The best way to start would be to understand how android manages view groups and lays out views on the screen. Let us take a look at the diagram below.

onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int)
Every parent view passes a height and width constraint to its child view based on which the child view decides how big it wants to be. The child view then calls setMeasuredDimension() to store its measured width and height.

How are these constraints passed?

Android uses a 32-bit int called the measure spec to pack a dimension and its mode. The mode is a constraint and can be of 3 types:

  • MeasureSpec.EXACTLY: A view should be absolutely the same size as dimension passed along with spec. Eg. layout_width= “100dp”, layout_width=”match_parent”,layout_weight=”1″.
  • MeasureSpec.AT_MOST: A view can have maximum height/width of dimension passed. However, it can be also smaller if it wishes to be. Eg android:layout_width=”wrap_content”
  • MeasureSpec.UNSPECIFIED: A view can be of any size. This is passed when we are using a ScrollView or ListView as our parent.
Читайте также:  Google play android developer что это

onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int)
Android applies any offsets or margins and calls this method to inform your view about where exactly it would be placed on the screen. Unlike onMeasure, it is called only once during the traversal. So it is recommended to perform any complex calculations in this method.

onDraw(canvas: Canvas)
Finally, Android provides you with a 2D drawing surface i.e the canvas on which you can draw using a paint object.

The UI thread then passes display lists to render thread which does a lot of optimizations and finally GPU process the data passed to it by render thread.

How to define attributes for your view?

Declaring XML attributes is simple. You just need to add a declarable-style in your attrs.xml and declare a format for every attribute.
For instance, if you are creating a simple view which displays a circle with its label. Your attributes may look like this.

The same is referenced while creating a view in the following manner.

Now, we have to parse these attributes in your java or kotlin class.

  • Create your view class which extends the android.view class
  • Obtain a reference to the attributes declared in XML. While attrs is passed in the constructor, the second parameter is a reference to the styleable we just declared. The latter two are used for getting default style attributes in theme or supplying a default style attributes.
  • Parsing the attribute arguments

Android automatically handles the process of converting dp or sp to the right amount of pixels according to screen size when parsing a dimension attribute. But, You need to ensure that the fallback value is converted to appropriate pixel value since android returns fallback value without any conversions if an attribute value is not defined in XML.

While parsing all other attributes is quite straightforward. I will brief you about how to parse flags. Declaring flags attributes can be really useful sometimes since we can check for multiple properties using a single attribute. This is the same way android handles the visibility flag.

colorType here is an integer which represents a flagSet. Now, since every bit in an integer can be used to represent an indication. We can check if a flag exists and perform our operations accordingly. To check if a flag type stroke exits, we can simply perform an or operation on flagSet with the stroke value. If the result stays the same that means the flag actually exists in the flagSet.

  • Finally, recycle the typed array to be used by the later caller.

Initialising your objects
It is always better to initialize your paint and path objects or other variables in the constructor itself. Since declaring it any other traversal method may result in the meaningless creation of objects again and again.

Calculating your view size

Calculating view size can be really challenging sometimes. You have to make sure that your view does not take any extra pixel or request any less pixel as it may end up showing extra white space or not showing complete view respectively. These are the basic steps that you need to follow to calculate the size of your view.

  • Calculate how much width and height your view requires. For instance, if you are drawing a simple circle with its label below the circle. The suggested width would be :
    (circle diameter+ any extra width if occupied by the label).
  • Calculate the desired width by adding the suggested width with paddingStart and paddingEnd. Similarly, desiredHeight would be suggested height plus paddingTop & paddingBottom.
  • Calculate actual size respecting the constraints. To calculate this, you simply need to pass measure spec passed to you in onMeasure() and your desired dimension in this method called resolveSize(). This method would tell you closest possible dimension to your desired width or height while still respecting its parent’s constraints.
  • Most importantly, you need to set the final width and height in onMeasure method by calling setMeasuredDimension(measuredWidth,measuredHeight) to store the measured width and height of this view otherwise, you might see your view crashing with an IllegalStateException.

Positioning your views

We can position our child views by using the onLayoutMethod. The code simply may involve iterating over any child views and assigning them a left, top, right and a bottom bound depending on measured widths and heights.

Drawing your view

Before using the canvas there are few things that we need to understand:

  • Paint: The Paint class holds the style and color information about how to draw geometries, text, and bitmaps. Here is how we create a paint object.

You can read about more about the properties here.

  • Drawing Shapes: You can directly draw shapes like a line, arc, circle etc on the canvas. Let us take a look at the diagram below to gain a better understanding.

Using Paths: Drawing complex shapes with the above methods may get a bit complex so android offers a Path class. With the Path class, you can imagine that you are holding a pen and you can draw a shape, then maybe move to a different position and draw another shape. Finally, when you are done creating a path. You can simply draw the path on the canvas like this. Also, when using paths you can also use different path effects (discussed below in detail). Below, is an example of the shape created using paths.

Читайте также:  Shadowgun кеш для андроид

  • Path Effects: If you also apply a Corner path effect to your paint object with a certain radius the polygon will look like this. You can also use other path effects like DashPathEffect, DiscretePath etc. To combine two different path effects you can use the ComposePathEffect.

bitmap: Bitmap that you want to draw on canvas
src: It takes a rect object which specifies the portion of the bitmap you want to draw. This can be null if you want to draw the complete bitmap.
dest: A rect object which tells how much area do you want to cover on the canvas with the bitmap
paint: The paint object with which you want to draw the bitmap

Android automatically does all the necessary scaling or translation to fit the source on destination area.

You can also draw drawables on canvas.

Before drawing a drawable, you would need to set bounds to your drawable. The left, top, right and bottom describe the drawable’s size and its position on the canvas. You can find the preferred size for Drawables using getIntrinsicHeight() and getIntrinsicWidth() methods and decide bounds accordingly.

Drawing Texts: Drawing texts can be a bit of pain. Not the drawing itself, but the alignment or measurement of text. This occurs because different characters have different heights and to make it more worse there can be different typefaces too. So to measure a text’s height you would need to calculate specific text bounds for your text like this.

Then, the rect object passed in the end would then contain the text bounds of actual text to be drawn. This way you can calculate the actual height of text to be drawn and set a correct baseline y for your text. To calculate the width of your text you should use textPaint.measureText() as it is more accurate than the width given by paint text bounds (because of the way these methods are implemented in skia library). Alternatively, for ensuring the text is centered horizontally on the canvas you can just set your paint’s alignment to TextAlign.CENTER and pass center point of your canvas in the x coordinate.

Drawing multiline text: If you want to handle line breaks (\n) or If you have a fixed width to draw a text you can use Static Layout or Dynamic Layout. This would automatically handle all the word breaks or line breaks and also tell you how much height would be needed to draw a text in given width.

  • Saving & Restoring Canvas: As you might have noticed, we need to save the canvas and translate it before drawing on it and finally we have to restore the canvas. A lot of times we need to draw something with a different setting such as rotating the canvas, translating it, or clipping a certain part of canvas while drawing a shape. In this case, we can call canvas.save() which would save our current canvas settings on a stack. After this, we change canvas settings ( translation etc) and then draw whatever we want to with these settings. Finally, when we are done drawing we can call canvas.restore() which would restore canvas to the previous configuration that we had saved.
  • Handling User Inputs: Finally, you have created your own custom view using XML attributes, BUT what if you want to change any property at runtime such as the radius of the circle, text color etc. You would need to inform Android API’s to reflect the changes. Now, if any change in property affects the size of your view you will set the variable and call requestLayout() which would recalculate your view’s size and redraw it. However, if a property like a text color is changed you would only need to redraw it with new text paint color and in this case, it would be wise to just call invalidate().

Additional Note: Now if your view has a lot of attributes, there may be a lot of times you would have to write invalidate()/requestLayout after every setter. This problem can be solved by using kotlin’s delegates. Let us take a look a the example below to be more clear.

Now, If I know that a property if changed should only redraw the view, I would initialize it using OnValidateProp but if it can affect the size of the view I would initialize by creating a new OnLayoutProp delegate.

Finally! You can start by creating your own custom views. If you are interested to see what an actual custom view code looks like. You can check out the library that I just published. It displays steps along with the descriptions and covers most of the things that I have discussed in this article.

Источник

Оцените статью