ReaderTransitionView: Use context theme color for texts (#5738)

* Put themed reader context in adapter

This avoids creating themed context everytime the page holder is created, this
also allows the transition view to use the same themed context.

* Check against app night mode to create themed reader context

* ReaderTransitionView: Use context theme color for texts

The whole reader will need to be recreated when changing reader background while
webtoon mode is used, because recreating just the RecyclerView without messing
up the scroll position is impossible (I hope I just missed something).

(cherry picked from commit 914b686c8eaf3954e291355975532eb28662c38c)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewerAdapter.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonConfig.kt
This commit is contained in:
Ivan Iskandar 2021-08-19 20:10:05 +07:00 committed by Jobobby04
parent 6aa59325d0
commit 32fa99377d
11 changed files with 64 additions and 60 deletions

View File

@ -8,22 +8,19 @@ import androidx.core.text.bold
import androidx.core.text.buildSpannedString import androidx.core.text.buildSpannedString
import androidx.core.view.isVisible import androidx.core.view.isVisible
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.databinding.ReaderTransitionViewBinding import eu.kanade.tachiyomi.databinding.ReaderTransitionViewBinding
import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition
import eu.kanade.tachiyomi.util.system.isNightMode
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class ReaderTransitionView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : class ReaderTransitionView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
LinearLayout(context, attrs) { LinearLayout(context, attrs) {
private val binding: ReaderTransitionViewBinding private val binding: ReaderTransitionViewBinding =
ReaderTransitionViewBinding.inflate(LayoutInflater.from(context), this, true)
init { init {
binding = ReaderTransitionViewBinding.inflate(LayoutInflater.from(context), this, true)
layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT) layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
} }
fun bind(transition: ChapterTransition) { fun bind(transition: ChapterTransition) {
when (transition) { when (transition) {
is ChapterTransition.Prev -> bindPrevChapterTransition(transition) is ChapterTransition.Prev -> bindPrevChapterTransition(transition)
@ -31,26 +28,6 @@ class ReaderTransitionView @JvmOverloads constructor(context: Context, attrs: At
} }
missingChapterWarning(transition) missingChapterWarning(transition)
val color = when (Injekt.get<PreferencesHelper>().readerTheme().get()) {
0 -> context.getColor(android.R.color.black)
3 -> context.getColor(automaticTextColor())
else -> context.getColor(android.R.color.white)
}
listOf(binding.upperText, binding.warningText, binding.lowerText).forEach {
it.setTextColor(color)
}
}
/**
* Picks text color for [ReaderActivity] based on light/dark theme preference
*/
private fun automaticTextColor(): Int {
return if (context.isNightMode()) {
android.R.color.white
} else {
android.R.color.black
}
} }
/** /**

View File

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.reader.viewer.pager
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.ActionBar import android.app.ActionBar
import android.content.Context
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.PointF import android.graphics.PointF
import android.graphics.drawable.Animatable import android.graphics.drawable.Animatable
@ -32,7 +33,6 @@ import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerConfig.ZoomType
import eu.kanade.tachiyomi.ui.webview.WebViewActivity import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.system.ImageUtil import eu.kanade.tachiyomi.util.system.ImageUtil
import eu.kanade.tachiyomi.util.system.createReaderThemeContext
import eu.kanade.tachiyomi.util.system.dpToPx import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.widget.ViewPagerAdapter import eu.kanade.tachiyomi.widget.ViewPagerAdapter
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -53,10 +53,11 @@ import kotlin.math.roundToInt
*/ */
@SuppressLint("ViewConstructor") @SuppressLint("ViewConstructor")
class PagerPageHolder( class PagerPageHolder(
readerThemedContext: Context,
val viewer: PagerViewer, val viewer: PagerViewer,
val page: ReaderPage, val page: ReaderPage,
private var extraPage: ReaderPage? = null private var extraPage: ReaderPage? = null
) : FrameLayout(viewer.activity), ViewPagerAdapter.PositionableView { ) : FrameLayout(readerThemedContext), ViewPagerAdapter.PositionableView {
/** /**
* Item that identifies this view. Needed by the adapter to not recreate views. * Item that identifies this view. Needed by the adapter to not recreate views.
@ -115,12 +116,6 @@ class PagerPageHolder(
*/ */
private var readImageHeaderSubscription: Subscription? = null private var readImageHeaderSubscription: Subscription? = null
/**
* Context that has been wrapped to use the correct theme values based on the
* current app theme and reader background color
*/
private val readerThemedContext = context.createReaderThemeContext(viewer.config.theme)
val stateChangedListener = object : SubsamplingScaleImageView.OnStateChangedListener { val stateChangedListener = object : SubsamplingScaleImageView.OnStateChangedListener {
override fun onScaleChanged(newScale: Float, origin: Int) { override fun onScaleChanged(newScale: Float, origin: Int) {
viewer.activity.hideMenu() viewer.activity.hideMenu()
@ -596,7 +591,7 @@ class PagerPageHolder(
private fun initRetryButton(): PagerButton { private fun initRetryButton(): PagerButton {
if (retryButton != null) return retryButton!! if (retryButton != null) return retryButton!!
retryButton = PagerButton(readerThemedContext, viewer).apply { retryButton = PagerButton(context, viewer).apply {
layoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply { layoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
gravity = Gravity.CENTER gravity = Gravity.CENTER
} }
@ -623,7 +618,7 @@ class PagerPageHolder(
} }
decodeErrorLayout = decodeLayout decodeErrorLayout = decodeLayout
TextView(readerThemedContext).apply { TextView(context).apply {
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply { layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
setMargins(margins, margins, margins, margins) setMargins(margins, margins, margins, margins)
} }
@ -633,7 +628,7 @@ class PagerPageHolder(
decodeLayout.addView(this) decodeLayout.addView(this)
} }
PagerButton(readerThemedContext, viewer).apply { PagerButton(context, viewer).apply {
layoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply { layoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
setMargins(margins, margins, margins, margins) setMargins(margins, margins, margins, margins)
} }
@ -647,7 +642,7 @@ class PagerPageHolder(
val imageUrl = page.imageUrl val imageUrl = page.imageUrl
if (imageUrl.orEmpty().startsWith("http", true)) { if (imageUrl.orEmpty().startsWith("http", true)) {
PagerButton(readerThemedContext, viewer).apply { PagerButton(context, viewer).apply {
layoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply { layoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
setMargins(margins, margins, margins, margins) setMargins(margins, margins, margins, margins)
} }

View File

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.ui.reader.viewer.pager package eu.kanade.tachiyomi.ui.reader.viewer.pager
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context
import android.view.Gravity import android.view.Gravity
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -23,9 +24,10 @@ import rx.android.schedulers.AndroidSchedulers
*/ */
@SuppressLint("ViewConstructor") @SuppressLint("ViewConstructor")
class PagerTransitionHolder( class PagerTransitionHolder(
readerThemedContext: Context,
val viewer: PagerViewer, val viewer: PagerViewer,
val transition: ChapterTransition val transition: ChapterTransition
) : LinearLayout(viewer.activity), ViewPagerAdapter.PositionableView { ) : LinearLayout(readerThemedContext), ViewPagerAdapter.PositionableView {
/** /**
* Item that identifies this view. Needed by the adapter to not recreate views. * Item that identifies this view. Needed by the adapter to not recreate views.

View File

@ -90,7 +90,6 @@ abstract class PagerViewer(val activity: ReaderActivity) : BaseViewer {
pager.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) pager.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
pager.offscreenPageLimit = 1 pager.offscreenPageLimit = 1
pager.id = R.id.reader_pager pager.id = R.id.reader_pager
pager.adapter = adapter
pager.addOnPageChangeListener( pager.addOnPageChangeListener(
// SY --> // SY -->
pagerListener pagerListener
@ -344,6 +343,7 @@ abstract class PagerViewer(val activity: ReaderActivity) : BaseViewer {
*/ */
private fun refreshAdapter() { private fun refreshAdapter() {
val currentItem = pager.currentItem val currentItem = pager.currentItem
adapter.refresh()
pager.adapter = adapter pager.adapter = adapter
pager.setCurrentItem(currentItem, false) pager.setCurrentItem(currentItem, false)
} }

View File

@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters
import eu.kanade.tachiyomi.ui.reader.viewer.hasMissingChapters import eu.kanade.tachiyomi.ui.reader.viewer.hasMissingChapters
import eu.kanade.tachiyomi.util.system.createReaderThemeContext
import eu.kanade.tachiyomi.widget.ViewPagerAdapter import eu.kanade.tachiyomi.widget.ViewPagerAdapter
import timber.log.Timber import timber.log.Timber
import kotlin.math.max import kotlin.math.max
@ -47,6 +48,12 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
private var doubledUp = viewer.config.doublePages private var doubledUp = viewer.config.doublePages
// SY <-- // SY <--
/**
* Context that has been wrapped to use the correct theme values based on the
* current app theme and reader background color
*/
private var readerThemedContext = viewer.activity.createReaderThemeContext(viewer.config.theme)
/** /**
* Updates this adapter with the given [chapters]. It handles setting a few pages of the * Updates this adapter with the given [chapters]. It handles setting a few pages of the
* next/previous chapter to allow seamless transitions and inverting the pages if the viewer * next/previous chapter to allow seamless transitions and inverting the pages if the viewer
@ -152,8 +159,8 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
val item = joinedItems[position].first val item = joinedItems[position].first
val item2 = joinedItems[position].second val item2 = joinedItems[position].second
return when (item) { return when (item) {
is ReaderPage -> PagerPageHolder(viewer, item, item2 as? ReaderPage) is ReaderPage -> PagerPageHolder(readerThemedContext, viewer, item, item2 as? ReaderPage)
is ChapterTransition -> PagerTransitionHolder(viewer, item) is ChapterTransition -> PagerTransitionHolder(readerThemedContext, viewer, item)
else -> throw NotImplementedError("Holder for ${item.javaClass} not implemented") else -> throw NotImplementedError("Holder for ${item.javaClass} not implemented")
} }
} }
@ -211,8 +218,11 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
notifyDataSetChanged() notifyDataSetChanged()
} }
// SY --> fun refresh() {
readerThemedContext = viewer.activity.createReaderThemeContext(viewer.config.theme)
}
// SY -->
private fun setJoinedItems(useSecondPage: Boolean = false) { private fun setJoinedItems(useSecondPage: Boolean = false) {
val oldCurrent = joinedItems.getOrNull(viewer.pager.currentItem) val oldCurrent = joinedItems.getOrNull(viewer.pager.currentItem)
if (!viewer.config.doublePages) { if (!viewer.config.doublePages) {

View File

@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters
import eu.kanade.tachiyomi.ui.reader.viewer.hasMissingChapters import eu.kanade.tachiyomi.ui.reader.viewer.hasMissingChapters
import eu.kanade.tachiyomi.util.system.createReaderThemeContext
/** /**
* RecyclerView Adapter used by this [viewer] to where [ViewerChapters] updates are posted. * RecyclerView Adapter used by this [viewer] to where [ViewerChapters] updates are posted.
@ -24,6 +25,12 @@ class WebtoonAdapter(val viewer: WebtoonViewer) : RecyclerView.Adapter<RecyclerV
var currentChapter: ReaderChapter? = null var currentChapter: ReaderChapter? = null
/**
* Context that has been wrapped to use the correct theme values based on the
* current app theme and reader background color
*/
private var readerThemedContext = viewer.activity.createReaderThemeContext(viewer.config.theme)
/** /**
* Updates this adapter with the given [chapters]. It handles setting a few pages of the * Updates this adapter with the given [chapters]. It handles setting a few pages of the
* next/previous chapter to allow seamless transitions. * next/previous chapter to allow seamless transitions.
@ -77,6 +84,10 @@ class WebtoonAdapter(val viewer: WebtoonViewer) : RecyclerView.Adapter<RecyclerV
result.dispatchUpdatesTo(this) result.dispatchUpdatesTo(this)
} }
fun refresh() {
readerThemedContext = viewer.activity.createReaderThemeContext(viewer.config.theme)
}
/** /**
* Returns the amount of items of the adapter. * Returns the amount of items of the adapter.
*/ */
@ -101,11 +112,11 @@ class WebtoonAdapter(val viewer: WebtoonViewer) : RecyclerView.Adapter<RecyclerV
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) { return when (viewType) {
PAGE_VIEW -> { PAGE_VIEW -> {
val view = FrameLayout(parent.context) val view = FrameLayout(readerThemedContext)
WebtoonPageHolder(view, viewer) WebtoonPageHolder(view, viewer)
} }
TRANSITION_VIEW -> { TRANSITION_VIEW -> {
val view = LinearLayout(parent.context) val view = LinearLayout(readerThemedContext)
WebtoonTransitionHolder(view, viewer) WebtoonTransitionHolder(view, viewer)
} }
else -> error("Unknown view type") else -> error("Unknown view type")

View File

@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.ui.reader.viewer.navigation.KindlishNavigation
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.LNavigation import eu.kanade.tachiyomi.ui.reader.viewer.navigation.LNavigation
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.RightAndLeftNavigation import eu.kanade.tachiyomi.ui.reader.viewer.navigation.RightAndLeftNavigation
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@ -22,14 +23,15 @@ class WebtoonConfig(
preferences: PreferencesHelper = Injekt.get() preferences: PreferencesHelper = Injekt.get()
) : ViewerConfig(preferences, scope) { ) : ViewerConfig(preferences, scope) {
var themeChangedListener: (() -> Unit)? = null
var imageCropBorders = false var imageCropBorders = false
private set private set
var sidePadding = 0 var sidePadding = 0
private set private set
var theme = preferences.readerTheme().get() val theme = preferences.readerTheme().get()
private set
// SY --> // SY -->
var usePageTransitions = false var usePageTransitions = false
@ -66,8 +68,11 @@ class WebtoonConfig(
preferences.dualPageInvertWebtoon() preferences.dualPageInvertWebtoon()
.register({ dualPageInvert = it }, { imagePropertyChangedListener?.invoke() }) .register({ dualPageInvert = it }, { imagePropertyChangedListener?.invoke() })
preferences.readerTheme() preferences.readerTheme().asFlow()
.register({ theme = it }, { imagePropertyChangedListener?.invoke() }) .drop(1)
.distinctUntilChanged()
.onEach { themeChangedListener?.invoke() }
.launchIn(scope)
// SY --> // SY -->
preferences.webtoonEnableZoomOut() preferences.webtoonEnableZoomOut()

View File

@ -27,7 +27,6 @@ import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressIndicator import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressIndicator
import eu.kanade.tachiyomi.ui.webview.WebViewActivity import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import eu.kanade.tachiyomi.util.system.ImageUtil import eu.kanade.tachiyomi.util.system.ImageUtil
import eu.kanade.tachiyomi.util.system.createReaderThemeContext
import eu.kanade.tachiyomi.util.system.dpToPx import eu.kanade.tachiyomi.util.system.dpToPx
import rx.Observable import rx.Observable
import rx.Subscription import rx.Subscription
@ -346,8 +345,7 @@ class WebtoonPageHolder(
progressContainer = FrameLayout(context) progressContainer = FrameLayout(context)
frame.addView(progressContainer, MATCH_PARENT, parentHeight) frame.addView(progressContainer, MATCH_PARENT, parentHeight)
val indicatorContext = context.createReaderThemeContext(viewer.config.theme) val progress = ReaderProgressIndicator(context).apply {
val progress = ReaderProgressIndicator(indicatorContext).apply {
updateLayoutParams<FrameLayout.LayoutParams> { updateLayoutParams<FrameLayout.LayoutParams> {
gravity = Gravity.CENTER_HORIZONTAL gravity = Gravity.CENTER_HORIZONTAL
updateMargins(top = parentHeight / 4) updateMargins(top = parentHeight / 4)

View File

@ -48,6 +48,11 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr
*/ */
private val layoutManager = WebtoonLayoutManager(activity) private val layoutManager = WebtoonLayoutManager(activity)
/**
* Configuration used by this viewer, like allow taps, or crop image borders.
*/
val config = WebtoonConfig(scope)
/** /**
* Adapter of the recycler view. * Adapter of the recycler view.
*/ */
@ -63,11 +68,6 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr
*/ */
/* [EXH] private */ var currentPage: Any? = null /* [EXH] private */ var currentPage: Any? = null
/**
* Configuration used by this viewer, like allow taps, or crop image borders.
*/
val config = WebtoonConfig(scope)
/** /**
* Subscriptions to keep while this viewer is used. * Subscriptions to keep while this viewer is used.
*/ */
@ -138,6 +138,10 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr
refreshAdapter() refreshAdapter()
} }
config.themeChangedListener = {
activity.recreate()
}
config.navigationModeChangedListener = { config.navigationModeChangedListener = {
val showOnStart = config.navigationOverlayOnStart || config.forceNavigationOverlay val showOnStart = config.navigationOverlayOnStart || config.forceNavigationOverlay
activity.binding.navigationOverlay.setNavigation(config.navigator, config.tappingEnabled, showOnStart) activity.binding.navigationOverlay.setNavigation(config.navigator, config.tappingEnabled, showOnStart)
@ -365,6 +369,7 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr
*/ */
private fun refreshAdapter() { private fun refreshAdapter() {
val position = layoutManager.findLastEndVisibleItemPosition() val position = layoutManager.findLastEndVisibleItemPosition()
adapter.refresh()
adapter.notifyItemRangeChanged( adapter.notifyItemRangeChanged(
max(0, position - 3), max(0, position - 3),
min(position + 3, adapter.itemCount - 1) min(position + 3, adapter.itemCount - 1)

View File

@ -310,7 +310,7 @@ fun Context.isNightMode(): Boolean {
fun Context.createReaderThemeContext(readerThemeSelected: Int): Context { fun Context.createReaderThemeContext(readerThemeSelected: Int): Context {
val isDarkBackground = when (readerThemeSelected) { val isDarkBackground = when (readerThemeSelected) {
1, 2 -> true // Black, Gray 1, 2 -> true // Black, Gray
3 -> isNightMode() // Automatic bg uses activity background by default 3 -> applicationContext.isNightMode() // Automatic bg uses activity background by default
else -> false // White else -> false // White
} }
val expected = if (isDarkBackground) Configuration.UI_MODE_NIGHT_YES else Configuration.UI_MODE_NIGHT_NO val expected = if (isDarkBackground) Configuration.UI_MODE_NIGHT_YES else Configuration.UI_MODE_NIGHT_NO

View File

@ -12,7 +12,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="16dp" android:layout_marginBottom="16dp"
android:textSize="17.5sp" android:textAppearance="?attr/textAppearanceSubtitle1"
tools:text="Top" /> tools:text="Top" />
<LinearLayout <LinearLayout
@ -35,6 +35,7 @@
android:id="@+id/warning_text" android:id="@+id/warning_text"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAppearance="?attr/textAppearanceSubtitle1"
tools:text="Warning" /> tools:text="Warning" />
</LinearLayout> </LinearLayout>
@ -43,7 +44,7 @@
android:id="@+id/lower_text" android:id="@+id/lower_text"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="17.5sp" android:textAppearance="?attr/textAppearanceSubtitle1"
tools:text="Bottom" /> tools:text="Bottom" />
</LinearLayout> </LinearLayout>