Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(sdds-acore/uikit): Focus selector in ViewGroup #188

Merged
merged 1 commit into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ internal abstract class ComponentFragment : Fragment(), PropertiesAdapter.Intera

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding?.componentContainer?.setOnClickListener {
view.findFocus()?.clearFocus()
}
propertiesOwner.properties
.flowWithLifecycle(lifecycle, Lifecycle.State.RESUMED)
.onEach { propertiesAdapter.updateProperties(it) }
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<androidx.constraintlayout.widget.ConstraintLayout
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"
android:padding="@dimen/sdds_spacer_8x"
Expand All @@ -21,18 +22,21 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">

<FrameLayout
<com.sdds.uikit.FrameLayout
android:id="@+id/component_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="false"
android:clipChildren="false"/>

</com.sdds.uikit.FrameLayout>

<com.sdds.uikit.FrameLayout
<com.sdds.uikit.LinearLayout
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="@dimen/sdds_spacer_8x"
android:orientation="vertical"
app:sd_clipToOutline="true"
app:sd_shapeAppearance="@style/Serv.SddsServ.Shape.Round.L"
app:sd_strokeColor="?serv_surfaceDefaultSolidTertiary"
app:sd_strokeWidth="1dp"
Expand All @@ -41,28 +45,22 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/component_canvas">
app:layout_constraintTop_toBottomOf="@id/component_canvas"
tools:targetApi="s">

<include
android:id="@+id/header"
layout="@layout/layout_header_property_item" />

<LinearLayout
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/properties_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<include
android:id="@+id/header"
layout="@layout/layout_header_property_item"
/>
android:orientation="vertical"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/properties_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />

</LinearLayout>

</com.sdds.uikit.FrameLayout>
</com.sdds.uikit.LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
style="?sd_cellStyle"
android:id="@+id/cell"
android:focusable="true"
app:sd_shapeAppearance="?serv_shapeRoundM"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
Expand Down
12 changes: 12 additions & 0 deletions playground/sandbox/src/main/res/values/styles_chip.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,22 +63,34 @@
<style name="Theme.Sandbox.Components.Chip.L.Embedded">
<item name="android:minHeight">44dp</item>
<item name="sd_shapeAppearance">?serv_shapeRoundS</item>
<item name="sd_fsStrokeInset">0dp</item>
<item name="sd_fsShapeAdjustment">0dp</item>
<item name="sd_fsScaleFactor">0.03</item>
</style>

<style name="Theme.Sandbox.Components.Chip.M.Embedded">
<item name="android:minHeight">36dp</item>
<item name="sd_shapeAppearance">?serv_shapeRoundXs</item>
<item name="sd_fsStrokeInset">0dp</item>
<item name="sd_fsShapeAdjustment">0dp</item>
<item name="sd_fsScaleFactor">0.03</item>
</style>

<style name="Theme.Sandbox.Components.Chip.S.Embedded">
<item name="android:minHeight">28dp</item>
<item name="sd_shapeAppearance">?serv_shapeRoundXxs</item>
<item name="sd_fsStrokeInset">0dp</item>
<item name="sd_fsShapeAdjustment">0dp</item>
<item name="sd_fsScaleFactor">0.03</item>
</style>

<style name="Theme.Sandbox.Components.Chip.XS.Embedded">
<item name="android:minHeight">20dp</item>
<item name="sd_shapeAppearance">?serv_shapeRoundXxs</item>
<item name="sd_shapeAppearanceAdjustment">-2dp</item>
<item name="sd_fsStrokeInset">0dp</item>
<item name="sd_fsShapeAdjustment">0dp</item>
<item name="sd_fsScaleFactor">0.03</item>
</style>

