0% found this document useful (0 votes)
3 views34 pages

Android View System Section 3

The document serves as an exhaustive reference guide to Android XML Views and UI components, detailing the TextView and EditText components, their attributes, and usage. It covers advanced input handling, button configurations, image rendering techniques, and modern image loading libraries, emphasizing best practices for performance and accessibility. Key features include Spannable text architecture, Material design principles, and the importance of proper layout configurations.

Uploaded by

Mayur Jindal
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views34 pages

Android View System Section 3

The document serves as an exhaustive reference guide to Android XML Views and UI components, detailing the TextView and EditText components, their attributes, and usage. It covers advanced input handling, button configurations, image rendering techniques, and modern image loading libraries, emphasizing best practices for performance and accessibility. Key features include Spannable text architecture, Material design principles, and the importance of proper layout configurations.

Uploaded by

Mayur Jindal
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Exhaustive Reference Guide to Android

XML Views and UI Components


3.1 TextView: The Foundation of Text Rendering
The TextView component represents the most complex UI primitive within the Android
framework. It is responsible for rendering, measuring, and styling text utilizing the underlying
BoringLayout, StaticLayout, or DynamicLayout engines. A mastery of its attributes and
programmatic controls is mandatory for developing high-performance, accessible applications.

Exhaustive Attribute Reference


A comprehensive understanding of TextView XML attributes ensures layout precision and
reduces the need for custom programmatic measurement.
Attribute Architectural Purpose & Usage
android:textSize Defines text dimensions. Must be specified in
sp (scale-independent pixels) to respect the
user's OS-level accessibility scaling
preferences.
android:textColor Accepts hexadecimal values or color state lists
(ColorStateList) for dynamic coloring based on
view state (e.g., pressed, disabled).
android:fontFamily Assigns system fonts or custom fonts located in
res/font. Supports downloadable fonts via
provider authorities.
android:letterSpacing Adjusts character tracking. Measured in ems
(e.g., 0.05). Critical for Material Design
typography compliance.
android:lineSpacingExtra Adds absolute padding (in dp) between lines of
text. Does not scale with text size.
android:lineSpacingMultiplier Multiplies the default line height by a float factor
(e.g., 1.2). The preferred method for adjusting
multiline readability.
android:includeFontPadding A boolean indicating whether to include extra
vertical padding allocated by the font designer.
Set to false for exact baseline alignment.
android:firstBaselineToTopHeight Defines the exact distance from the top of the
TextView bounds to the baseline of the first line
of text, ensuring pixel-perfect spacing.
android:textAppearance Applies a pre-defined style resource containing
multiple text attributes, ensuring DRY (Don't
Repeat Yourself) XML layouts.
Spannable Text Architecture
When a single TextView requires heterogeneous styling (e.g., bolding specific words, altering
colors, embedding clickable links), the Spannable API is utilized. This avoids the severe
performance penalty of nesting multiple TextView elements within a LinearLayout.
SpannableStringBuilder is the mutable variant, ideal for constructing complex text sequentially.
val termsText = "Please accept the Terms and Conditions to continue."​
val spannableBuilder = SpannableStringBuilder(termsText)​

// Apply ForegroundColorSpan and StyleSpan​
val highlightColor = [Link](context, [Link])​
[Link](​
ForegroundColorSpan(highlightColor),​
18, 23, // "Terms"​
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE​
)​
[Link](​
StyleSpan([Link]),​
18, 23,​
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE​
)​

// Apply ClickableSpan​
val clickableSpan = object : ClickableSpan() {​
override fun onClick(widget: View) {​
// Trigger navigation to Terms screen​
[Link]([Link].action_to_terms)​
}​
override fun updateDrawState(ds: TextPaint) {​
[Link](ds)​
[Link] = false // Remove default underline​
[Link] = highlightColor​
}​
}​
[Link](​
clickableSpan,​
28, 38, // "Conditions"​
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE​
)​

[Link] = spannableBuilder​
[Link] = [Link]()​

⚠️ Common Mistake: Failing to set movementMethod = [Link]()


renders ClickableSpan and URLSpan entirely inactive, as the view will not intercept touch
events for the span bounds.
Autolink, [Link], and Marquee
The android:autoLink="web|email|phone" attribute automatically parses plaintext and converts
recognizable patterns into URLSpan instances. For HTML rendering, [Link]() provides
backward compatibility:
val htmlContent = "<b>Bold</b> and <i>Italic</i>"​
[Link] = if ([Link].SDK_INT >=
Build.VERSION_CODES.N) {​
[Link](htmlContent, Html.FROM_HTML_MODE_COMPACT)​
} else {​
@Suppress("DEPRECATION")​
[Link](htmlContent)​
}​

To implement a Marquee effect for text that exceeds its bounds, the TextView must hold focus,
and specific XML attributes must be applied:
<TextView​
android:layout_width="match_parent"​
android:layout_height="wrap_content"​
android:singleLine="true"​
android:ellipsize="marquee"​
android:marqueeRepeatLimit="marquee_forever"​
android:focusable="true"​
android:focusableInTouchMode="true"​
android:text="This is a very long scrolling text that will slide
horizontally..." />​

StaticLayout and DynamicLayout


For multiline text measurement outside of the standard layout pass (e.g., custom View drawing),
developers must utilize StaticLayout. If the text is immutable, StaticLayout is used. If the text is
editable, DynamicLayout is required. Prior to API 23, constructors were used, but modern
applications should utilize [Link]. The builder pattern is the preferred method for
constructing StaticLayout objects to access newer features like hyphenation frequency and text
direction heuristics.
val textPaint = TextPaint(Paint.ANTI_ALIAS_FLAG).apply {​
textSize = 48f​
color = [Link]​
}​

val widthLimit = 500 // available width in pixels​

val staticLayout = if ([Link].SDK_INT >= Build.VERSION_CODES.M)
{​
[Link]("Sample multiline text for custom
measurement.", 0, 45, textPaint, widthLimit)​
.setAlignment([Link].ALIGN_NORMAL)​
.setLineSpacing(0f, 1f)​
.setIncludePad(false)​
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)​
.build() // The builder must not be reused after this call
[span_5](start_span)[span_5](end_span)[span_6](start_span)[span_6](end
_span)​
} else {​
@Suppress("DEPRECATION")​
StaticLayout("Sample multiline text for custom measurement.",
textPaint, widthLimit,​
[Link].ALIGN_NORMAL, 1f, 0f, false)​
}​

val exactCalculatedHeight = [Link]​

🔥 Performance Note: StaticLayout calculation is highly CPU-intensive. When rendering


