Configurable reader tap actions
parent
72187e7da0
commit
6f7f3dc5e2
@ -1,89 +0,0 @@
|
|||||||
package org.koitharu.kotatsu.core.util
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.view.GestureDetector
|
|
||||||
import android.view.MotionEvent
|
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
class GridTouchHelper(
|
|
||||||
context: Context,
|
|
||||||
private val listener: OnGridTouchListener,
|
|
||||||
) : GestureDetector.SimpleOnGestureListener() {
|
|
||||||
|
|
||||||
private val detector = GestureDetector(context, this)
|
|
||||||
private val width = context.resources.displayMetrics.widthPixels
|
|
||||||
private val height = context.resources.displayMetrics.heightPixels
|
|
||||||
private var isDispatching = false
|
|
||||||
|
|
||||||
init {
|
|
||||||
detector.setIsLongpressEnabled(true)
|
|
||||||
detector.setOnDoubleTapListener(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun dispatchTouchEvent(event: MotionEvent) {
|
|
||||||
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
|
|
||||||
isDispatching = listener.onProcessTouch(event.rawX.toInt(), event.rawY.toInt())
|
|
||||||
}
|
|
||||||
detector.onTouchEvent(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSingleTapConfirmed(event: MotionEvent): Boolean {
|
|
||||||
if (!isDispatching) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
val xIndex = (event.rawX * 2f / width).roundToInt()
|
|
||||||
val yIndex = (event.rawY * 2f / height).roundToInt()
|
|
||||||
listener.onGridTouch(
|
|
||||||
when (xIndex) {
|
|
||||||
0 -> AREA_LEFT
|
|
||||||
1 -> {
|
|
||||||
when (yIndex) {
|
|
||||||
0 -> AREA_TOP
|
|
||||||
1 -> AREA_CENTER
|
|
||||||
2 -> AREA_BOTTOM
|
|
||||||
else -> return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
2 -> AREA_RIGHT
|
|
||||||
else -> return false
|
|
||||||
},
|
|
||||||
)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLongPress(event: MotionEvent) {
|
|
||||||
super.onLongPress(event)
|
|
||||||
val xIndex = (event.rawX * 2f / width).roundToInt()
|
|
||||||
val yIndex = (event.rawY * 2f / height).roundToInt()
|
|
||||||
listener.onGridLongTouch(
|
|
||||||
when(xIndex) {
|
|
||||||
1 -> {
|
|
||||||
when (yIndex) {
|
|
||||||
1 -> AREA_CENTER
|
|
||||||
else -> -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> -1
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
const val AREA_CENTER = 1
|
|
||||||
const val AREA_LEFT = 2
|
|
||||||
const val AREA_RIGHT = 3
|
|
||||||
const val AREA_TOP = 4
|
|
||||||
const val AREA_BOTTOM = 5
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OnGridTouchListener {
|
|
||||||
|
|
||||||
fun onGridTouch(area: Int)
|
|
||||||
|
|
||||||
fun onGridLongTouch(area: Int)
|
|
||||||
|
|
||||||
fun onProcessTouch(rawX: Int, rawY: Int): Boolean
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
package org.koitharu.kotatsu.reader.data
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.getEnumValue
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.observe
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.putEnumValue
|
||||||
|
import org.koitharu.kotatsu.reader.domain.TapGridArea
|
||||||
|
import org.koitharu.kotatsu.reader.ui.tapgrid.TapAction
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class TapGridSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||||
|
|
||||||
|
private val prefs = context.getSharedPreferences("tap_grid", Context.MODE_PRIVATE)
|
||||||
|
|
||||||
|
fun getTapAction(area: TapGridArea, isLongTap: Boolean): TapAction? {
|
||||||
|
val key = getPrefKey(area, isLongTap)
|
||||||
|
return if (!isLongTap && key !in prefs) {
|
||||||
|
getDefaultTapAction(area)
|
||||||
|
} else {
|
||||||
|
prefs.getEnumValue(key, TapAction::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTapAction(area: TapGridArea, isLongTap: Boolean, action: TapAction?) {
|
||||||
|
val key = getPrefKey(area, isLongTap)
|
||||||
|
prefs.edit { putEnumValue(key, action) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun observe() = prefs.observe().flowOn(Dispatchers.IO)
|
||||||
|
|
||||||
|
private fun getPrefKey(area: TapGridArea, isLongTap: Boolean): String = if (isLongTap) {
|
||||||
|
area.name + "_long"
|
||||||
|
} else {
|
||||||
|
area.name
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDefaultTapAction(area: TapGridArea): TapAction = when (area) {
|
||||||
|
TapGridArea.TOP_LEFT,
|
||||||
|
TapGridArea.TOP_CENTER,
|
||||||
|
TapGridArea.CENTER_LEFT,
|
||||||
|
TapGridArea.BOTTOM_LEFT -> TapAction.PAGE_PREV
|
||||||
|
|
||||||
|
TapGridArea.CENTER -> TapAction.TOGGLE_UI
|
||||||
|
TapGridArea.TOP_RIGHT,
|
||||||
|
TapGridArea.CENTER_RIGHT,
|
||||||
|
TapGridArea.BOTTOM_CENTER,
|
||||||
|
TapGridArea.BOTTOM_RIGHT -> TapAction.PAGE_NEXT
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
package org.koitharu.kotatsu.reader.domain
|
||||||
|
|
||||||
|
enum class TapGridArea {
|
||||||
|
|
||||||
|
TOP_LEFT,
|
||||||
|
TOP_CENTER,
|
||||||
|
TOP_RIGHT,
|
||||||
|
CENTER_LEFT,
|
||||||
|
CENTER,
|
||||||
|
CENTER_RIGHT,
|
||||||
|
BOTTOM_LEFT,
|
||||||
|
BOTTOM_CENTER,
|
||||||
|
BOTTOM_RIGHT;
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
package org.koitharu.kotatsu.reader.ui.tapgrid
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
|
||||||
|
enum class TapAction(
|
||||||
|
@StringRes val nameStringResId: Int,
|
||||||
|
val color: Int,
|
||||||
|
) {
|
||||||
|
|
||||||
|
PAGE_NEXT(R.string.next_page, 0x8BFF00),
|
||||||
|
PAGE_PREV(R.string.prev_page, 0xFF4700),
|
||||||
|
CHAPTER_NEXT(R.string.next_chapter, 0x327E49),
|
||||||
|
CHAPTER_PREV(R.string.prev_chapter, 0x7E1218),
|
||||||
|
TOGGLE_UI(R.string.toggle_ui, 0x3D69C5),
|
||||||
|
SHOW_MENU(R.string.show_menu, 0xAA1AC5),
|
||||||
|
}
|
||||||
@ -0,0 +1,82 @@
|
|||||||
|
package org.koitharu.kotatsu.reader.ui.tapgrid
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.GestureDetector
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import org.koitharu.kotatsu.reader.domain.TapGridArea
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
class TapGridDispatcher(
|
||||||
|
context: Context,
|
||||||
|
private val listener: OnGridTouchListener,
|
||||||
|
) : GestureDetector.SimpleOnGestureListener() {
|
||||||
|
|
||||||
|
private val detector = GestureDetector(context, this)
|
||||||
|
private val width = context.resources.displayMetrics.widthPixels
|
||||||
|
private val height = context.resources.displayMetrics.heightPixels
|
||||||
|
private var isDispatching = false
|
||||||
|
|
||||||
|
init {
|
||||||
|
detector.setIsLongpressEnabled(true)
|
||||||
|
detector.setOnDoubleTapListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dispatchTouchEvent(event: MotionEvent) {
|
||||||
|
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
|
||||||
|
isDispatching = listener.onProcessTouch(event.rawX.toInt(), event.rawY.toInt())
|
||||||
|
}
|
||||||
|
detector.onTouchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSingleTapConfirmed(event: MotionEvent): Boolean {
|
||||||
|
if (!isDispatching) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return listener.onGridTouch(getArea(event.rawX, event.rawY))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLongPress(event: MotionEvent) {
|
||||||
|
if (isDispatching) {
|
||||||
|
listener.onGridLongTouch(getArea(event.rawX, event.rawY))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getArea(x: Float, y: Float): TapGridArea {
|
||||||
|
val xIndex = (x * 2f / width).roundToInt()
|
||||||
|
val yIndex = (y * 2f / height).roundToInt()
|
||||||
|
val area = when (xIndex) {
|
||||||
|
0 -> when (yIndex) { // LEFT
|
||||||
|
0 -> TapGridArea.TOP_LEFT
|
||||||
|
1 -> TapGridArea.CENTER_LEFT
|
||||||
|
2 -> TapGridArea.BOTTOM_LEFT
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
1 -> when (yIndex) { // CENTER
|
||||||
|
0 -> TapGridArea.TOP_CENTER
|
||||||
|
1 -> TapGridArea.CENTER
|
||||||
|
2 -> TapGridArea.BOTTOM_CENTER
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
2 -> when (yIndex) { // RIGHT
|
||||||
|
0 -> TapGridArea.TOP_RIGHT
|
||||||
|
1 -> TapGridArea.CENTER_RIGHT
|
||||||
|
2 -> TapGridArea.BOTTOM_RIGHT
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
return checkNotNull(area) { "Invalid area ($xIndex, $yIndex)" }
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OnGridTouchListener {
|
||||||
|
|
||||||
|
fun onGridTouch(area: TapGridArea): Boolean
|
||||||
|
|
||||||
|
fun onGridLongTouch(area: TapGridArea)
|
||||||
|
|
||||||
|
fun onProcessTouch(rawX: Int, rawY: Int): Boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,129 @@
|
|||||||
|
package org.koitharu.kotatsu.settings.reader
|
||||||
|
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.graphics.drawable.ColorDrawable
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.graphics.drawable.LayerDrawable
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.core.graphics.ColorUtils
|
||||||
|
import androidx.core.graphics.Insets
|
||||||
|
import androidx.core.text.bold
|
||||||
|
import androidx.core.text.buildSpannedString
|
||||||
|
import androidx.core.view.updatePadding
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.findKeyByValue
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.getThemeDrawable
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.observe
|
||||||
|
import org.koitharu.kotatsu.databinding.ActivityReaderTapActionsBinding
|
||||||
|
import org.koitharu.kotatsu.reader.data.TapGridSettings
|
||||||
|
import org.koitharu.kotatsu.reader.domain.TapGridArea
|
||||||
|
import org.koitharu.kotatsu.reader.ui.tapgrid.TapAction
|
||||||
|
import java.util.EnumMap
|
||||||
|
import javax.inject.Inject
|
||||||
|
import com.google.android.material.R as materialR
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class ReaderTapGridConfigActivity : BaseActivity<ActivityReaderTapActionsBinding>(), View.OnClickListener,
|
||||||
|
View.OnLongClickListener {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var tapGridSettings: TapGridSettings
|
||||||
|
|
||||||
|
private val controls = EnumMap<TapGridArea, TextView>(TapGridArea::class.java)
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(ActivityReaderTapActionsBinding.inflate(layoutInflater))
|
||||||
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
controls[TapGridArea.TOP_LEFT] = viewBinding.textViewTopLeft
|
||||||
|
controls[TapGridArea.TOP_CENTER] = viewBinding.textViewTopCenter
|
||||||
|
controls[TapGridArea.TOP_RIGHT] = viewBinding.textViewTopRight
|
||||||
|
controls[TapGridArea.CENTER_LEFT] = viewBinding.textViewCenterLeft
|
||||||
|
controls[TapGridArea.CENTER] = viewBinding.textViewCenter
|
||||||
|
controls[TapGridArea.CENTER_RIGHT] = viewBinding.textViewCenterRight
|
||||||
|
controls[TapGridArea.BOTTOM_LEFT] = viewBinding.textViewBottomLeft
|
||||||
|
controls[TapGridArea.BOTTOM_CENTER] = viewBinding.textViewBottomCenter
|
||||||
|
controls[TapGridArea.BOTTOM_RIGHT] = viewBinding.textViewBottomRight
|
||||||
|
|
||||||
|
controls.forEach { (_, view) ->
|
||||||
|
view.setOnClickListener(this)
|
||||||
|
view.setOnLongClickListener(this)
|
||||||
|
}
|
||||||
|
updateValues()
|
||||||
|
tapGridSettings.observe().observe(this) { updateValues() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onWindowInsetsChanged(insets: Insets) {
|
||||||
|
viewBinding.root.updatePadding(
|
||||||
|
left = insets.left,
|
||||||
|
top = insets.top,
|
||||||
|
right = insets.right,
|
||||||
|
bottom = insets.bottom,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(v: View) {
|
||||||
|
val area = controls.findKeyByValue(v) ?: return
|
||||||
|
showActionSelector(area, isLongTap = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLongClick(v: View?): Boolean {
|
||||||
|
val area = controls.findKeyByValue(v) ?: return false
|
||||||
|
showActionSelector(area, isLongTap = true)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateValues() {
|
||||||
|
controls.forEach { (area, view) ->
|
||||||
|
view.text = buildSpannedString {
|
||||||
|
appendLine(getString(R.string.tap_action))
|
||||||
|
bold {
|
||||||
|
appendLine(getTapActionText(area, isLongTap = false))
|
||||||
|
}
|
||||||
|
appendLine()
|
||||||
|
appendLine(getString(R.string.long_tap_action))
|
||||||
|
bold {
|
||||||
|
appendLine(getTapActionText(area, isLongTap = true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
view.background = createBackground(tapGridSettings.getTapAction(area, false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getTapActionText(area: TapGridArea, isLongTap: Boolean): String {
|
||||||
|
return tapGridSettings.getTapAction(area, isLongTap)?.let {
|
||||||
|
getString(it.nameStringResId)
|
||||||
|
} ?: getString(R.string.none)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showActionSelector(area: TapGridArea, isLongTap: Boolean) {
|
||||||
|
val selectedItem = tapGridSettings.getTapAction(area, isLongTap)?.ordinal ?: -1
|
||||||
|
val listener = DialogInterface.OnClickListener { dialog, which ->
|
||||||
|
tapGridSettings.setTapAction(area, isLongTap, TapAction.entries.getOrNull(which - 1))
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
val names = arrayOfNulls<String>(TapAction.entries.size + 1)
|
||||||
|
names[0] = getString(R.string.none)
|
||||||
|
TapAction.entries.forEachIndexed { index, action -> names[index + 1] = getString(action.nameStringResId) }
|
||||||
|
MaterialAlertDialogBuilder(this)
|
||||||
|
.setSingleChoiceItems(names, selectedItem + 1, listener)
|
||||||
|
.setTitle(if (isLongTap) R.string.long_tap_action else R.string.tap_action)
|
||||||
|
.setIcon(R.drawable.ic_tap)
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createBackground(action: TapAction?): Drawable? {
|
||||||
|
val ripple = getThemeDrawable(materialR.attr.selectableItemBackground)
|
||||||
|
return if (action == null) {
|
||||||
|
ripple
|
||||||
|
} else {
|
||||||
|
LayerDrawable(arrayOf(ripple, ColorDrawable(ColorUtils.setAlphaComponent(action.color, 60))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,191 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Guideline
|
||||||
|
android:id="@+id/guideline_top"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layout_constraintGuide_percent="0.33" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Guideline
|
||||||
|
android:id="@+id/guideline_bottom"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layout_constraintGuide_percent="0.67" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Guideline
|
||||||
|
android:id="@+id/guideline_left"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintGuide_percent="0.33" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Guideline
|
||||||
|
android:id="@+id/guideline_right"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintGuide_percent="0.67" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_top_left"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:gravity="center"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/guideline_top"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/guideline_left"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_top_center"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:gravity="center"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/guideline_top"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/guideline_right"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/guideline_left"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_top_right"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:gravity="center"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/guideline_top"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/guideline_right"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_center_left"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:gravity="center"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/textView_bottom_left"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/guideline_left"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/textView_top_left" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_center"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:gravity="center"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/textView_bottom_center"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/guideline_right"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/guideline_left"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/textView_top_center" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_center_right"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:gravity="center"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/textView_bottom_right"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/guideline_right"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/textView_top_right" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_bottom_left"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:gravity="center"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/guideline_left"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/guideline_bottom" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_bottom_center"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:gravity="center"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/guideline_right"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/guideline_left"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/guideline_bottom" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_bottom_right"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:gravity="center"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/guideline_right"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/guideline_bottom" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="1dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:background="?android:divider"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/guideline_right"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/guideline_right"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="1dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:background="?android:divider"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/guideline_left"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/guideline_left"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="?android:divider"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/guideline_top"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/guideline_top" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="?android:divider"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/guideline_bottom"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/guideline_bottom" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="?android:divider"
|
||||||
|
app:layout_constraintBottom_toTopOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</LinearLayout>
|
||||||
Loading…
Reference in New Issue