<style name="Theme.Sandbox.Components.Chip.L.Pilled">
Expand Down
33 changes: 31 additions & 2 deletions sdds-core/uikit/src/main/kotlin/com/sdds/uikit/CellLayout.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ import com.sdds.uikit.CellLayout.CellContent.START
import com.sdds.uikit.CellLayout.CellContent.SUBTITLE
import com.sdds.uikit.CellLayout.CellContent.TITLE
import com.sdds.uikit.internal.base.configure
import com.sdds.uikit.internal.base.isClippedToOutline
import com.sdds.uikit.internal.focusselector.FocusSelectorDelegate
import com.sdds.uikit.internal.focusselector.HasFocusSelector
import com.sdds.uikit.shape.ShapeModel
import com.sdds.uikit.shape.Shapeable
import com.sdds.uikit.shape.shapeable
Expand All @@ -43,7 +46,7 @@ open class CellLayout @JvmOverloads constructor(
attrs: AttributeSet? = null,
defStyleAttr: Int = R.attr.sd_cellStyle,
defStyleRes: Int = R.style.Sdds_Components_Cell,
) : ViewGroup(context, attrs, defStyleAttr, defStyleRes), Shapeable {
) : ViewGroup(context, attrs, defStyleAttr, defStyleRes), Shapeable, HasFocusSelector by FocusSelectorDelegate() {

private val _shaper = shapeable(attrs, defStyleAttr)
private val _startContentBounds: Rect = Rect()
Expand Down Expand Up @@ -143,6 +146,33 @@ open class CellLayout @JvmOverloads constructor(
_disclosureEnabled = typedArray.getBoolean(R.styleable.CellLayout_sd_disclosureEnabled, false)
_forceDuplicateParentState = typedArray.getBoolean(R.styleable.CellLayout_sd_forceDuplicateParentState, false)
typedArray.recycle()
clipToOutline = context.isClippedToOutline(attrs, defStyleAttr, defStyleRes)
@Suppress("LeakingThis")
applySelector(this, context, attrs, defStyleAttr)
}

override fun setPressed(pressed: Boolean) {
if (isPressed != pressed) {
handlePressedChange(this, pressed)
}
super.setPressed(pressed)
}

override fun onFocusChanged(gainFocus: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect)
updateFocusSelector(this, gainFocus)
redispatchActivated()
}

override fun dispatchSetActivated(activated: Boolean) {
// При вызове setActivated хотим, чтобы на children всегда приходил activated = false
// Чтобы потом самостоятельно отправить им нужное значение
super.dispatchSetActivated(false)
}

private fun redispatchActivated() {
// отправляем всем children значение activate = true, если в контейнер в фокусе
super.dispatchSetActivated(isFocused)
}