massive multiline text blocks in a RecyclerView, calculating StaticLayout instances
asynchronously and caching the results prevents main thread stalling.

Compound Drawables and Custom Fonts


Compound drawables optimize layout hierarchies by embedding images directly into the
TextView, eliminating the need for a wrapping LinearLayout and an adjacent ImageView.
<TextView​
android:layout_width="wrap_content"​
android:layout_height="wrap_content"​
android:text="Profile Information"​
android:drawableStart="@drawable/ic_user_profile"​
android:drawablePadding="12dp"​
android:drawableTint="?attr/colorPrimary"​
android:textIsSelectable="true" ​
android:fontFamily="@font/roboto_mono"/>​

Setting android:textIsSelectable="true" allows native text selection and copy functionality without
converting the view into an EditText. The fontFamily attribute accepts XML font families placed
in res/font/. For programmatic access bridging legacy APIs, [Link](context,
[Link].roboto_mono) is utilized.

3.2 EditText: Advanced Input Handling


The EditText component is the primary vector for user input. Its configuration dictates the
software keyboard (IME) presented and the sanitization of incoming data. Modern
implementation strictly dictates wrapping TextInputEditText within a Material TextInputLayout.

Complete Material Setup and Validation UIs


<[Link]​
android:id="@+id/passwordInputLayout"​
android:layout_width="match_parent"​
android:layout_height="wrap_content"​
android:hint="Secure Password"​
app:hintTextColor="?attr/colorPrimary"​
app:endIconMode="password_toggle"​
app:startIconDrawable="@drawable/ic_lock"​
app:counterEnabled="true"​
app:counterMaxLength="32"​
app:prefixText="AUTH-"​
app:errorEnabled="true">​

<[Link]​
android:id="@+id/passwordEditText"​
android:layout_width="match_parent"​
android:layout_height="wrap_content"​
android:inputType="textPassword|textNoSuggestions"​
android:imeOptions="actionDone" />​
</[Link]>​

Input Types, IME Options, and EditorInfo


The android:inputType attribute drastically alters the keyboard layout. textEmailAddress
provides an '@' symbol, numberDecimal presents a numeric pad with a period, and
textCapSentences automatically capitalizes the first letter of sentences.
The android:imeOptions attribute replaces the keyboard's standard carriage return with specific
action keys. Handling these actions requires an OnEditorActionListener:
[Link] { view, actionId,
event ->​
if (actionId == EditorInfo.IME_ACTION_DONE) {​
val input = [Link]()​
submitLogin(input)​

// Hide keyboard​
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as
InputMethodManager​
[Link]([Link], 0)​
true // Event consumed​
} else {​
false​
}​
}​

Input Filters and TextWatcher


InputFilter objects constrain what can be typed in real-time at the keystroke level, whereas a
TextWatcher responds after the text has mutated.
// Limit length and force uppercase​
val lengthFilter = [Link](32)​
val allCapsFilter = [Link]()​

// Alphanumeric Regex Filter: Reject anything not matching [a-zA-Z0-9]​
val alphanumericFilter = InputFilter { source, start, end, dest,
dstart, dend ->​
val regex = Regex("^[a-zA-Z0-9]*$")​
if ([Link]().matches(regex)) {​
null // Accept the input​
} else {​
"" // Reject the input (replace invalid char with empty
string)​
}​
}​

[Link] = arrayOf(lengthFilter,
allCapsFilter, alphanumericFilter)​

// TextWatcher for real-time error state validation​
[Link](object : TextWatcher {​
override fun beforeTextChanged(s: CharSequence?, start: Int,
count: Int, after: Int) {}​
override fun onTextChanged(s: CharSequence?, start: Int, before:
Int, count: Int) {​
if ([Link]() || [Link] < 8) {​
[Link] = "Password must be at
least 8 characters"​
} else {​
[Link] = null // Clear error​
}​
}​
override fun afterTextChanged(s: Editable?) {}​
})​

3.3 Button and MaterialButton Configurations


Modern Android development deprecates the raw Button in favor of MaterialButton from the
Material Components library. This component provides native support for icon rendering, corner
shaping, state layers, and explicit semantic styles.

Material 3 Button Styles


Style Attribute Visual Output & Emphasis
style="@style/[Link]" Filled Button: High emphasis. Used for
primary actions (Save, Submit).
style="@style/[Link] Outlined Button: Medium emphasis. Used for
Button" secondary actions (Cancel).
style="@style/[Link] Text Button: Low emphasis. Used for optional
on" actions (Learn More).
style="@style/[Link] Elevated Button: Filled, with shadow depth.
Button"
style="@style/[Link] Tonal Button: Uses secondary container
ton" colors.
MaterialButtonToggleGroup
MaterialButtonToggleGroup organizes a cluster of mutually exclusive or multi-select buttons,
creating a segmented control system.
<[Link]​
android:id="@+id/themeToggleGroup"​
android:layout_width="wrap_content"​
android:layout_height="wrap_content"​
app:singleSelection="true"​
app:selectionRequired="true"​
app:checkedButton="@+id/btnSystem">​

<[Link]​
style="@style/[Link]"​
android:id="@+id/btnLight"​
android:layout_width="wrap_content"​
android:layout_height="wrap_content"​
android:text="Light"​
app:icon="@drawable/ic_sun" />​

<[Link]​
style="@style/[Link]"​
android:id="@+id/btnDark"​
android:layout_width="wrap_content"​
android:layout_height="wrap_content"​
android:text="Dark"​
app:icon="@drawable/ic_moon" />​

<[Link]​
style="@style/[Link]"​
android:id="@+id/btnSystem"​
android:layout_width="wrap_content"​
android:layout_height="wrap_content"​
android:text="System" />​

</[Link]>​
// Listener implementation for Toggle Group
[span_10](start_span)[span_10](end_span)​
[Link] { group,
checkedId, isChecked ->​
if (isChecked) {​
when (checkedId) {​
[Link] ->
[Link](AppCompatDelegate.MODE_NIGHT_NO)​
[Link] ->
[Link](AppCompatDelegate.MODE_NIGHT_YES
)​
[Link] ->
[Link](AppCompatDelegate.MODE_NIGHT_FOL
LOW_SYSTEM)​
}​
}​
}​

💡 Pro Tip: To create an entirely circular icon button without text, use
[Link] or leverage ShapeableImageView with
android:background="?attr/selectableItemBackgroundBorderless" to implement ripple effects.

3.4 ImageView and Asset Rendering


The ImageView requires explicit configuration of its scaleType to prevent aspect ratio distortion
when rendering bitmaps that do not match the view's layout bounds.

