Skip to Content

Câu hỏi phỏng vấn Android UI - Views

Phần này tổng hợp các câu hỏi về Android UI Views - hệ thống View truyền thống của Android.


1. View Fundamentals

Q: View và ViewGroup khác nhau thế nào?

Trả lời:

AspectViewViewGroup
DefinitionSingle UI elementContainer for Views
Can contain children
ExamplesTextView, Button, ImageViewLinearLayout, ConstraintLayout
HierarchyLeaf nodeBranch node
// View - single element val textView = TextView(context).apply { text = "Hello, World!" textSize = 16f } // ViewGroup - container val linearLayout = LinearLayout(context).apply { orientation = LinearLayout.VERTICAL addView(TextView(context).apply { text = "Child 1" }) addView(TextView(context).apply { text = "Child 2" }) }

💡 Mẹo: View.java có hơn 34,000 dòng code! Tránh tạo View không cần thiết để optimize performance.

📚 Tìm hiểu thêm: AndroidView trong Compose


Q: View Lifecycle gồm những giai đoạn nào?

Trả lời:

PhaseMethodPurpose
CreationonFinishInflate()After XML inflation
AttachmentonAttachedToWindow()View added to window
MeasurementonMeasure()Determine size
LayoutonLayout()Position children
DrawingonDraw()Render on canvas
InteractiononTouchEvent()Handle user input
DetachmentonDetachedFromWindow()Cleanup
class CustomView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null ) : View(context, attrs) { override fun onAttachedToWindow() { super.onAttachedToWindow() // Start animations, register listeners } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { // Calculate desired size val width = calculateWidth(widthMeasureSpec) val height = calculateHeight(heightMeasureSpec) setMeasuredDimension(width, height) } override fun onDraw(canvas: Canvas) { // Draw content canvas.drawRect(...) canvas.drawText(...) } override fun onDetachedFromWindow() { super.onDetachedFromWindow() // Cleanup resources } }

2. RecyclerView

Q: RecyclerView hoạt động như thế nào?

Trả lời:

RecyclerView tái sử dụng ViewHolders để hiển thị large datasets hiệu quả.

Components:

ComponentResponsibility
AdapterCreate ViewHolders, bind data
ViewHolderHold view references
LayoutManagerPosition items
ItemDecorationDividers, spacing
ItemAnimatorAdd/remove animations
class UserAdapter : RecyclerView.Adapter<UserAdapter.ViewHolder>() { private var items = listOf<User>() class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { private val binding = ItemUserBinding.bind(view) fun bind(user: User) { binding.nameText.text = user.name binding.avatar.load(user.avatarUrl) } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = ItemUserBinding.inflate( LayoutInflater.from(parent.context), parent, false ) return ViewHolder(view.root) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { holder.bind(items[position]) } override fun getItemCount() = items.size fun submitList(newItems: List<User>) { items = newItems notifyDataSetChanged() // Use DiffUtil for better performance } }

Q: DiffUtil là gì? Tại sao nên dùng?

Trả lời:

DiffUtil tính toán minimal updates cần thiết để biến đổi list cũ thành list mới.

// ❌ Without DiffUtil - redraws everything adapter.submitList(newList) adapter.notifyDataSetChanged() // ✅ With DiffUtil - only updates changed items class UserDiffCallback : DiffUtil.ItemCallback<User>() { override fun areItemsTheSame(oldItem: User, newItem: User): Boolean { return oldItem.id == newItem.id } override fun areContentsTheSame(oldItem: User, newItem: User): Boolean { return oldItem == newItem } } class UserAdapter : ListAdapter<User, UserAdapter.ViewHolder>(UserDiffCallback()) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { // ... } override fun onBindViewHolder(holder: ViewHolder, position: Int) { holder.bind(getItem(position)) } } // Usage adapter.submitList(newList) // DiffUtil handles updates automatically

Benefits:

  • ✅ Smooth animations
  • ✅ Better performance
  • ✅ Preserves scroll position

Q: RecyclerView optimizations?

Trả lời:

// 1. Use setHasFixedSize for fixed-size items recyclerView.setHasFixedSize(true) // 2. Set item view cache size recyclerView.setItemViewCacheSize(20) // 3. Use RecycledViewPool for multiple RecyclerViews val sharedPool = RecyclerView.RecycledViewPool() recyclerView1.setRecycledViewPool(sharedPool) recyclerView2.setRecycledViewPool(sharedPool) // 4. Avoid nested RecyclerViews (use ConcatAdapter) val concatAdapter = ConcatAdapter(headerAdapter, contentAdapter, footerAdapter) recyclerView.adapter = concatAdapter // 5. Use stable IDs class MyAdapter : RecyclerView.Adapter<ViewHolder>() { init { setHasStableIds(true) } override fun getItemId(position: Int): Long = items[position].id } // 6. Prefetch for nested scrolling (recyclerView.layoutManager as LinearLayoutManager) .initialPrefetchItemCount = 4

3. Custom Views

Q: Làm sao tạo Custom View?

Trả lời:

3 cách tạo Custom View:

1. Compound View (combine existing views)

class UserInfoView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null ) : LinearLayout(context, attrs) { private val binding: ViewUserInfoBinding init { orientation = HORIZONTAL binding = ViewUserInfoBinding.inflate( LayoutInflater.from(context), this ) // Read custom attributes context.obtainStyledAttributes(attrs, R.styleable.UserInfoView).use { binding.nameText.text = it.getString(R.styleable.UserInfoView_userName) } } fun setUser(user: User) { binding.nameText.text = user.name binding.avatar.load(user.avatarUrl) } }

2. Custom Drawing (draw on canvas)

class CircleProgressView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null ) : View(context, attrs) { private val paint = Paint(Paint.ANTI_ALIAS_FLAG) var progress: Float = 0f set(value) { field = value.coerceIn(0f, 1f) invalidate() // Request redraw } override fun onDraw(canvas: Canvas) { val centerX = width / 2f val centerY = height / 2f val radius = minOf(centerX, centerY) - paint.strokeWidth // Draw background circle paint.color = Color.LTGRAY canvas.drawCircle(centerX, centerY, radius, paint) // Draw progress arc paint.color = Color.BLUE canvas.drawArc( centerX - radius, centerY - radius, centerX + radius, centerY + radius, -90f, 360f * progress, false, paint ) } }

3. Extend existing View

class SquareImageView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null ) : AppCompatImageView(context, attrs) { override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { super.onMeasure(widthMeasureSpec, heightMeasureSpec) // Make it square val size = minOf(measuredWidth, measuredHeight) setMeasuredDimension(size, size) } }

4. Touch Handling

Q: Touch event handling trong Android?

Trả lời:

Touch events được dispatch từ parent → children (tunnel down), xử lý từ children → parent (bubble up).

MethodPurposeReturn
dispatchTouchEvent()Dispatch to childrentrue if handled
onInterceptTouchEvent()Parent interceptstrue to steal
onTouchEvent()Handle touchtrue if consumed
class CustomViewGroup : FrameLayout { override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { // Return true to steal touch from children return when (ev.action) { MotionEvent.ACTION_MOVE -> { val dx = abs(ev.x - startX) dx > touchSlop // Intercept if horizontal swipe } else -> false } } override fun onTouchEvent(event: MotionEvent): Boolean { when (event.action) { MotionEvent.ACTION_DOWN -> { startX = event.x return true } MotionEvent.ACTION_MOVE -> { // Handle drag return true } MotionEvent.ACTION_UP -> { // Handle release return true } } return super.onTouchEvent(event) } }

5. Layouts

Q: ConstraintLayout vs other layouts?

Trả lời:

LayoutNestingPerformanceFlexibility
ConstraintLayoutFlat✅ Best✅ Highest
LinearLayoutCan nest⚠️ OKMedium
RelativeLayout2-pass⚠️ SlowerMedium
FrameLayoutFlat✅ FastLow
<!-- ConstraintLayout - flat hierarchy --> <androidx.constraintlayout.widget.ConstraintLayout> <ImageView android:id="@+id/avatar" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"/> <TextView android:id="@+id/name" app:layout_constraintStart_toEndOf="@id/avatar" app:layout_constraintTop_toTopOf="@id/avatar"/> <TextView android:id="@+id/email" app:layout_constraintStart_toStartOf="@id/name" app:layout_constraintTop_toBottomOf="@id/name"/> </androidx.constraintlayout.widget.ConstraintLayout>

ConstraintLayout features:

  • Chains, barriers, guidelines
  • Percentage sizing
  • Circular positioning
  • MotionLayout animations

Q: View measure specs?

Trả lời:

MeasureSpec là 32-bit int chứa mode (2 bits) và size (30 bits).

ModeDescriptionExample
EXACTLYExact size required100dp, match_parent
AT_MOSTMax size limitwrap_content with limit
UNSPECIFIEDNo constraintsScrollView children
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { val widthMode = MeasureSpec.getMode(widthMeasureSpec) val widthSize = MeasureSpec.getSize(widthMeasureSpec) val desiredWidth = 200.dpToPx() val width = when (widthMode) { MeasureSpec.EXACTLY -> widthSize MeasureSpec.AT_MOST -> minOf(desiredWidth, widthSize) else -> desiredWidth } setMeasuredDimension(width, calculatedHeight) }

6. Animations

Q: Animation types trong Android?

Trả lời:

TypeUse CaseAPI
Property AnimationAny propertyObjectAnimator, ValueAnimator
View AnimationSimple transformsXML, Animation class
TransitionLayout changesTransitionManager
MotionLayoutComplex gesturesMotionScene
// ObjectAnimator ObjectAnimator.ofFloat(view, "translationX", 0f, 100f).apply { duration = 300 interpolator = AccelerateDecelerateInterpolator() start() } // ViewPropertyAnimator (simpler) view.animate() .translationX(100f) .alpha(0.5f) .setDuration(300) .setInterpolator(AccelerateDecelerateInterpolator()) .start() // Transition TransitionManager.beginDelayedTransition(container, Fade()) view.visibility = View.GONE // Animates automatically // MotionLayout motionLayout.transitionToEnd() motionLayout.setTransitionDuration(500)

📝 View Best Practices

PracticeWhy
Keep hierarchy flatFaster measure/layout
Use ConstraintLayoutSingle-pass layout
Recycle ViewHoldersMemory efficiency
Use DiffUtilMinimal updates
Avoid requestLayout() in loopsExpensive
Use invalidate() for redrawNot full relayout
Clean up in onDetachedFromWindow()Prevent leaks
// ❌ Bad - deep hierarchy LinearLayout > LinearLayout > LinearLayout > TextView // ✅ Good - flat with ConstraintLayout ConstraintLayout > TextView, TextView, TextView

📚 Tìm hiểu thêm: AndroidView trong Compose

Last updated on