override fun addView(child: View?, index: Int, params: ViewGroup.LayoutParams?) {
Expand Down Expand Up @@ -395,7 +425,6 @@ open class CellLayout @JvmOverloads constructor(
}

private fun View.applyContentRole(cellParams: LayoutParams) {
isDuplicateParentStateEnabled = cellParams.forceDuplicateParentState && _forceDuplicateParentState
(this as? TextView)?.apply {
when (cellParams.cellContent) {
LABEL -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Checkable
import android.widget.CompoundButton
import android.widget.LinearLayout
import androidx.core.view.children
import androidx.core.view.marginTop
import androidx.core.view.updateMarginsRelative
Expand Down
30 changes: 29 additions & 1 deletion sdds-core/uikit/src/main/kotlin/com/sdds/uikit/FlowLayout.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ import android.view.ViewDebug
import android.view.ViewGroup
import androidx.core.view.isGone
import com.sdds.uikit.internal.base.configure
import com.sdds.uikit.internal.base.isClippedToOutline
import com.sdds.uikit.internal.focusselector.FocusSelectorDelegate
import com.sdds.uikit.internal.focusselector.HasFocusSelector
import com.sdds.uikit.shape.ShapeModel
import com.sdds.uikit.shape.Shapeable
import com.sdds.uikit.shape.shapeable

/**
* FlowLayout поддерживает позиционирование с автоматическим построчным переносом.
Expand All @@ -25,8 +31,9 @@ open class FlowLayout @JvmOverloads constructor(
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0,
) : ViewGroup(context, attrs, defStyleAttr, defStyleRes) {
) : ViewGroup(context, attrs, defStyleAttr, defStyleRes), Shapeable, HasFocusSelector by FocusSelectorDelegate() {

private val _shapeable: Shapeable = shapeable(attrs, defStyleAttr)
private val _flowManager: FlowLayoutManager
private var _horizontalSpacing: Int
private var _verticalSpacing: Int
Expand All @@ -46,6 +53,12 @@ open class FlowLayout @JvmOverloads constructor(
}
}

/**
* @see Shapeable.shape
*/
override val shape: ShapeModel?
get() = _shapeable.shape

init {
if (DEBUG_BOUNDS) {
setWillNotDraw(false)
Expand All @@ -56,12 +69,27 @@ open class FlowLayout @JvmOverloads constructor(
_gravity = typedArray.getInt(R.styleable.FlowLayout_android_gravity, Gravity.TOP or Gravity.START)
_flowManager = FlowLayoutManager(_horizontalSpacing, _verticalSpacing)
typedArray.recycle()
clipToOutline = context.isClippedToOutline(attrs, defStyleAttr, defStyleRes)
@Suppress("LeakingThis")
applySelector(this, context, attrs, defStyleAttr)
}

internal fun getRowBounds(rowIndex: Int, outRect: Rect) {
outRect.set(_flowManager.boundsList.getOrNull(rowIndex) ?: return)
}

override fun onFocusChanged(gainFocus: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect)
updateFocusSelector(this, gainFocus)
}

override fun setPressed(pressed: Boolean) {
if (isPressed != pressed) {
handlePressedChange(this, pressed)
}
super.setPressed(pressed)
}

override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (DEBUG_BOUNDS) _flowManager.drawDebug(canvas)
Expand Down
46 changes: 40 additions & 6 deletions sdds-core/uikit/src/main/kotlin/com/sdds/uikit/FrameLayout.kt
Original file line number Diff line number Diff line change
@@ -1,26 +1,60 @@
package com.sdds.uikit

import android.content.Context
import android.graphics.Rect
import android.util.AttributeSet
import com.sdds.uikit.internal.base.shape.ShapeHelper
import com.sdds.uikit.internal.base.isClippedToOutline
import com.sdds.uikit.internal.focusselector.FocusSelectorDelegate
import com.sdds.uikit.internal.focusselector.HasFocusSelector
import com.sdds.uikit.shape.ShapeModel
import com.sdds.uikit.shape.Shapeable
import com.sdds.uikit.shape.shapeable
import android.widget.FrameLayout as AndroidFrameLayout

/**
* Реализация [FrameLayout] с поддержкой ShapeModel.
* Реализация [FrameLayout] с поддержкой ShapeModel и селектором фокуса
* @param context контекст
* @param attrs аттрибуты view
* @param defStyleAttr аттрибут стиля по умолчанию
* @author Малышев Александр on 27.08.2024
*/
@Suppress("LeakingThis")
open class FrameLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
) : AndroidFrameLayout(context, attrs, defStyleAttr) {
defStyleRes: Int = 0,
) : AndroidFrameLayout(
context,
attrs,
defStyleAttr,
defStyleRes,
),
Shapeable,
HasFocusSelector by FocusSelectorDelegate() {

private val _shapeable: Shapeable = shapeable(attrs, defStyleAttr, defStyleRes)

/**
* @see Shapeable.shape
*/
override val shape: ShapeModel?
get() = _shapeable.shape

init {
ShapeHelper(this, attrs, defStyleAttr)
clipToOutline = true
clipToOutline = context.isClippedToOutline(attrs, defStyleAttr, defStyleRes)
@Suppress("LeakingThis")
applySelector(this, context, attrs, defStyleAttr)
}

override fun onFocusChanged(gainFocus: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect)
updateFocusSelector(this, gainFocus)
}

override fun setPressed(pressed: Boolean) {
if (isPressed != pressed) {
handlePressedChange(this, pressed)
}
super.setPressed(pressed)
}
}
Loading
Loading