ScaleType Matrix
ScaleType Transformation Logic
fitCenter Scales uniformly to fit entirely within bounds.
Image is centered. Leaves blank space if
aspect ratios mismatch.
centerCrop Scales uniformly to fill bounds entirely. Crops
excess edges. The standard for profile pictures
and hero images.
centerInside Centers the image. Scales down if it exceeds
bounds, but never scales up.
fitXY Independent X and Y scaling. Distorts aspect
ratio. Generally avoided.
fitStart / fitEnd Same as fitCenter, but aligns the matrix to the
start or end of the view bounds.
matrix Skips auto-scaling. Defers rendering entirely to
an injected ImageMatrix.
android:adjustViewBounds="true" forces the ImageView to resize its bounding box to match the
exact aspect ratio of the loaded drawable, which is critical when wrapping height based on a
downloaded image's dimensions. app:srcCompat must be used instead of android:src to ensure
backward compatibility for vector drawables.

ShapeableImageView
Material provides ShapeableImageView to apply corners and strokes directly to bitmaps without
complex custom view clipping logic.
<[Link]​
android:id="@+id/avatarImage"​
android:layout_width="120dp"​
android:layout_height="120dp"​
android:scaleType="centerCrop"​
app:srcCompat="@drawable/placeholder_avatar"​
app:shapeAppearanceOverlay="@style/CircleImageStyle"​
app:strokeWidth="2dp"​
app:strokeColor="?attr/colorPrimary" />​

<style name="CircleImageStyle">​
<item name="cornerFamily">rounded</item>​
<item name="cornerSize">50%</item>​
</style>​

Image Loading Libraries: Coil vs Glide


Modern architectures transition from Picasso/Glide to Coil, a Kotlin-first library heavily utilizing
Coroutines.
Coil Implementation (Modern Kotlin):
val imageLoader = [Link](context)​
.crossfade(true)​
.build()​

val request = [Link](context)​
.data("[Link]
.target([Link])​
.placeholder([Link].ic_loading)​
.error([Link].ic_error_state)​
.transformations(CircleCropTransformation())​
.build()​

[Link](request)​

Glide Implementation (Java/Legacy standard):


[Link](this)​
.load("[Link]
.placeholder([Link].ic_loading)​
.error([Link].ic_error_state)​
.circleCrop()​
.into([Link])​

3.5 RecyclerView: The Ultimate Data Presentation


Engine
The RecyclerView is the architectural backbone of Android data presentation. It is EXTREMELY
complex, utilizing a sophisticated recycling mechanism to render massive datasets with minimal
memory footprints.

Architecture: ViewHolder, Scrap Heap, and Recycled Pool


RecyclerView prevents OutOfMemory (OOM) exceptions and UI thread stuttering by reusing
View hierarchies.
1.​ Recycled Pool: When an item scrolls off-screen entirely, its View is detached and placed
in the Recycled Pool. When a new item scrolls on-screen, the RecyclerView pulls a view
from the pool and invokes onBindViewHolder to map new data to the old view.
2.​ Scrap Heap: During layout passes (e.g., when the dataset changes size but views remain
on-screen), views are temporarily detached and placed in the Scrap Heap. They are
retrieved instantly without rebinding.

Full Implementation: Multi-View Type Chat Application


To handle complex layouts (e.g., a chat app with incoming and outgoing messages), multiple
ViewHolders are strictly typed. Furthermore, ListAdapter (which implements AsyncListDiffer
under the hood) automatically calculates difference states on a background thread via DiffUtil,
preventing UI freezes.
// Data Model​
data class ChatMessage(val id: String, val body: String, val
timestamp: Long, val isSelf: Boolean)​

// The Adapter​
class ChatAdapter : ListAdapter<ChatMessage,
[Link]>(ChatDiffCallback()) {​

companion object {​
private const val VIEW_TYPE_INCOMING = 0​
private const val VIEW_TYPE_OUTGOING = 1​
}​

// DiffUtil implementation for async diffing​
class ChatDiffCallback : [Link]<ChatMessage>() {​
override fun areItemsTheSame(oldItem: ChatMessage, newItem:
ChatMessage): Boolean {​
return [Link] == [Link] // Check primary key​
}​
override fun areContentsTheSame(oldItem: ChatMessage, newItem:
ChatMessage): Boolean {​
return oldItem == newItem // Data class structural
equality checks all fields​
}​
}​

override fun getItemViewType(position: Int): Int {​
return if (getItem(position).isSelf) VIEW_TYPE_OUTGOING else
VIEW_TYPE_INCOMING​
}​

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
[Link] {​
val inflater = [Link]([Link])​
return if (viewType == VIEW_TYPE_OUTGOING) {​
val binding = [Link](inflater,
parent, false)​
OutgoingViewHolder(binding)​
} else {​
val binding = [Link](inflater,
parent, false)​
IncomingViewHolder(binding)​
}​
}​

override fun onBindViewHolder(holder: [Link],
position: Int) {​
val message = getItem(position)​
when (holder) {​
is OutgoingViewHolder -> [Link](message)​
is IncomingViewHolder -> [Link](message)​
}​
}​

inner class OutgoingViewHolder(private val binding:
ItemChatOutgoingBinding) : ​
[Link]([Link]) {​
fun bind(message: ChatMessage) {​
[Link] = [Link]​
[Link] =
[Link]([Link], [Link],
DateUtils.FORMAT_SHOW_TIME)​
}​
}​

inner class IncomingViewHolder(private val binding:
ItemChatIncomingBinding) : ​
[Link]([Link]) {​
fun bind(message: ChatMessage) {​
[Link] = [Link]​
[Link] =
[Link]([Link], [Link],
DateUtils.FORMAT_SHOW_TIME)​
}​
}​
}​

If an architecture dictates avoiding ListAdapter subclassing, AsyncListDiffer is instantiated


manually within a standard [Link] to achieve identical asynchronous
background diffing.

LayoutManagers in Depth
The LayoutManager dictates the spatial arrangement of items.
●​ LinearLayoutManager: 1D lists. Using stackFromEnd = true and reverseLayout = true
creates the standard "bottom-up" chat behavior.
●​ GridLayoutManager: 2D grids. spanSizeLookup allows dynamic spans.
●​ StaggeredGridLayoutManager: Masonry grids where items possess heterogeneous
heights (e.g., Pinterest).
val gridManager = GridLayoutManager(context, 3) // 3 columns​
[Link] = object :
[Link]() {​
override fun getSpanSize(position: Int): Int {​
val viewType = [Link](position)​
// If it's a section header, span all 3 columns. Otherwise, 1
column.​
return if (viewType == VIEW_TYPE_HEADER) 3 else 1​
}​
}​
[Link] = gridManager​

🔥 Performance Note: Calling [Link](true) is a critical


optimization. It signals that adding/removing items will not alter the outer dimensions of the
RecyclerView itself, allowing the system to skip entire layout passes.
[Link] = 6 caches off-screen views for nested horizontal lists,
eliminating stutter.

ConcatAdapter: Headers and Footers


Before ConcatAdapter, merging headers, list items, and loading footers required complex index
math within a single adapter. ConcatAdapter chains adapters sequentially.
val headerAdapter = HeaderAdapter(HeaderData("Welcome"))​
val mainChatAdapter = ChatAdapter()​
val footerLoadingAdapter = LoadingAdapter()​

val config =
[Link]().setIsolateViewTypes(false).build()​
val concatAdapter = ConcatAdapter(config, headerAdapter,
mainChatAdapter, footerLoadingAdapter)​

[Link] = concatAdapter​

ItemDecoration and ItemTouchHelper


ItemDecoration executes canvas drawing commands below or above the views.
class CustomDividerDecoration(context: Context) :
[Link]() {​
private val paint = Paint().apply { color = [Link];
strokeWidth = 2f }​

override fun onDraw(c: Canvas, parent: RecyclerView, state:
[Link]) {​
val left = [Link]()​
val right = ([Link] - [Link]).toFloat()​
for (i in 0 until [Link] - 1) { // Skip last item​
val child = [Link](i)​
val params = [Link] as
[Link]​
val top = ([Link] + [Link]).toFloat()​
[Link](left, top, right, top, paint)​
}​
}​
}​
[Link](CustomDividerDecoration(context
))​

ItemTouchHelper implements swipe-to-dismiss and drag-to-reorder logic directly into the


RecyclerView system.
val swipeHandler = object : [Link](​
0, // No drag directions​
[Link] or [Link] // Swipe directions​
) {​
override fun onMove(​
recyclerView: RecyclerView, viewHolder:
[Link], target: [Link]​
): Boolean = false // Drag not supported​

override fun onSwiped(viewHolder: [Link],
direction: Int) {​
val position = [Link]​
// 1. Remove from backing list​
// 2. [Link](newList)​
}​

override fun onChildDraw(​
c: Canvas, recyclerView: RecyclerView, viewHolder:
[Link],​
dX: Float, dY: Float, actionState: Int, isCurrentlyActive:
Boolean​
) {​
// Pixel-perfect background rendering during swipe​
val itemView = [Link]​
val bgPaint = Paint().apply { color = [Link] }​
[Link](​
[Link]() + dX, [Link](),​
[Link](), [Link](),
bgPaint​
)​
[Link](c, recyclerView, viewHolder, dX, dY,
actionState, isCurrentlyActive)​
}​
}​
ItemTouchHelper(swipeHandler).attachToRecyclerView([Link]
w)​

RecycledViewPool Sharing
For vertically scrolling lists containing horizontal scrolling lists (e.g., Netflix), each horizontal list
generates its own recycled pool. Sharing a single pool reduces memory allocations dramatically.
val sharedPool = [Link]()​
[Link](VIEW_TYPE_MOVIE_POSTER, 20)​

// Inside the parent adapter's onBindViewHolder:​
[Link](sharedPool)​

3.6 ScrollView and NestedScrollView


ScrollView permits vertical scrolling for layouts exceeding viewport dimensions. However, it
cannot resolve touch conflicts when multiple scrollable surfaces overlap.
NestedScrollView implements the NestedScrolling child/parent protocols, allowing smooth event
delegation.
<[Link]​
android:layout_width="match_parent"​
android:layout_height="match_parent"​
android:fillViewport="true">​
<LinearLayout android:orientation="vertical"...>​
</LinearLayout>​
</[Link]>​
⚠️ Common Mistake: Placing a RecyclerView inside a NestedScrollView entirely disables the
RecyclerView's recycling architecture. The system forces the RecyclerView to wrap its content
height, inflating every single item simultaneously and causing critical OOM exceptions. If this is
unavoidable, [Link] = false must be set, but migrating
to a ConcatAdapter architecture is the universally preferred solution.
To perform smooth programmatic scrolling:
[Link] {​
[Link](0,
[Link])​
}​

3.7 ViewPager2 and TabLayout Integration


ViewPager2 deprecates the legacy ViewPager. Architecturally, it is a RecyclerView turned
horizontally (or vertically), supporting [Link] natively.

FragmentStateAdapter
When paging massive independent UI sections, FragmentStateAdapter dictates how Fragment
lifecycles map to page state.
class MainPagerAdapter(activity: FragmentActivity) :
FragmentStateAdapter(activity) {​
override fun getItemCount(): Int = 3​

override fun createFragment(position: Int): Fragment {​
return when (position) {​
0 -> FeedFragment()​
1 -> ExploreFragment()​
else -> ProfileFragment()​
}​
}​
}​

[Link] = MainPagerAdapter(this)​
// Render vertical pages:​
[Link] = ViewPager2.ORIENTATION_VERTICAL​
// Control offscreen loading retention:​
[Link] = 2 ​
// Disable user swiping programmatically:​
[Link] = false ​

TabLayoutMediator Integration
TabLayout renders the navigation tabs, mapping 1:1 with the ViewPager.
<[Link]​
android:id="@+id/tabLayout"​
android:layout_width="match_parent"​
android:layout_height="wrap_content"​
app:tabMode="fixed"​
app:tabIndicatorColor="?attr/colorPrimary"​
app:tabSelectedTextColor="?attr/colorPrimary" />​

TabLayoutMediator([Link], [Link]) { tab,


position ->​
[Link] = when (position) {​
0 -> "Feed"​
1 -> "Explore"​
else -> "Profile"​
}​
// Custom views can be assigned here via [Link] =...​
}.attach()​

Custom PageTransformer
PageTransformer executes mathematical transformations on layout views dynamically during
swipe transitions.
class ZoomOutPageTransformer : [Link] {​
private val MIN_SCALE = 0.85f​
private val MIN_ALPHA = 0.5f​

override fun transformPage(view: View, position: Float) {​
[Link] {​
val pageWidth = width​
val pageHeight = height​
when {​
position < -1 -> { alpha = 0f } // Way off-screen to
left​
position <= 1 -> { // [-1, 1] range is visible​
val scaleFactor = [Link](MIN_SCALE, 1 -
[Link](position))​
val vertMargin = pageHeight * (1 - scaleFactor) /
2​
val horzMargin = pageWidth * (1 - scaleFactor) / 2​
translationX = if (position < 0) horzMargin -
vertMargin / 2 else -horzMargin + vertMargin / 2​

scaleX = scaleFactor​
scaleY = scaleFactor​
alpha = MIN_ALPHA + (scaleFactor - MIN_SCALE) / (1
- MIN_SCALE) * (1 - MIN_ALPHA)​
}​
else -> { alpha = 0f } // Way off-screen to right​
}​
}​
}​
}​
[Link](ZoomOutPageTransformer())​

3.8 MaterialCardView
MaterialCardView extends FrameLayout to provide hardware-accelerated clipping, corner radii,
elevation shadows, and stroke borders.
<[Link]​
android:layout_width="match_parent"​
android:layout_height="wrap_content"​
android:layout_margin="8dp"​
android:clickable="true"​
android:focusable="true"​
android:checkable="true"​
app:cardElevation="6dp"​
app:cardCornerRadius="16dp"​
app:cardBackgroundColor="?attr/colorSurface"​
app:contentPadding="16dp"​
app:strokeColor="@color/outline_selector"​
app:strokeWidth="1dp"​
app:rippleColor="?attr/colorControlHighlight">​

<TextView​
android:layout_width="wrap_content"​
android:layout_height="wrap_content"​
android:text="Selectable Card Content" />​

</[Link]>​

When android:checkable="true" is enabled, toggling [Link] = true triggers a


native overlay animation, presenting a check icon (configurable via app:checkedIcon) without
custom layout logic.

3.9 MaterialToolbar and CollapsingToolbarLayout


MaterialToolbar replaces the legacy ActionBar, offering granular programmatic control over
app-bar layouts. Integrating it with CollapsingToolbarLayout produces the standard "hero-image
shrink" pattern.
<[Link]​
android:layout_width="match_parent"​
android:layout_height="match_parent">​

<[Link]​
android:layout_width="match_parent"​
android:layout_height="250dp">​

<[Link]​
android:layout_width="match_parent"​
android:layout_height="match_parent"​
app:contentScrim="?attr/colorPrimary"​
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">​

<ImageView​
android:layout_width="match_parent"​
android:layout_height="match_parent"​
android:scaleType="centerCrop"​
android:src="@drawable/hero_image"​
app:layout_collapseMode="parallax"​
app:layout_collapseParallaxMultiplier="0.5" />​

<[Link]​
android:id="@+id/toolbar"​
android:layout_width="match_parent"​
android:layout_height="?attr/actionBarSize"​
app:layout_collapseMode="pin"​
app:navigationIcon="@drawable/ic_back" />​

</[Link]>​
</[Link]>​

<[Link]​
android:layout_width="match_parent"​
android:layout_height="match_parent"​
app:layout_behavior="@string/appbar_scrolling_view_behavior">​
</[Link]>​

</[Link]>​

// Binding in Activity​
setSupportActionBar([Link])​
supportActionBar?.setDisplayShowTitleEnabled(false)​

// Handle click listeners​
[Link] { ​
[Link]() ​
}​

[Link] { menuItem ->​
when ([Link]) {​
[Link].action_settings -> { /* Handle settings */ true }​
else -> false​
}​
}​

3.10 BottomNavigationView
BottomNavigationView drives primary hierarchical navigation across 3–5 top-level destinations.
<[Link]​
android:id="@+id/bottomNavigation"​
android:layout_width="match_parent"​
android:layout_height="wrap_content"​
app:menu="@menu/bottom_nav_menu"​
app:labelVisibilityMode="selected"​

app:itemActiveIndicatorStyle="@style/[Link]
[Link]" />​

Listener Setup and Badge Integration:


[Link] { item ->​
when ([Link]) {​
[Link].nav_home -> { navigateToHome(); true }​
[Link].nav_messages -> { navigateToMessages(); true }​
else -> false​
}​
}​

// Badge provisioning​
val badge =
[Link]([Link].nav_messages)​
[Link] = true​
[Link] = 14​
[Link] = [Link](this,
[Link].red_badge)​

// Dismiss badge dynamically​
// [Link]([Link].nav_messages)​

3.11 TabLayout (Advanced Configuration)


Beyond integrating with ViewPager, TabLayout acts as an independent selection filter. Setting
app:tabMode="scrollable" permits infinite horizontal tabs. app:tabMode="fixed" forcibly crushes
tabs into the screen width.
[Link](object :
[Link] {​
override fun onTabSelected(tab: [Link]?) {​
val customView = tab?.customView​
// Perform complex animations on custom tab view selections​
}​
override fun onTabUnselected(tab: [Link]?) {}​
override fun onTabReselected(tab: [Link]?) {}​
})​

3.12 NavigationView (Drawer)


The NavigationView represents the standard slide-out drawer menu, strictly anchored within a
DrawerLayout.
<[Link]​
android:id="@+id/drawerLayout"​
android:layout_width="match_parent"​
android:layout_height="match_parent">​

<FrameLayout... />​

<[Link]​
android:id="@+id/navigationView"​
android:layout_width="wrap_content"​
android:layout_height="match_parent"​
android:layout_gravity="start"​
app:headerLayout="@layout/nav_header_main"​
app:menu="@menu/activity_main_drawer" />​

</[Link]>​

In activity_main_drawer.xml, wrapping items in <group android:checkableBehavior="single">


automatically manages highlighted visual states during navigation.

3.13 FloatingActionButton & ExtendedFAB


The FloatingActionButton (FAB) specifies the primary screen action.
ExtendedFloatingActionButton (EFAB) permits descriptive text. Material Guidelines mandate
EFAB shrinking during downward scrolling to reclaim viewport space.
<[Link]
onButton​
android:id="@+id/extendedFab"​
android:layout_width="wrap_content"​
android:layout_height="wrap_content"​
android:layout_gravity="bottom|end"​
android:layout_margin="16dp"​
android:text="Compose Message"​
app:icon="@drawable/ic_edit"​
app:backgroundTint="?attr/colorPrimaryContainer" />​
// Shrink on scroll down, extend on scroll up​
[Link](object :
[Link]() {​
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy:
Int) {​
if (dy > 0 && [Link]) {​
[Link]()​
} else if (dy < 0 &&![Link]) {​
[Link]()​
}​
}​
})​

3.14 BottomAppBar
The BottomAppBar cradles the FAB, inverting standard app bar placement to optimize
reachability on tall displays.
<[Link]>​
<[Link]... />​

<[Link]​
android:id="@+id/bottomAppBar"​
android:layout_width="match_parent"​
android:layout_height="wrap_content"​
android:layout_gravity="bottom"​
app:fabAlignmentMode="center"​
app:fabCradleMargin="8dp"​
app:fabCradleRoundedCornerRadius="16dp"​
app:navigationIcon="@drawable/ic_menu" />​

<[Link]​
android:id="@+id/cradledFab"​
android:layout_width="wrap_content"​
android:layout_height="wrap_content"​
app:srcCompat="@drawable/ic_add"​
app:layout_anchor="@id/bottomAppBar" />​
</[Link]>​

3.15 Chip and ChipGroup


Chips compress complex entities, attributes, and actions into interactive pills.
Chip Style Architectural Purpose
[Link] Executes an immediate action.
Chip Style Architectural Purpose
[Link] Toggles selection states, presenting a
checkmark when active.
[Link] Represents user input (e.g., an email address).
Includes a close icon.
[Link] Offers dynamic recommendations.
<[Link]​
android:id="@+id/filterChipGroup"​
android:layout_width="match_parent"​
android:layout_height="wrap_content"​
app:singleSelection="true"​
app:selectionRequired="true">​

<[Link]​
style="@style/[Link]"​
android:id="@+id/chipOnline"​
android:layout_width="wrap_content"​
android:layout_height="wrap_content"​
android:text="Online Only" />​

</[Link]>​

To dynamically inject Entry chips from an EditText source:


val inputChip = Chip(context).apply {​
text = userTypedEmail​
isCloseIconVisible = true​
setChipDrawable([Link](context, null,
0, [Link].Widget_Material3_Chip_Entry))​
setOnCloseIconClickListener { ​
[Link](this) ​
}​
}​
[Link](inputChip)​

3.16 Slider and RangeSlider


Material 3 deprecates the legacy SeekBar in favor of Slider and RangeSlider. RangeSlider
natively supports discrete dual thumb selection bounding.
<[Link]​
android:id="@+id/priceRangeSlider"​
android:layout_width="match_parent"​
android:layout_height="wrap_content"​
android:valueFrom="0.0"​
android:valueTo="1000.0"​
app:values="@array/initial_slider_values"​
app:stepSize="50.0"​
app:tickVisible="true" />​

Applying a LabelFormatter ensures floating-point values are parsed into contextual formats
(e.g., currency) without manual string interpolation.
[Link] { value ->​
val format = [Link]()​
[Link] = 0​
[Link] = [Link]("USD")​
[Link]([Link]())​
}​

[Link] { slider, value, fromUser
->​
val values = [Link]​
Log.d("Slider", "Min: ${values}, Max: ${values}")​
}​

3.17 SwitchMaterial, CheckBox, RadioButton


Boolean configuration toggles. The RadioGroup enforces exclusive single-selection behavior
across nested RadioButton children.
<RadioGroup​
android:id="@+id/paymentRadioGroup"​
android:layout_width="match_parent"​
android:layout_height="wrap_content"​
android:orientation="vertical">​

<[Link]​
android:id="@+id/radioCredit"​
android:layout_width="match_parent"​
android:layout_height="wrap_content"​
android:text="Credit Card" />​

<[Link]​
android:id="@+id/radioPaypal"​
android:layout_width="match_parent"​
android:layout_height="wrap_content"​
android:text="PayPal" />​
</RadioGroup>​

[Link] { group,
checkedId ->​
when (checkedId) {​
[Link] -> showCreditForm()​
[Link] -> showPaypalAuth()​
}​
}​

3.18 Progress Indicators


Material 3 delineates progress visualizations into LinearProgressIndicator and
CircularProgressIndicator. Indicators dictate a determinate state (known percentage) or
indeterminate state (continuous animation denoting blocking operations).
<[Link]
or​
android:layout_width="wrap_content"​
android:layout_height="wrap_content"​
android:indeterminate="true"​
app:indicatorColor="@color/primary"​
app:trackColor="@color/surface_variant" />​

<[Link]​
android:id="@+id/downloadProgress"​
android:layout_width="match_parent"​
android:layout_height="wrap_content"​
android:progress="45"​
android:max="100" />​

To update determinately with animation:


[Link](85, true)​

3.19 SearchBar and SearchView (Material 3)


Material 3 drastically re-architects search behaviors. The SearchBar is a passive trigger button.
The SearchView is an active overlay surface containing suggestions. Integration mandates a
CoordinatorLayout.
<[Link]>​
<[Link]>​
<[Link]​
android:id="@+id/search_bar"​
android:layout_width="match_parent"​
android:layout_height="wrap_content"​
android:hint="Search products..." />​
</[Link]>​

<[Link]​
android:id="@+id/search_view"​
android:layout_width="match_parent"​
android:layout_height="match_parent"​
android:hint="Search products..."​
app:layout_anchor="@id/search_bar">​

<[Link]​
android:id="@+id/searchSuggestionsRecycler"​
android:layout_width="match_parent"​
android:layout_height="match_parent" />​

</[Link]>​
</[Link]>​

// Bridge the View to the Bar


[span_19](start_span)[span_19](end_span)[span_20](start_span)[span_20]
(end_span)​
[Link]([Link])​

[Link] { v, actionId,
event ->​
if (actionId == EditorInfo.IME_ACTION_SEARCH) {​
val query = [Link]()​
performSearch(query)​
[Link]()​
true​
} else false​
}​

3.20 BottomSheet Dialog and Architecture


Bottom sheets operate structurally as Persistent logic (anchored inside a CoordinatorLayout) or
Modal logic (floating Dialog fragments).

Behavior States and Implementation


The BottomSheetBehavior enforces strict interaction states :
●​ STATE_EXPANDED: View occupies maximum height.
●​ STATE_COLLAPSED: View rests at defined peekHeight.
●​ STATE_DRAGGING: User interaction in progress.
●​ STATE_SETTLING: Physics-based snap animation playing.
●​ STATE_HIDDEN: View dismissed completely.
●​ STATE_HALF_EXPANDED: Intermediary state if fitToContents is disabled.
<[Link]>​
<LinearLayout​
android:id="@+id/persistentBottomSheet"​
android:layout_width="match_parent"​
android:layout_height="wrap_content"​
android:background="?attr/colorSurface"​
android:orientation="vertical"​
app:layout_behavior="[Link]
etBehavior"​
app:behavior_peekHeight="120dp"​
app:behavior_hideable="true"​
app:behavior_skipCollapsed="false">​
</LinearLayout>​
</[Link]>​

val sheetBehavior =
[Link]([Link])​

// Programmatic control [span_26](start_span)[span_26](end_span)​
[Link] = BottomSheetBehavior.STATE_EXPANDED​

[Link](object :
[Link]() {​
override fun onStateChanged(bottomSheet: View, newState: Int) {​
when (newState) {​
BottomSheetBehavior.STATE_HIDDEN -> { /* Destroy resources
*/ }​
}​
}​

override fun onSlide(bottomSheet: View, slideOffset: Float) {​
// slideOffset metrics:​
// -1.0 (Hidden) -> 0.0 (Collapsed) -> 1.0 (Expanded)​
// Highly critical for fading background scrim alphas
[span_27](start_span)[span_27](end_span)​
[Link] = maxOf(0f, slideOffset)​
}​
})​

3.21 Snackbar and Toast Protocols


Toast renders system-level floating notifications, restricted primarily to background syncs or
errors. Contextual UI actions demand Snackbar.
When dealing with a FAB or Bottom Navigation, setAnchorView MUST be invoked. Otherwise,
the Snackbar visually occludes the primary buttons.
[Link]([Link], "Message archived.",
Snackbar.LENGTH_LONG)​
.setAnchorView([Link])​
.setAction("UNDO") {​
// Reverse archiving logic​
}​
.setActionTextColor([Link](context,
[Link].snackbar_action))​
.show()​

3.22 MaterialAlertDialogBuilder
The Builder pattern dictates alert dialog generation. Complex inputs require inflating entirely
custom XML layouts into the builder's setView layer.
val dialogBinding = [Link](layoutInflater)​

val dialog = MaterialAlertDialogBuilder(this)​
.setTitle("Rename Project")​
.setMessage("Enter a new identifier for this architecture
workspace.")​
.setView([Link])​
.setPositiveButton("Save") { dialogInterface, _ ->​
val input = [Link]()​
saveProjectName(input)​
}​
.setNegativeButton("Cancel", null)​
.create()​

[Link]()​

3.23 PopupMenu and ListPopupWindow


To display a contextual dropdown list anchored directly beneath a specific View,
ListPopupWindow is superior to PopupMenu as it allows adapter injection.
val listPopupWindow = ListPopupWindow(context, null,
[Link])​
[Link] = [Link] // Anchor target​

val adapter = ArrayAdapter(context,
[Link].simple_list_item_1, listOf("Sort by Date", "Sort by
Name", "Sort by Size"))​
[Link](adapter)​

[Link] { parent, view, position, id ->​
val selectedItem = [Link](position)​
applySorting(selectedItem)​
[Link]()​
}​

// Show dropdown​
[Link] { [Link]() }​
3.24 WebView and Modern Back Navigation
The WebView integrates browser instances natively. Its lifecycle dictates that onBackPressed
closes the host Activity, bypassing internal web history entirely. To intercept back gestures
without utilizing deprecated APIs, OnBackPressedDispatcher integration is mandatory.
[Link] {​
[Link] = true // Enable with high security
caution​
[Link] = true​
// Force link navigation to remain inside WebView rather than
launching Chrome [span_32](start_span)[span_32](end_span)​
webViewClient = WebViewClient() ​
webChromeClient = WebChromeClient() // Permits JS alert boxes​
loadUrl("[Link]
}​

// Modern Android Back Stack Navigation
[span_33](start_span)[span_33](end_span)[span_34](start_span)[span_34]
(end_span)​
val backCallback = object : OnBackPressedCallback(true) {​
override fun handleOnBackPressed() {​
if ([Link]()) {​
[Link]() // Navigate web history stack
[span_35](start_span)[span_35](end_span)​
} else {​
isEnabled = false // Disable interception​
[Link]() // Execute native
Activity back action​
}​
}​
}​
[Link](this, backCallback)
[span_36](start_span)[span_36](end_span)​

To execute native Kotlin via JavaScript callbacks triggered in the HTML:


class WebAppInterface(private val mContext: Context) {​
@JavascriptInterface​
fun transmitToken(token: String) {​
// Invoked via JavaScript: [Link]("123")​
}​
}​
[Link](WebAppInterface(this),
"AndroidBridge")​

3.25 MapView / SupportMapFragment Integration


While SupportMapFragment handles lifecycle events autonomously, placing maps inside
complex XML hierarchies (like a scrolling detail page) dictates the use of MapView. MapView
explicitly requires manual lifecycle forwarding to prevent memory leaks and blank renderings.
<[Link]​
android:id="@+id/locationMapView"​
android:layout_width="match_parent"​
android:layout_height="250dp"​
app:liteMode="true" /> ```​

```kotlin​
override fun onCreate(savedInstanceState: Bundle?) {​
[Link](savedInstanceState)​
[Link](savedInstanceState)​
[Link] { googleMap ->​
val markerLocation = LatLng(37.7749, -122.4194)​

[Link](MarkerOptions().position(markerLocation).title("Sa
n Francisco"))​

[Link]([Link](markerLocation,
12f))​
}​
}​

// Lifecycle forwarding is mandatory​
override fun onStart() { [Link]();
[Link]() }​
override fun onResume() { [Link]();
[Link]() }​
override fun onPause() { [Link]();
[Link]() }​
override fun onStop() { [Link]();
[Link]() }​
override fun onDestroy() { [Link]();
[Link]() }​
override fun onLowMemory() { [Link]();
[Link]() }​

3.26 Media3 PlayerView (ExoPlayer)


The legacy VideoView cannot handle DASH, HLS, or dynamic DRM streaming. Modern
architecture dictates the use of AndroidX Media3 PlayerView (the direct integration successor to
standalone ExoPlayer).

PlayerView Layout Configuration


<[Link]​
android:id="@+id/mediaPlayerView"​
android:layout_width="match_parent"​
android:layout_height="wrap_content"​
android:keepScreenOn="true"​
app:show_buffering="when_playing"​
app:use_controller="true"​
app:resize_mode="fit" /> ```​

### Media3 Kotlin Initialization​

```kotlin​
private var player: ExoPlayer? = null​

private fun initializePlayer() {​
player = [Link](this).build()​
[Link] = player
[span_39](start_span)[span_39](end_span)​

val mediaItem = [Link]()​

.setUri("[Link]
zy.mp4")​
.setMediaId("sample_video_1")​
.build()​

player?.setMediaItem(mediaItem)​
player?.prepare()​
player?.playWhenReady = true​
}​

override fun onStop() {​
[Link]()​
// Player release is mandatory to unblock hardware decoders​
player?.release()​
player = null​
}​

Advanced UI Customization
The default controller interface can be fully overridden by supplying a custom layout
exo_player_control_view.xml. Alternatively, default drawables can be hijacked natively without
layout manipulation by overriding specific system drawable names.
Creating a file in res/values/[Link]:
<resources>​
<drawable
name="exo_styled_controls_play">@drawable/ic_custom_play</drawable>​
<drawable
name="exo_styled_controls_pause">@drawable/ic_custom_pause</drawable>​
</resources>​

3.27 CalendarView, DatePicker, and TimePicker


Do not utilize raw XML <DatePicker> nodes directly in the view hierarchy, as scaling and
aesthetic continuity fail across OEM skins. Material Pickers guarantee cohesive cross-device
rendering.
val datePicker = [Link]()​
.setTitleText("Select Deployment Date")​
.setSelection([Link]())​
.build()​

[Link] { selectionMilliseconds ->​
val format = SimpleDateFormat("MMM dd, yyyy", [Link]())​
[Link] =
[Link](Date(selectionMilliseconds))​
}​

[Link] {​
[Link](supportFragmentManager, "MATERIAL_DATE_PICKER")​
}​

3.28 RatingBar and SeekBar


The RatingBar renders user-generated appraisal scores natively. It extends ProgressBar.
<RatingBar​
android:id="@+id/courseRatingBar"​
android:layout_width="wrap_content"​
android:layout_height="wrap_content"​
android:numStars="5"​
android:stepSize="0.5"​
android:rating="4.5"​
android:isIndicator="false"​
android:progressTint="@color/star_gold" />​

android:isIndicator="true" prevents user interaction, making it strictly a display view.


Note: For single continuous values historically handled by SeekBar, modern architectures
enforce the use of Slider (See Section 3.16).

3.29 NumberPicker
A specialized scrolling spinner enforcing strictly bounded integer inputs (e.g., age, quantities).
<NumberPicker​
android:id="@+id/quantityPicker"​
android:layout_width="wrap_content"​
android:layout_height="wrap_content" />​

[Link] {​
minValue = 1​
maxValue = 100​
wrapSelectorWheel = true // Allows scrolling from 100 directly
back to 1​
setOnValueChangedListener { picker, oldVal, newVal ->​
Log.d("Picker", "Quantity updated from $oldVal to $newVal")​
}​
}​

3.30 TextSwitcher, ImageSwitcher, ViewFlipper,


ViewAnimator
The ViewAnimator family handles logical view swaps with integrated entry/exit animations.
●​ ViewSwitcher: Hard-coded to exactly two child views.
●​ ViewFlipper: Swaps between an arbitrary N children automatically via a timer interval.
●​ TextSwitcher / ImageSwitcher: Require programmatic factories to instantiate internal
views automatically upon receiving new payloads.

ViewFlipper Autonomous Carousel Setup


<ViewFlipper​
android:id="@+id/bannerViewFlipper"​
android:layout_width="match_parent"​
android:layout_height="200dp"​
android:measureAllChildren="false"> ​
<ImageView android:src="@drawable/promo_banner_1"
android:scaleType="centerCrop"/>​
<ImageView android:src="@drawable/promo_banner_2"
android:scaleType="centerCrop"/>​
<ImageView android:src="@drawable/promo_banner_3"
android:scaleType="centerCrop"/>​
</ViewFlipper>​

// Load animations [span_46](start_span)[span_46](end_span)​


val slideIn = [Link](this,
[Link].slide_in_left)​
val slideOut = [Link](this,
[Link].slide_out_right)​

[Link] {​
inAnimation = slideIn​
outAnimation = slideOut
[span_48](start_span)[span_48](end_span)[span_49](start_span)[span_49]
(end_span)​
flipInterval = 4000 // Transition every 4 seconds​
startFlipping() ​
}​

### TextSwitcher Programmatic Factory


A TextSwitcher requires an injected ViewFactory capable of producing TextView references.
Calling setText executes the factory, populates the string, and triggers the animator concurrently.
[Link] {​
TextView(this).apply {​
layoutParams = [Link](​
[Link].MATCH_PARENT,​
[Link].WRAP_CONTENT​
)​
gravity = [Link]​
textSize = 18f​
setTextColor([Link])​
}​
}​

[Link] =
[Link](this, [Link].fade_in)​
[Link] =
[Link](this, [Link].fade_out)​

// Dynamically updates text while playing the injected animations​
[Link]("Critical Update Available")​

Works cited

1. [Link] | API reference - Android Developers,


[Link] 2. How is StaticLayout
used in Android? - Stack Overflow,
[Link] 3.
[Link] Class ([Link]) - Microsoft Learn,
[Link]
5.0 4. How to Create a Button Group with MaterialButtonToggleGroup - Learn to Droid,
[Link] 5.
Material Toggle Button: Android Studio Tutorial (Kotlin) - YouTube,
[Link] 6. Hands-on with Material Components for
Android: Buttons | by Nick Rout | Over Engineering,
[Link]
6fa1a92ec0a 7. RangeSlider | compose-multiplatform – Kotlin Programming Language,
[Link]
[Link] 8. Sliders – Material Design 3, [Link] 9.
Slider in Compose material3 Android Kotlin | by Valeria Lopez Chavez | Medium,
[Link]
10. RangeSlider | API reference - Android Developers,
[Link] 11.
[Link] - material-components-android - GitHub,
[Link]
ents/[Link] 12. SearchBar | compose-multiplatform – Kotlin Programming Language,
[Link]
[Link] 13. Search – Material Design 3, [Link] 14.
how to use material 3 searchbar from xml - Stack Overflow,
[Link] 15.
BottomSheet Demo in Kotlin Android | by Anubhav Sharma | May, 2021 - Medium,
[Link] 16. How to
create BottomSheet in Android Kotlin - Stack Overflow,
[Link] 17.
Getting Started with Bottom Sheets in Android Using Kotlin Part 2 [Beginner Friendly],
[Link]
nner-friendly-1in4 18. Android Bottom Sheet Behavior and Animated Button on Top of It | by
Elvina Sh,
[Link]
6a9bfe545 19. BottomSheetBehavior | API reference - Android Developers,
[Link]
Behavior 20. Update your app to support future predictive back gesture | Google Codelabs,
[Link] 21. Provide custom
back navigation - Android Developers,
[Link] 22. Back button in
android webview within a fragment - Stack Overflow,
[Link]
nt 23. onBackPressed() is deprecated. What is the alternative? - Stack Overflow,
[Link]
tive 24. Getting started | Android media,
[Link] 25. Implementing ExoPlayer
Using Android Media3 — Part II | by Anand Jeyapal | Medium,
[Link]
a430b4 26. Media3 exoplayer custom player view control layout pause and play buttons not
working,
[Link]
out-pause-and-play-buttons-not-wo 27. ViewFlipper Tutorial With Example In Android Studio,
[Link] 28. ViewFlipper | API reference - Android Developers,
[Link] 29. ImageSwitcher | API
reference - Android Developers,
[Link] 30. Android Studio -
ViewFlipper to switch Views, Images and Text - YouTube,
[Link]

You might also like