Irbisa · Шпаргалка
View, разметка и RecyclerView
Разметка, ConstraintLayout, ViewBinding, свои View, measure/layout/draw, RecyclerView, DiffUtil
Чем отличаются
View.VISIBLE,View.INVISIBLEиView.GONE?Чем отличаются
px,dpиsp, и что где использовать?Чем стиль отличается от темы, и что означает
?attr/colorPrimary?<!-- res/values/themes.xml: a theme, applied to the whole app --><style name="Theme.App" parent="Theme.Material3.DayNight.NoActionBar"> <item name="colorPrimary">@color/brand</item></style> <!-- A style: values for one view, colours taken from the theme -->…Чем отличаются ViewBinding, DataBinding и
findViewById?// build.gradle.kts: android { buildFeatures { viewBinding = true } } // res/layout/activity_main.xml -> ActivityMainBindingclass MainActivity : AppCompatActivity() { // Fine in an Activity: the Activity and its views die together private lateinit var binding: ActivityMainBinding…Как работает
RecyclerView, и когда вызываютсяonCreateViewHolderиonBindViewHolder?class OrderAdapter( private val items: List<Order>, private val onClick: (Order) -> Unit,) : RecyclerView.Adapter<OrderAdapter.Holder>() { class Holder(val binding: ItemOrderBinding) : RecyclerView.ViewHolder(binding.root)…Что добавляют
RecyclerViewклассыItemDecoration,ItemTouchHelperиSnapHelper?class SpacingDecoration(private val space: Int) : RecyclerView.ItemDecoration() { override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { val pos = parent.getChildAdapterPosition(view) if (pos == RecyclerView.NO_POSITION) return outRect.bottom = space if (pos == 0) outRect.top = space // equal gaps, no doubling between rows…Какие проблемы решает
ConstraintLayout, и что делают0dp, цепочки, guideline и barrier?<!-- right: 0dp fills what start..end leaves; match_parent would ignore the end constraint --><TextView android:id="@+id/title" android:layout_width="0dp" android:layout_height="wrap_content" app:layout_constraintTop_toTopOf="parent"…Как работает инфлейт разметки, и что меняют
<include>,<merge>иViewStub?<!-- res/layout/view_header.xml: <merge>, so both views attach to the including parent --><merge xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:parentTag="android.widget.LinearLayout"> <ImageView android:id="@+id/avatar"…Этот Fragment держит binding в
lateinit var, и LeakCanary сообщает об утечке. Что не так и как это исправить?class ProfileFragment : Fragment(R.layout.fragment_profile) { private lateinit var binding: FragmentProfileBinding override fun onViewCreated(view: View, savedInstanceState: Bundle?) { binding = FragmentProfileBinding.bind(view) lifecycleScope.launch { viewModel.state.collect { binding.title.text = it.title } } }}Какой жизненный цикл у
View, от создания доonDetachedFromWindow?Почему
view.widthравен 0 вonCreate, и как получить настоящий размер?// binding: ViewBinding for this Activity, as in the ViewBinding questionoverride fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(binding.root) Log.d(TAG, "width=${binding.chart.width}") // 0: not measured yet…Как работают
onMeasure,onLayoutиonDrawв кастомной View, и когда вызыватьinvalidate(), а когдаrequestLayout()?class MeterView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) { private val paint = Paint(Paint.ANTI_ALIAS_FLAG) // created once, not in onDraw private val bar = RectF()…Как добавить View собственные XML-атрибуты и сделать так, чтобы она сохраняла состояние при повороте?
// res/values/attrs.xml// <declare-styleable name="RatingView">// <attr name="rating" format="integer" />// <attr name="starColor" format="color" />// </declare-styleable>…Когда стоит использовать
SurfaceViewилиTextureViewвместо обычной View?Какие API анимации есть у View, и почему анимация
translationXплавная, а анимация высоты дёргается?// wrong: requestLayout and a re-measure of the branch on every frameValueAnimator.ofInt(0, targetHeight).apply { addUpdateListener { panel.updateLayoutParams { height = it.animatedValue as Int } }}.start() // right: RenderNode properties, no measure or layout…Что дают
DiffUtilиListAdapterпо сравнению сnotifyDataSetChanged(), и как обновить одну view в одной строке?class ChatAdapter : ListAdapter<Chat, ChatAdapter.Holder>(Diff) { override fun onBindViewHolder(holder: Holder, position: Int) { val item = getItem(position) holder.binding.title.text = item.title holder.binding.unread.text = item.unread.toString()…Найдите баг: после нажатия на лайк сердечко не закрашивается, пока строка не уйдёт за экран и не вернётся.
data class Post(val id: Long, val text: String, var liked: Boolean) object PostDiff : DiffUtil.ItemCallback<Post>() { override fun areItemsTheSame(old: Post, new: Post) = old.id == new.id override fun areContentsTheSame(old: Post, new: Post) = old == new} class FeedViewModel : ViewModel() { private val _posts = MutableStateFlow<List<Post>>(emptyList()) val posts: StateFlow<List<Post>> = _posts // the Fragment calls adapter.submitList(it) fun toggleLike(id: Long) { val copy = _posts.value.toMutableList() copy.first { it.id == id }.apply { liked = !liked } _posts.value = copy }}После скролла строки этого списка показывают бейдж предыдущего элемента и чужое фото. Что не так в
onBindViewHolder?override fun onBindViewHolder(holder: Holder, position: Int) { val item = items[position] holder.binding.title.text = item.title if (item.isUrgent) { holder.binding.badge.visibility = View.VISIBLE } holder.binding.root.setOnClickListener { onClick(items[position]) } imageLoader.load(item.photoUrl) { bitmap -> holder.binding.photo.setImageBitmap(bitmap) }}Найдите баг: после прокрутки списка задач те, которых вы не касались, сами становятся отмеченными или снимаются.
class TaskAdapter( private val onToggle: (Task, Boolean) -> Unit,) : ListAdapter<Task, TaskAdapter.Holder>(TaskDiff) { class Holder(val binding: ItemTaskBinding) : RecyclerView.ViewHolder(binding.root) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = Holder(ItemTaskBinding.inflate(LayoutInflater.from(parent.context), parent, false)) override fun onBindViewHolder(holder: Holder, position: Int) { val task = getItem(position) holder.binding.title.text = task.title holder.binding.done.isChecked = task.done holder.binding.done.setOnCheckedChangeListener { _, checked -> onToggle(task, checked) } }}Как показать в одном
RecyclerViewнесколько типов строк: заголовки, рекламу и карточки?// HeaderVH, ArticleVH, AdVH: ViewHolder subclasses, omittedsealed interface Row { val id: Long data class Header(override val id: Long, val title: String) : Row data class Article(override val id: Long, val headline: String) : Row data class Ad(override val id: Long, val slot: String) : Row…RecyclerViewдёргается при скролле. Что проверять и в каком порядке?Как
CoordinatorLayoutи сворачивающийся тулбар работают вместе сRecyclerView?<androidx.coordinatorlayout.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent"> <com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent"…Как touch-событие проходит по иерархии View, и как родитель забирает жест у ребёнка?
import kotlin.math.abs class CarouselHost(ctx: Context, attrs: AttributeSet?) : FrameLayout(ctx, attrs) { private val slop = ViewConfiguration.get(ctx).scaledTouchSlop private var downX = 0f private var downY = 0f…Почему этот код падает при нажатии на кнопку, но обычно не падает, когда похожая строка выполняется в
onCreate?override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(binding.root) thread { binding.status.text = "Loading" } // usually no crash binding.refresh.setOnClickListener { thread { binding.status.text = repository.load() } // crashes }}После повышения
targetSdkдо 35 и выше тулбар уехал под статус-бар, а клавиатура закрывает поле ввода. Что изменилось и как это исправить?// binding: ViewBinding for this Activity, as in the ViewBinding questionoverride fun onCreate(savedInstanceState: Bundle?) { enableEdgeToEdge() // same behaviour below API 35 super.onCreate(savedInstanceState) setContentView(binding.root)…