Custom views can also take custom attributes which can be used in Android layout resource files. To add attributes to your custom view you need to do the following:
Define the name and type of your attributes: this is done inside res/values/attrs.xml (create it if necessary). The following file defines a color attribute for our smiley's face color and an enum attribute for the smiley's expression:
Use your attributes inside your layout: this can be done inside any layout files that use your custom view. The following layout file creates a screen with a happy yellow smiley:
Tip: Custom attributes do not work with the tools: prefix in Android Studio 2.1 and older (and possibly in future versions). In this example, replacing app:smileyColor with tools:smileyColor would result in smileyColor neither being set during runtime nor at design time.
Read your attributes: this is done inside your custom view source code. The following snippet of SmileyView demonstrates how the attributes can be extracted:
(Optional) Add default style: this is done by adding a style with the default values and loading it inside your custom view. The following default smiley style represents a happy yellow one:
Which gets applied in our SmileyView by adding it as the last parameter of the call to obtainStyledAttributes (see code in step 3):
Note that any attribute values set in the inflated layout file (see code in step 2) will override the corresponding values of the default style.
(Optional) Provide styles inside themes: this is done by adding a new style reference attribute which can be used inside your themes and providing a style for that attribute. Here we simply name our reference attribute smileyStyle:
Which we then provide a style for in our app theme (here we just reuse the default style from step 4):
Compound view for SVG/VectorDrawable as drawableRight
Main motive to develop this compound view is, below 5.0 devices does not support svg in drawable inside TextView/EditText. One more pros is, we can set height and width of drawableRight inside EditText.
I have separated it from my project and created in separate module.
Module Name : custom_edit_drawable (short name for prefix- c_d_e)
"c_d_e_" prefix to use so that app module resources should not override them by mistake. Example : "abc" prefix is used by google in support library.
use AppCompat >= 23
Layout file : c_e_d_compound_view.xml
Custom Attributes : attrs.xml
Code : EditTextWithDrawable.java
Example : How to use above view
Layout : activity_main.xml
Activity : MainActivity.java
Creating a compound view
A compound view is a custom ViewGroup that's treated as a single view by the surrounding program code. Such a ViewGroup can be really useful in DDD-like design, because it can correspond to an aggregate, in this example, a Contact. It can be reused everywhere that contact is displayed.
This means that the surrounding controller code, an Activity, Fragment or Adapter, can simply pass the data object to the view without picking it apart into a number of different UI widgets.
This facilitates code reuse and makes for a better design according to SOLID priciples.
The layout XML
This is usually where you start. You have an existing bit of XML that you find yourself reusing, perhaps as an <include/>. Extract it into a separate XML file and wrap the root tag in a <merge> element:
This XML file keeps working in the Layout Editor in Android Studio perfectly fine. You can treat it like any other layout.
The compound ViewGroup
Once you have the XML file, create the custom view group.
The init(Context, AttributeSet) method is where you would read any custom XML attributes as explained in Adding Attributes to Views.
With these pieces in place, you can use it in your app.
Usage in XML
Here's an example fragment_contact_info.xml that illustrates how you'd put a single ContactView on top of a list of messages:
Usage in Code
Here's an example RecyclerView.Adapter that shows a list of contacts. This example illustrates just how much cleaner the controller code gets when it's completely free of View manipulation.
Creating Custom Views
If you need a completely customized view, you'll need to subclass View (the superclass of all Android views) and provide your custom sizing (onMeasure(...)) and drawing (onDraw(...)) methods:
Create your custom view skeleton: this is basically the same for every custom view. Here we create the skeleton for a custom view that can draw a smiley, called SmileyView:
Initialize your paints: the Paint objects are the brushes of your virtual canvas defining how your geometric objects are rendered (e.g. color, fill and stroke style, etc.). Here we create two Paints, one yellow filled paint for the circle and one black stroke paint for the eyes and the mouth:
Implement your own onMeasure(...) method: this is required so that the parent layouts (e.g. FrameLayout) can properly align your custom view. It provides a set of measureSpecs that you can use to determine your view's height and width. Here we create a square by making sure that the height and width are the same:
Note that onMeasure(...) must contain at least one call to setMeasuredDimension(..) or else your custom view will crash with an IllegalStateException.
Implement your own onSizeChanged(...) method: this allows you to catch the current height and width of your custom view to properly adjust your rendering code. Here we just calculate our center and our radius:
Implement your own onDraw(...) method: this is where you implement the actual rendering of your view. It provides a Canvas object that you can draw on (see the official Canvas documentation for all drawing methods available).
Add your custom view to a layout: the custom view can now be included in any layout files that you have. Here we just wrap it inside a FrameLayout:
Note that it is recommended to build your project after the view code is finished. Without building it you won't be able to see the view on a preview screen in Android Studio.
After putting everything together, you should be greeted with the following screen after launching the activity containing the above layout:
CustomView performance tips
Do not allocate new objects in onDraw
Instead of drawing drawables in canvas...
Use a Bitmap for faster drawing:
Do not redraw the entire view to update just a small part of it. Instead redraw the specific part of view.
If your view is doing some continuous animation, for instance a watch-face showing each and every second, at least stop the animation at onStop() of the activity and start it back on onStart() of the activity.
Do not do any calculations inside the onDraw method of a view, you should instead finish drawing before calling invalidate(). By using this technique you can avoid frame dropping in your view.
The basic operations of a view are translate, rotate, etc... Almost every developer has faced this problem when they use bitmap or gradients in their custom view. If the view is going to show a rotated view and the bitmap has to be rotated in that custom view, many of us will think that it will be expensive. Many think that rotating a bitmap is very expensive because in order to do that, you need to translate the bitmap's pixel matrix. But the truth is that it is not that tough! Instead of rotating the bitmap, just rotate the canvas itself!
Responding to Touch Events
Many custom views need to accept user interaction in the form of touch events. You can get access to touch events by overriding onTouchEvent. There are a number of actions you can filter out. The main ones are
ACTION_DOWN: This is triggered once when your finger first touches the view.
ACTION_MOVE: This is called every time your finger moves a little across the view. It gets called many times.
ACTION_UP: This is the last action to be called as you lift your finger off the screen.
You can add the following method to your view and then observe the log output when you touch and move your finger around your view.