Android - Extending View Subclass
In a previous article we discuss the approach of extending a custom view in Android. Another approach to creating a custom View is by extending an existing widget. Extending an existing subclass is relatively easier when compared to extending the whole class. <!--more--> This approach gives the developer existing features and styles to customize. This article will go through creating a custom view using this approach.
Introduction
What we will do
We're going to create a color slider. This view resembles a seek bar and so we will extend it. We'll use the view to select the color of a text in a TextView
.
Let's dive in!
Prerequisites
To follow through with this tutorial, you will need:
- Have Android Studio installed.
- Basic knowledge of building Android applications.
- Basic understanding of Kotlin programming language.
Let's get started
Step 1 — Creating an Android Project
In this step, we're going to create our application. Open Android Studio and start a new project with an empty activity template. On the next page, give the application a name and select API 21 for minimum SDK. This is because the features we will use require Android version 5.0.
Click Finish
and wait for the project build process to finish.
Step 2 — Creating The Color Slider Class
On your project window,
- Select
File -> New -> Kotlin File/Class
- On the next screen select class, give it a name, and press Enter.
On the newly created file. Add the following code to extend the SeekBar
class.
class ColorSlider(context: Context, attrs: AttributeSet): androidx.appcompat.widget.AppCompatSeekBar(context, attrs) {
}
We'll use that constructor so that we can add our view through XML. Visit this page for more details about other constructors.
In this class, add the following member variables.
private val colors = arrayOf(Color.RED, Color.BLACK, Color.YELLOW, Color.BLUE, Color.GRAY, Color.GREEN)
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private var listeners = ArrayList<(Int) -> Unit>()
The first variable is an array of colors from which the user will select. The second one is a paint object that we will use to draw custom tick marks. The third one is an array of functions that will run whenever a user selects a particular color.
Next, add an init
block to perform some customizations on the view upon creation.
init {
progressBackgroundTintList = ContextCompat.getColorStateList(context, android.R.color.transparent)
progressTintList = ContextCompat.getColorStateList(context, android.R.color.transparent)
splitTrack = false
max = 5
}
Here we set the progress background tint and progress tint to transparent. We also set the split track to false to make the seek bar thumb transparent. We set the maximum value to 5 because our array contains six colors.
We override the onDraw
method of the seek bar to create custom tick marks.
Step 3 — Overriding 'onDraw' Method
Now that we have set our view with the desired attributes let's override the onDraw
method to begin drawing. We will make custom-colored square tick marks. The squares will get colors from the colors array.
Let's get to it!
First, override the onDraw
method by adding the code below.
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
}
Here we have to call super.onDraw
to allow the parent view to draw first. Then create a function to draw the tick marks.
private fun drawTickMarks(canvas: Canvas?){
}
Call the function in the onDraw
method.
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
drawTickMarks(canvas)
}
Add the drawTickMarks
implementation as shown.
private fun drawTickMarks(canvas: Canvas?) {
canvas?.let {
val w = 24F
val h = 24F
val spacing = (width - paddingLeft - paddingRight) / max.toFloat()
it.translate(paddingLeft.toFloat(), height / 2 .toFloat())
for (color in colors) {
paint.color = color
it.apply {
drawRect(-w, -h, w, h, paint)
translate(spacing, 0F)
}
}
}
}
In the function, we first check if the canvas is null. We use Kotlin's let function to perform all the tasks. The statement canvas?.let{}
means that if the canvas is not null, it executes the block's code.
We explicitly declare the width and height of the tick marks to 24 pixels in the let function. The value should be a float. For the spacing between the tick marks, we subtract the left and right padding from the width the divide it by max
. We obtain all these values from the super class. The spacing value should also be a float.
Before drawing the squares, we use the translate
method to move the drawing pen to the right position. We then loop through the colors array drawing the squares on the canvas. The drawRect
method draws the squares given the dimensions and paint object. translate
moves the pen to the next drawing position.
That's all for the drawing!
In the init
block, set onSeekBarChangeListener
by adding this block
setOnSeekBarChangeListener(object: OnSeekBarChangeListener{
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
listeners.forEach {
it(colors[progress])
}
}
override fun onStartTrackingTouch(p0: SeekBar?) {
Log.i("Picker", "Tracking started")
}
override fun onStopTrackingTouch(p0: SeekBar?) {
Log.i("Picker", "Tracking stopped")
}
})
This will run the listener functions every time the seek bar progress changes. We also need to add a member function that we'll use to add listeners to the listener array.
fun addListener(function: (Int) -> Unit) {
listeners.add(function)
}
That's all we need for the class. Let's now add the view to an XML file.
Step 4 — Adding the View
Open the activity_main
file and add the following views.
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/Color"
android:textSize="@android:dimen/app_icon_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.45" />
<com.kayere.colorslider.ColorSlider
android:id="@+id/colorSlider"
android:layout_width="260dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView"
app:layout_constraintVertical_bias="0.11" />
As I mentioned earlier, we'll use the color slider to change the color of the text view. For the color slider, you will have to use the package name of your application.
Step 5 — Finishing The application
Open MainActivity
file and add this code in the onCreate
method.
colorSlider.addListener {
textView.setTextColor(it)
}
This will change the color of the text view every time the user selects a color.
That's it! Build and run the app. The results should resemble the one below.
Conclusion
In this article, we have gone through creating a custom view by extending a widget. We have also seen how we can change the view's appearance by drawing. Creating custom views through this approach is a bit easier.
It gives the application a unique appearance with less work to do. Check this article for a guide on creating custom views by extending the View
class. You can find the source code of the application on GitHub. Feel free to raise an issue or a PR if you notice any error.
Peer Review Contributions by: Linus Muema