Android Custom View series
- (1) To achieve the effect of rounded corner mask
- Android custom View Demo (2) to achieve the circular avatar effect
- Android custom View Demo (3) to achieve the micro channel shot one shot of the animation effect
The Android custom View implements the rounded corner mask effect
A picture is worth a thousand words, and a mask will highlight key areas
This article through two ways to achieve this effect, to achieve the effect of custom View practice
The use of this effect
- In cropping the picture, determine the cropping range
- Guide the user in the APP and highlight an area
This is a sparrow although small viscera complete small Demo, very suitable for practice.
1. The introduction
Learn from this article
- Common and useful APIS for Canvas and Paint
- The use of Xfermode
- How to enable off-screen buffering at the View level
- The difference between Canvas’s off-screen buffer and View’s off-screen buffer
- How to set the use of custom properties for a custom View
2. The first implementation
class RoundRectCoverView(context: Context, attrs: AttributeSet) : View(context, attrs) {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private var mPadding = 40.dp / / spacing
private var mRoundCorner = 10.dp // The Angle of the rounded rectangle
private var mCoverColor = "# 99000000".toColorInt()// The color of the mask
private val porterDuffXfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_OUT)
init {
// Turn on off-screen buffering at the View level, turn off hardware acceleration, and use software to draw
setLayerType(LAYER_TYPE_SOFTWARE, null)}override fun onDraw(canvas: Canvas) {
// Draw a rounded rectangle, which is the transparent area (Destination image)
canvas.drawRoundRect(mPadding, mPadding, width - mPadding, height - mPadding, mRoundCorner, mRoundCorner, paint)
// Set the color of the mask
paint.color = mCoverColor
// Set paint's xferMode to porterduff.mode.src_out
paint.xfermode = porterDuffXfermode
// Draw the mask's rectangle (Source image)
canvas.drawRect(0f.0f, width.toFloat(), height.toFloat(), paint)
// Clear paint's xfermode
paint.xfermode = null
}
Copy the code
SetLayerType (LAYER_TYPE_SOFTWARE, null) is used to enable off-screen buffering at the View level. It is used to take out the entire area of the View, which is transparent. So you might be wondering, why take out such a transparent area? This is required when using porterduff.mode.
What is PorterDuff Mode
In this case, we use porterduff.mode.src_out, which corresponds exactly to Source Out, which is the Mode we want
- Note that the Destination image is the first rounded rectangle to be drawn ina transparent View, which requires off-screen buffering
- The Source image is the painted mask which is the entire View
- Porterduff.mode.src_out, similar to the overlap between the last and the previous drawing, cut out and throw away, the result is what we want
3. The second way of implementation
class RoundRectCoverView(context: Context, attrs: AttributeSet) : View(context, attrs) {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private var mPadding = 40.dp / / spacing
private var mRoundCorner = 10.dp // The Angle of the rounded rectangle
private var mCoverColor = "# 99000000".toColorInt()// The color of the mask
private val bounds = RectF()
private val porterDuffXfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_OUT)
private val clipPath = Path()
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
// Set the range of off-screen buffering
bounds.set(0f.0f, width.toFloat(), height.toFloat())
// Set the rectangle of the Clip Path
clipPath.addRoundRect(mPadding, mPadding, width - mPadding, height - mPadding, mRoundCorner, mRoundCorner, Path.Direction.CW)
}
override fun onDraw(canvas: Canvas) {
//Canvas off-screen buffering
val count = canvas.saveLayer(bounds, paint)
// The KTX extension functions are equivalent to the save and restore operations on Canvas
canvas.withSave {
// Paint the color of the mask
canvas.drawColor(mCoverColor)
// Cut by Path
canvas.clipPath(clipPath)
// Draw the scope of the hollow out
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SRC)
}
// Draw the off-screen cached content to the View
canvas.restoreToCount(count)
}
}
Copy the code
To explain, in the onSizeChanged method, we set the range of the off-screen buffer, note that this range is sufficient, too much performance. At the same time set the Clip Path clipping rectangle area.
In the ontouch method
-
First turn on the off-screen buffering **(notice that this is the Canvas off-screen buffering)** This is the bounds of the bounds
-
Paint the full mask color
-
Then cut the Canvas into a rounded rectangle
-
Then in the words of a transparent color, the Mode is specified as porterduff.mode.src
-
I’m going to draw the off-screen buffer to the View
Note: Remember to turn off the screen buffer, otherwise the results may not be what you want
4. Set custom attributes
4.1 In the values folder, create a file attrs.xml (you can call it anything, but this is the specification).
<resources>
<declare-styleable name="RoundRectCoverView">
<attr name="roundCorner" format="dimension" />
<attr name="roundPadding" format="dimension" />
<attr name="roundCoverColor" format="color" />
</declare-styleable>
</resources>
Copy the code
Declare the properties that you want to set in the XML, for example, I’ve declared the size of the rounded corners, the padding of the rounded rectangle, and the color of the mask
4.2 Dynamically obtaining values set in XML in code
init {
// Get the attributes of the XML configuration through TypeArray
val ta = context.obtainStyledAttributes(attrs, R.styleable.RoundRectCoverView)
mPadding = ta.getDimension(R.styleable.RoundRectCoverView_roundPadding, 40.dp)
mRoundCorner = ta.getDimension(R.styleable.RoundRectCoverView_roundCorner, 10.dp)
mCoverColor = ta.getColor(R.styleable.RoundRectCoverView_roundCoverColor, "# 99000000".toColorInt())
ta.recycle()
}
Copy the code
- Obtained through TypeArray, the value of the declared property
- Assign a value to a member variable, which is used directly in the following example
- Recycling TypeArray
4.3 used directly in XML code
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="0dp"
android:layout_height="0dp"
android:src="@drawable/captain_america"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.jhb.customviewcollection.RoundRectCoverView.RoundRectCoverView
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:roundCorner="10dp"/ / Angleapp:roundCoverColor="#aa000000"// Mask colorapp:roundPadding="30dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
Copy the code
5. To summarize
- The difference between the two ways of off-screen buffer and the way to open
- Canvas.savelayer () can be used for short off-screen buffering. Google doesn’t recommend using it because it takes performance to draw a piece each time
- View.setlayertype () is directly put the whole
View
Are drawn in an off-screen buffer, as Google recommends.setLayerType(LAYER_TYPE_HARDWARE)
Is using the GPU to buffer,setLayerType(LAYER_TYPE_SOFTWARE)
I’m just going to use oneBitmap
To buffer.
- The type of PorterDuff.Mode that has been used requires a transparent background color
- How to customize attributes
This is a simple and use of the Demo, very suitable for practice!
6. Source address
RoundRectCoverView.kt
7. The original address
The Android custom View implements the rounded corner mask effect
8. Refer to articles
hencoder
PorterDuff.Mode
I recommend my open source project WanAndroid client
WanAndroidJetpack architecture diagram
- A pure Android learning project, WanAndroid client.
- Project use
MVVM
Architecture,Kotlin
Voice writing. - Extensive use of Android Jetpack includes, but is not limited to
Lifecycle
,LiveData
,ViewModel
,Databinding
,Room
,ConstraintLayout
And more in the future. - using
Retrofit
和Kotlin-Coroutine
coroutinesNetwork interaction. - Loading pictures
Glide
Mainstream load picture frame. - Data storage is mainly used
Room
And tencentMMKV
.
Kotlin + MVVM + Jetpack + Retrofit + Glide is a good project to learn MMVM architecture.
This project itself is also a special learning Android related knowledge of the APP, welcome to download the experience!
Source code address (with download link)
WanAndroidJetpack
Overview of the APP
Order if you likeStarsIf you have questions, please refer to the following questions: