MotionLayout Example
In this tutorial you will learn about motionlayout and look at examples of how to use it to manage animations in your app.
What is MotionLayout?
But first what is it?
MotionLayout is a layout type that helps you manage motion and widget animation in your app.
This class subclasses and builds upon the flexible ConstraintLayout
. It is thus available starting from API level 14. It bridges the gap between layout transitions and complex motion handling, offering a mix of features between the property animation framework, TransitionManager, and CoordinatorLayout.
You can read more about motionlayout here.
Example 1: MotionLayout - Parallax Scrolling using MotionLayout
This is a simple example that introduces you to motionlayout by implementing Parallax scrolling effect on content.
This is important especially on detail pages to provide a modern experience to users. Here's a demonstration of what is being built:
Step 1: Dependencies
No third party dependency is needed for this project.
Step 2: Define Motions
You need to define motions as xml resource files. First create folder known as xml
under your res
directory and add the following two scenes:
scene_header.xml
In this file define a root MotionScene
element:
<MotionScene
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:motion="http://schemas.android.com/apk/res-auto">
....
Define a transition, passing in the start, the end as well as duration:
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/end"
motion:duration="1000"
motion:motionInterpolator="linear">
<OnSwipe
motion:touchAnchorId="@+id/image"
motion:touchAnchorSide="bottom"
motion:dragDirection="dragUp"/>
Here's the full code:
<?xml version="1.0" encoding="utf-8"?>
<MotionScene
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:motion="http://schemas.android.com/apk/res-auto">
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/end"
motion:duration="1000"
motion:motionInterpolator="linear">
<OnSwipe
motion:touchAnchorId="@+id/image"
motion:touchAnchorSide="bottom"
motion:dragDirection="dragUp"/>
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@id/image"
android:layout_width="match_parent"
android:layout_height="220dp"
android:alpha="1.0"
motion:layout_constraintTop_toTopOf="parent"/>
<Constraint
android:id="@id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintBottom_toBottomOf="@+id/guideline"
motion:layout_constraintStart_toStartOf="parent"/>
<Constraint
android:id="@id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
motion:layout_constraintGuide_begin="210dp"/>
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@id/image"
android:layout_width="match_parent"
android:layout_height="220dp"
android:alpha="0"
android:translationX="0dp"
android:translationY="0dp"
motion:layout_constraintTop_toTopOf="parent"/>
<Constraint
android:id="@id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleX="0.6"
android:scaleY="0.6"
motion:layout_constraintBottom_toBottomOf="@+id/guideline"
motion:layout_constraintStart_toStartOf="parent"/>
<Constraint
android:id="@id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
motion:layout_constraintGuide_begin="56dp"/>
</ConstraintSet>
</Transition>
</MotionScene>
scene_main.xml
<?xml version="1.0" encoding="utf-8"?>
<MotionScene
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:motion="http://schemas.android.com/apk/res-auto">
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/end"
motion:duration="300"
motion:motionInterpolator="linear">
<OnSwipe
motion:touchAnchorId="@+id/header"
motion:touchAnchorSide="bottom"
motion:dragDirection="dragUp" />
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@id/header"
android:layout_width="match_parent"
android:layout_height="220dp"
motion:layout_constraintTop_toTopOf="parent" />
<Constraint
android:id="@id/contents"
android:layout_width="match_parent"
android:layout_height="0dp"
motion:layout_constraintTop_toBottomOf="@+id/header"
motion:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="56dp"
motion:layout_constraintTop_toTopOf="parent"
motion:progress="1"/>
<Constraint
android:id="@id/contents"
android:layout_width="match_parent"
android:layout_height="0dp"
motion:layout_constraintTop_toBottomOf="@+id/header"
motion:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>
</Transition>
</MotionScene>
Step 3: Create Layouts
The next step is to create your xml layouts. There are three such layouts:
(a). header_scroll.xml
The root element in this layout will be the MotionLayout
:
<androidx.constraintlayout.motion.widget.MotionLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000"
app:layoutDescription="@xml/scene_header">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="220dp"
android:scaleType="centerCrop"
android:src="@drawable/flower"/>
<TextView
android:id="@+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Lorem Ipsum"
android:textSize="30dp"
android:padding="10dp"
android:textColor="#FFFFFF"/>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="210dp"/>
</androidx.constraintlayout.motion.widget.MotionLayout>
(b). contents_scroll.xml
In the content, place a textview inside a NestedScrollView, this will be the widget to show the details content:
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/contents"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/large_text"
android:padding="10dp"/>
</androidx.core.widget.NestedScrollView>
(c). activity_main.xml
This is a simple layou. Simply include the header_scroll
and content_scroll
inside a MotionLayout
in this layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutDescription="@xml/scene_main"
tools:context=".MainActivity">
<include layout="@layout/header_scroll"/>
<include layout="@layout/contents_scroll"/>
</androidx.constraintlayout.motion.widget.MotionLayout>
Step : Write Code
The next step is to write your MainActivity
code, in this case in Kotlin Programming language. This will be your launcher activity.
Here's the full code:
MainActivity.kt
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
Step : Run Code
Afetr following the abobe steps run the code in android studio. Scroll above to the description of this project to see the result.
Reference
You can find the download links to the project below:
Number | Link |
---|---|
1. | Download code |
2. | Follow code author |
More Examples
Here are more examples
How to Animated Profile Page with MotionLayout
Android Sample to use MotionLayout written in Kotlin..
Here is the demo GIF:
Let us look at a full MotionLayout
Example code:
Step 1. Dependencies
We need to add some dependencies in our app/build.gradle
file as shown below:
(a). build.gradle
Our app-level
build.gradle
.
We Prepare our dependencies as shown below. You may use later versions.
At the top of our app/build.gradle
we will apply the following 3 plugins:
- Our
com.android.application
plugin. - Our
kotlin-android
plugin. - Our
kotlin-android-extensions
plugin.
We then declare our app dependencies under the dependencies
closure, using the implementation
statement. We will need the following 5 dependencies:
- Our
Kotlin-stdlib-jdk7
library. Material
- Collection of Modular and customizable Material Design UI components for Android.Appcompat
- Allows us access to new APIs on older API versions of the platform (many using Material Design).Core-ktx
- With this we can target the latest platform features and APIs while also supporting older devices.Constraintlayout
- This allows us to Position and size widgets in a flexible way with relative positioning.
Here is our full app/build.gradle
:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
applicationId "ir.alirezaiyan.profile"
minSdkVersion 16
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.google.android.material:material:1.2.0-alpha04'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
Step 2. Create Motion XML
Create a directory known as xml
inside your res
directory and add the following profile_motion
transition animations:
(a). profile_motion.xml
<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:motion="http://schemas.android.com/apk/res-auto">
<Transition
motion:constraintSetEnd="@id/end"
motion:constraintSetStart="@id/start">
<OnSwipe
motion:dragDirection="dragUp"
motion:touchAnchorId="@id/recyclerview"
motion:touchAnchorSide="top" />
<KeyFrameSet>
</KeyFrameSet>
</Transition>
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
android:translationY="-25dp"
motion:layout_constraintTop_toBottomOf="@+id/avatarImageView">
</Constraint>
<Constraint
android:id="@+id/avatarImageView"
android:layout_width="match_parent"
android:layout_height="250dp"
android:elevation="2dp"
android:scaleX="1"
android:scaleY="1"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent">
<CustomAttribute
motion:attributeName="CornerRadius"
motion:customIntegerValue="20" />
</Constraint>
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
motion:layout_constraintEnd_toEndOf="parent"
android:translationY="0dp"
motion:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="250dp"
motion:layout_constraintTop_toTopOf="parent">
</Constraint>
<Constraint
android:id="@+id/avatarImageView"
android:layout_width="250dp"
android:layout_height="250dp"
android:elevation="0dp"
android:scaleX="0.5"
android:scaleY="0.5"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="@id/recyclerview"
motion:layout_constraintBottom_toTopOf="@id/recyclerview">
<CustomAttribute
motion:attributeName="CornerRadius"
motion:customIntegerValue="500" />
</Constraint>
</ConstraintSet>
</MotionScene>
Step 3. Design Layouts
For this project let's create the following layouts:
(a). item1.xml
Our
item1
layout.
This layout will represent our 's layout. Specify androidx.cardview.widget.CardView
as it's root element then inside it place the following widgets:
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="@color/colorPrimary"
android:layout_marginBottom="10dp"
app:cardCornerRadius="12dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="200dp">
<ImageView
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="40dp"
android:background="?selectableItemBackgroundBorderless"
android:padding="4dp"
android:src="@drawable/ic_baseline_star_border_24" />
<TextView
android:id="@+id/username"
style="@style/TextAppearance.AppCompat.Light.SearchResult.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="64dp"
android:text="Alfred cervantes"
android:textColor="#FFF"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/phonenumber"
style="@style/TextAppearance.AppCompat.Small"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/username"
android:layout_centerHorizontal="true"
android:layout_marginTop="8dp"
android:textColor="#FFF"
tools:ignore="HardcodedText"
tools:text="(800) 555-8501" />
<ImageView
android:id="@+id/voicecall"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_alignParentBottom="true"
android:layout_margin="16dp"
android:background="?selectableItemBackgroundBorderless"
android:padding="4dp"
android:src="@drawable/ic_baseline_voicemail_24" />
<ImageView
android:id="@+id/videocall"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_alignBaseline="@id/voicecall"
android:layout_alignParentBottom="true"
android:layout_margin="16dp"
android:layout_toRightOf="@id/voicecall"
android:background="?selectableItemBackgroundBorderless"
android:padding="4dp"
android:src="@drawable/ic_baseline_duo_24" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
(b). item2.xml
Our
item2
layout.
It has the same widgets as the first layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
app:cardBackgroundColor="@color/colorPrimary"
app:cardCornerRadius="12dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="200dp">
<ImageView
android:id="@+id/videocall"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentRight="true"
android:layout_margin="16dp"
android:background="?selectableItemBackgroundBorderless"
android:padding="4dp"
android:src="@drawable/ic_baseline_duo_24" />
<TextView
android:id="@+id/phonenumber1"
style="@style/TextAppearance.AppCompat.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/videocall"
android:layout_centerHorizontal="true"
android:textColor="#FFF"
tools:ignore="HardcodedText"
android:text="(800) 555-8501" />
<TextView
android:id="@+id/phonenumber2"
style="@style/TextAppearance.AppCompat.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/phonenumber1"
android:layout_centerHorizontal="true"
android:layout_marginTop="8dp"
android:textColor="#FFF"
tools:ignore="HardcodedText"
android:text="(800) 555-8502" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
(c). item3.xml
Our
item3
layout.
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
app:cardBackgroundColor="@color/colorPrimary"
app:cardCornerRadius="12dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="200dp">
<ImageView
android:id="@+id/videocall"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentRight="true"
android:layout_margin="16dp"
android:background="?selectableItemBackgroundBorderless"
android:padding="4dp"
android:src="@drawable/ic_baseline_contact_mail_24" />
<TextView
android:id="@+id/address1"
style="@style/TextAppearance.AppCompat.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/videocall"
android:layout_centerHorizontal="true"
android:textColor="#FFF"
tools:ignore="HardcodedText"
android:text="[email protected]" />
<TextView
android:id="@+id/address2"
style="@style/TextAppearance.AppCompat.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/address1"
android:layout_centerHorizontal="true"
android:layout_marginTop="8dp"
android:textColor="#FFF"
tools:ignore="HardcodedText"
android:text="[email protected]" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
(d). activity_main.xml
Our
activity_main
layout.
This layout will represent our Main Activity's layout. Specify androidx.constraintlayout.motion.widget.MotionLayout
as it's root element then inside it place the following widgets:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/profileLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimaryDark"
android:fitsSystemWindows="true"
app:layoutDescription="@xml/profile_motion"
tools:context=".MainActivity"
tools:motionProgress="0.0"
tools:showPaths="true"
tools:visibility="visible">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="@color/colorPrimaryDark"
android:clipToPadding="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/avatarImageView"
tools:itemCount="50"
tools:listitem="@layout/item1" />
<ImageView
android:id="@+id/newrelease"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_margin="8dp"
android:background="?selectableItemBackgroundBorderless"
android:padding="4dp"
android:src="@drawable/ic_baseline_new_releases_24"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/menu"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_margin="8dp"
android:background="?selectableItemBackgroundBorderless"
android:padding="4dp"
android:src="@drawable/ic_baseline_more_horiz_24"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ir.alirezaiyan.profile.AvatarView
android:id="@+id/avatarImageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:src="@drawable/avatar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:targetApi="lollipop" />
</androidx.constraintlayout.motion.widget.MotionLayout>
Step 4. Write Code
Finally we need to write our code as follows:
(a). AvatarView.kt
Our
AvatarView
class.
Create a Kotlin file named AvatarView.kt
and add the necessary imports. Here are some of the imports we will be using:
1. BitmapDrawable
from the android.graphics.drawable
package.
2. ColorDrawable
from the android.graphics.drawable
package.
3. Drawable
from the android.graphics.drawable
package.
4. AttributeSet
from the android.util
package.
5. AppCompatImageView
from the androidx.appcompat.widget
package.
Then extend the AppCompatImageView
and add its contents as follows:
First override these callbacks:
onDraw(canvas: Canvas)
.onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int)
.
Then we will be creating the following functions:
init()
.setCornerRadius(parameter)
- Pass to this method aInt
object as a parameter.getBitmapFromDrawable(parameter)
- We pass aDrawable?
object as a parameter.resizeBitmap(width: Int, height: Int): Bitmap
.
(a). Our setCornerRadius()
function
Write the setCornerRadius()
function as follows:
(b). Our resizeBitmap()
function
Write the resizeBitmap()
function as follows:
private fun resizeBitmap(width: Int, height: Int): Bitmap {
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
mBitmap?.let {
mMatrix.setScale(width.toFloat() / it.width, height.toFloat() / it.height)
canvas.drawBitmap(it, mMatrix, null)
}
return bitmap
}
(c). Our getBitmapFromDrawable()
function
Write the getBitmapFromDrawable()
function as follows:
private fun getBitmapFromDrawable(drawable: Drawable?): Bitmap? {
if (drawable == null) {
return null
}
return if (drawable is BitmapDrawable) {
drawable.bitmap
} else try {
val bitmap: Bitmap = if (drawable is ColorDrawable) {
Bitmap
.createBitmap(width, height, Bitmap.Config.ARGB_8888)
} else {
Bitmap.createBitmap(
drawable.intrinsicWidth, drawable.intrinsicHeight,
Bitmap.Config.ARGB_8888
)
}
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
bitmap
} catch (e: Exception) {
e.printStackTrace()
null
}
}
(d). Our init()
function
Write the init()
function as follows:
private fun init() {
mBitmap = getBitmapFromDrawable(drawable)
val mBitmapShader =
BitmapShader(mBitmap!!, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
mBitmapPaint.shader = mBitmapShader
mBitmapPaint.isAntiAlias = true
}
Here is the full code:
package replace_with_your_package_name
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.*
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageView
class AvatarView : AppCompatImageView {
private var mBitmap: Bitmap? = null
private var cornerRadius = 50f
private val mBitmapPaint = Paint()
private val mMatrix = Matrix()
private val mRectF = RectF()
constructor(context: Context?) : super(context) {
init()
}
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
init()
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
val a =
context.obtainStyledAttributes(attrs, R.styleable.AvatarView, defStyleAttr, 0)
cornerRadius = a
.getDimensionPixelSize(R.styleable.AvatarView_av_radius, cornerRadius.toInt()).toFloat()
a.recycle()
init()
}
private fun init() {
mBitmap = getBitmapFromDrawable(drawable)
val mBitmapShader =
BitmapShader(mBitmap!!, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
mBitmapPaint.shader = mBitmapShader
mBitmapPaint.isAntiAlias = true
}
@SuppressLint("DrawAllocation")
override fun onDraw(canvas: Canvas) {
if (mBitmap == null) {
return
}
mBitmapPaint.shader = BitmapShader(
resizeBitmap(width, height), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP
)
mRectF.set(0F, 0F, width.toFloat(), height.toFloat())
canvas.drawRoundRect(
mRectF,
cornerRadius,
cornerRadius,
mBitmapPaint
)
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec)
}
fun setCornerRadius(cornerRadius: Int) {
this.cornerRadius = cornerRadius.toFloat()
invalidate()
}
private fun getBitmapFromDrawable(drawable: Drawable?): Bitmap? {
if (drawable == null) {
return null
}
return if (drawable is BitmapDrawable) {
drawable.bitmap
} else try {
val bitmap: Bitmap = if (drawable is ColorDrawable) {
Bitmap
.createBitmap(width, height, Bitmap.Config.ARGB_8888)
} else {
Bitmap.createBitmap(
drawable.intrinsicWidth, drawable.intrinsicHeight,
Bitmap.Config.ARGB_8888
)
}
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
bitmap
} catch (e: Exception) {
e.printStackTrace()
null
}
}
private fun resizeBitmap(width: Int, height: Int): Bitmap {
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
mBitmap?.let {
mMatrix.setScale(width.toFloat() / it.width, height.toFloat() / it.height)
canvas.drawBitmap(it, mMatrix, null)
}
return bitmap
}
}
(b). MainActivity.kt
Our
MainActivity
class.
Create a Kotlin file named MainActivity.kt
and add the necessary imports. Here are some of the imports we will be using:
1. ViewGroup
from the android.view
package.
2. AppCompatActivity
from the androidx.appcompat.app
package.
3. LinearLayoutManager
from the androidx.recyclerview.widget
package.
4. RecyclerView
from the androidx.recyclerview.widget
package.
5. *
from the kotlinx.android.synthetic.main.activity_main
package.
Then extend the AppCompatActivity
and add its contents as follows:
First override these callbacks:
onCreate(savedInstanceState: Bundle?)
.onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder
.onBindViewHolder(holder: ViewHolder, position: Int)
.
Here is the full code:
package replace_with_your_package_name
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerview.layoutManager = LinearLayoutManager(baseContext)
recyclerview.adapter = Adapter()
newrelease.setOnClickListener {
Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("http://github.com/rezaiyan/awesomeprofile")
}.also { startActivity(it) }
}
}
class Adapter() : RecyclerView.Adapter<Adapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val layout = when (viewType) {
0 -> R.layout.item1
1 -> R.layout.item2
else -> R.layout.item3
}
return ViewHolder(
LayoutInflater.from(parent.context).inflate(
layout,
parent,
false
)
)
}
override fun getItemViewType(position: Int) = position
override fun getItemCount() = 3
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
}
}
Reference
Download the code below:
No. | Link |
---|---|
1. | Download Full Code |
2. | Read more here. |
3. | Follow code author here. |
Kotlin Android MotionLayout Carousel Example
This is MotionLayout Carousel step by step example.
This example will comprise the following files:
MainActivity.kt
Step 1: Create Project
- Open your
AndroidStudio
IDE. - Go to
File-->New-->Project
to create a new project.
Step 2: Add Dependencies
No third party dependency is needed for this project.
Step 3: Design Layouts
(a). activity_main.xml
Create a file named activity_main.xml
and design it as follows:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/Theme.MotionCarouselExample.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/Theme.MotionCarouselExample.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.constraintlayout.motion.widget.MotionLayout
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutDescription="@xml/carousel_scene"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<TextView
android:id="@+id/textView0"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginEnd="16dp"
android:text="textView0"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/textView1"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView1"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginEnd="16dp"
android:text="textView1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/textView2"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView2"
android:layout_width="150dp"
android:layout_height="150dp"
android:text="textView2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView3"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginStart="16dp"
android:text="textView3"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/textView2"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView4"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginStart="16dp"
android:text="textView4"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/textView3"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="100dp" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_end="100dp" />
<androidx.constraintlayout.helper.widget.Carousel
android:id="@+id/carousel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:carousel_infinite="true"
app:carousel_backwardTransition="@+id/backward"
app:carousel_firstView="@+id/textView2"
app:carousel_forwardTransition="@+id/forward"
app:carousel_nextState="@+id/next"
app:carousel_emptyViewsBehavior="invisible"
app:carousel_previousState="@+id/previous"
app:constraint_referenced_ids="textView0,textView1,textView2,textView3,textView4" />
</androidx.constraintlayout.motion.widget.MotionLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Step 4: Write Code
Write Code as follows:
(a). MainActivity.kt
Create a file named MainActivity.kt
Here is the full code
package jp.numero.carousel_example
import android.graphics.Color
import android.os.Bundle
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.helper.widget.Carousel
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import org.w3c.dom.Text
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(findViewById(R.id.toolbar))
val list = listOf(
Color.MAGENTA,
Color.RED,
Color.CYAN,
Color.BLUE,
Color.GREEN,
Color.YELLOW,
Color.LTGRAY
).mapIndexed { index, color ->
Item(index.toString(), color)
}.toMutableList()
val carousel = findViewById<Carousel>(R.id.carousel)
carousel.setAdapter(object : Carousel.Adapter {
override fun count(): Int = list.size
override fun populate(view: View, index: Int) {
if (view !is TextView) return
val item = list[index]
//view.text = item.text
view.setBackgroundColor(item.color)
}
override fun onNewItem(index: Int) {
}
})
}
}
data class Item(
val text: String,
val color: Int
)
Run
Simply copy the source code into your Android Project,Build and Run.
Reference
Kotlin Android MotionLayout Pager Example
Let us look at a Android MotionLayoutPager using this example. Basically we implement a ViewPager using MotionLayout
.
Here is the demo GIF:
This example will comprise the following files:
MainActivity.kt
Step 1: Create Project
- Open your
AndroidStudio
IDE. - Go to
File-->New-->Project
to create a new project.
Step 2: Add Dependencies
In your app/build.gradle
add dependencies as shown below:
First enable Java8:
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
No third party dependency is needed. We use the standard androidx libraries. Make sure to include ConstraintLayout
:
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.1.0'
implementation 'com.google.android.material:material:1.2.0-alpha03'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4'
}
Step 3: Design Layouts
(a). activity_main.xml
Create a file named activity_main.xml
and design it as follows:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
style="@style/Widget.MaterialComponents.AppBarLayout.Primary"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
style="@style/Widget.MaterialComponents.Toolbar.Primary"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.constraintlayout.motion.widget.MotionLayout
android:id="@+id/motionLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutDescription="@xml/motion_list"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<com.google.android.material.card.MaterialCardView
android:id="@+id/leftView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cardElevation="4dp">
<TextView
android:id="@+id/leftTextView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:textAppearance="?attr/textAppearanceHeadline5"
android:textColor="?attr/colorOnSurface" />
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:id="@+id/rightView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cardElevation="4dp">
<TextView
android:id="@+id/rightTextView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:textAppearance="?attr/textAppearanceHeadline5"
android:textColor="?attr/colorOnSurface" />
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:id="@+id/centerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cardElevation="4dp">
<TextView
android:id="@+id/centerTextView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:textAppearance="?attr/textAppearanceHeadline5"
android:textColor="?attr/colorOnSurface" />
</com.google.android.material.card.MaterialCardView>
</androidx.constraintlayout.motion.widget.MotionLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Step 4: Write Code
Write Code as follows:
(a). MainActivity.kt
Create a file named MainActivity.kt
Here is the full code
package com.example.motionlayout_pager_example
import android.annotation.SuppressLint
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import android.view.Menu
import android.view.MenuItem
import androidx.constraintlayout.motion.widget.MotionLayout
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
private var currentPosition = 0
private val itemList = listOf(
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
setupMotion()
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_settings -> true
else -> super.onOptionsItemSelected(item)
}
}
private fun setupMotion() {
motionLayout.setTransitionListener(object : MotionLayout.TransitionListener {
override fun onTransitionTrigger(motionLayout: MotionLayout?, triggerId: Int, positive: Boolean, progress: Float) {
}
override fun onTransitionStarted(motionLayout: MotionLayout?, startedId: Int, endId: Int) {
}
override fun onTransitionChange(motionLayout: MotionLayout?, startId: Int, endId: Int, progress: Float) {
}
override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {
when (currentId) {
R.id.move_left_to_right -> {
if (currentPosition > 0) {
currentPosition--
} else {
currentPosition = itemList.lastIndex
}
motionLayout?.progress = 0F
updateView()
}
R.id.move_right_to_left -> {
if (currentPosition < itemList.lastIndex) {
currentPosition++
} else {
currentPosition = 0
}
motionLayout?.progress = 0F
updateView()
}
}
}
})
updateView()
}
@SuppressLint("SetTextI18n")
private fun updateView() {
centerTextView.text = "Item\n${itemList[currentPosition]}"
rightTextView.text = if (currentPosition == itemList.lastIndex) {
"Item\n${itemList.first()}"
} else {
"Item\n${itemList[currentPosition + 1]}"
}
leftTextView.text = if (currentPosition == 0) {
"Item\n${itemList.last()}"
} else {
"Item\n${itemList[currentPosition - 1]}"
}
}
}
Run
Simply copy the source code into your Android Project,Build and Run.