relayout of train view

This commit is contained in:
2026-10-02 11:02:36 +08:00
parent 55fad2c0cd
commit 1a5a756e82
8 changed files with 129 additions and 267 deletions

View File

@@ -13,7 +13,6 @@ internal const val ASSESSMENT_API_KEY =
internal const val SSO_LOGIN_URL = "https://d1kt.cn/login" internal const val SSO_LOGIN_URL = "https://d1kt.cn/login"
internal const val PICK_VIDEO_REQUEST = 1001
internal const val RECORD_AUDIO_REQUEST = 1002 internal const val RECORD_AUDIO_REQUEST = 1002
internal const val PROGRESS_MAX = 1000 internal const val PROGRESS_MAX = 1000

View File

@@ -167,14 +167,6 @@ internal class CourseCatalogController(
} }
} }
fun useCourseInPlace(video: TrainingVideoSummary) {
// Keep the current module (e.g. Train) while syncing course selection.
if (screenState.courseEnrollment[video.videoHash] != true) {
enrollCourse(video)
}
loadRemoteVideo(video)
}
fun loadRemoteVideo(video: TrainingVideoSummary) { fun loadRemoteVideo(video: TrainingVideoSummary) {
screenState.currentCourse = video screenState.currentCourse = video
screenState.activeItemId = video.videoHash screenState.activeItemId = video.videoHash

View File

@@ -7,7 +7,6 @@ import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Configuration import android.content.res.Configuration
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import cn.learningpad.oraltrainer.sdk.AuthSession import cn.learningpad.oraltrainer.sdk.AuthSession
import cn.learningpad.oraltrainer.sdk.GestureEvent import cn.learningpad.oraltrainer.sdk.GestureEvent
@@ -17,8 +16,6 @@ import cn.learningpad.oraltrainer.sdk.OralTrainerListener
import cn.learningpad.oraltrainer.sdk.OralTrainerSdk import cn.learningpad.oraltrainer.sdk.OralTrainerSdk
import cn.learningpad.oraltrainer.sdk.PlaybackSnapshot import cn.learningpad.oraltrainer.sdk.PlaybackSnapshot
import cn.learningpad.oraltrainer.sdk.SentenceBoundary import cn.learningpad.oraltrainer.sdk.SentenceBoundary
import cn.learningpad.oraltrainer.sdk.SentenceBoundaryApiCallback
import cn.learningpad.oraltrainer.sdk.SentenceBoundaryApiResult
import cn.learningpad.oraltrainer.sdk.TrainingMediaItem import cn.learningpad.oraltrainer.sdk.TrainingMediaItem
import cn.learningpad.oraltrainer.sdk.TrainingVideoSummary import cn.learningpad.oraltrainer.sdk.TrainingVideoSummary
@@ -173,53 +170,6 @@ class MainActivity : Activity() {
super.onBackPressed() super.onBackPressed()
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode != PICK_VIDEO_REQUEST || resultCode != RESULT_OK) return
val uri = data?.data ?: return
val persistableFlags = data.flags and Intent.FLAG_GRANT_READ_URI_PERMISSION
if (persistableFlags != 0) {
runCatching { contentResolver.takePersistableUriPermission(uri, persistableFlags) }
}
val item = TrainingMediaItem(
id = uri.toString(),
title = uri.lastPathSegment ?: "本地视频",
uri = uri,
)
screenState.activeItemId = item.id
screenState.currentSentenceCount = 0
controller.loadItem(item)
if (bindings.hasPlayerChrome) {
bindings.lessonTitleText.text = item.title
bindings.sentenceMetaText.text = "句子边界分析中"
bindings.sentenceText.text = "正在匹配服务端句子边界"
bindings.statusText.text = "正在获取本地视频的句子边界..."
}
screenFactory.renderCatalog()
sdk.sentenceBoundaryApi.fetchForUri(uri, contentResolver, object : SentenceBoundaryApiCallback {
override fun onSuccess(result: SentenceBoundaryApiResult) {
if (screenState.activeItemId != item.id) return
screenState.currentSentenceCount = result.sentences.size
controller.loadItem(item.copy(sentences = result.sentences))
if (bindings.hasPlayerChrome) {
bindings.statusText.text = "句子边界已加载:${result.sentences.size} 句"
}
}
override fun onError(error: Throwable) {
if (screenState.activeItemId != item.id) return
if (bindings.hasPlayerChrome) {
bindings.statusText.text =
"句子边界获取失败,使用默认快退/快进:${error.message.orEmpty()}"
bindings.sentenceMetaText.text = "暂无句子边界"
bindings.sentenceText.text = ""
}
}
})
}
override fun onDestroy() { override fun onDestroy() {
authCoordinator.reset() authCoordinator.reset()
testSession.release() testSession.release()
@@ -268,19 +218,6 @@ class MainActivity : Activity() {
setRootView() setRootView()
} }
private fun openLocalVideoPicker() {
@Suppress("DEPRECATION")
startActivityForResult(
Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "video/*"
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
},
PICK_VIDEO_REQUEST,
)
}
private fun switchModule(module: Module) { private fun switchModule(module: Module) {
if (module != Module.TEST) { if (module != Module.TEST) {
testSession.stopDubbingIfNeeded() testSession.stopDubbingIfNeeded()
@@ -393,10 +330,6 @@ class MainActivity : Activity() {
private fun screenActions() = object : SampleScreenActions { private fun screenActions() = object : SampleScreenActions {
override fun onModuleSelected(module: Module) = switchModule(module) override fun onModuleSelected(module: Module) = switchModule(module)
override fun onImportLocalVideo() = openLocalVideoPicker()
override fun onRefreshCatalog() = catalogController.loadCatalog()
override fun onSeekProgressPreview(progress: Int) { override fun onSeekProgressPreview(progress: Int) {
if (!bindings.hasPlayerChrome) return if (!bindings.hasPlayerChrome) return
val duration = controller.snapshot().durationMs val duration = controller.snapshot().durationMs
@@ -474,10 +407,6 @@ class MainActivity : Activity() {
override fun onRefreshCourses() = catalogController.refreshCourses() override fun onRefreshCourses() = catalogController.refreshCourses()
override fun onTrainCatalogVideoClick(video: TrainingVideoSummary) {
catalogController.useCourseInPlace(video)
}
override fun onLogout() = logout() override fun onLogout() = logout()
override fun nickname(): String = override fun nickname(): String =

View File

@@ -17,13 +17,10 @@ internal interface SentenceExplainActions {
internal interface TrainScreenActions : internal interface TrainScreenActions :
SentenceExplainActions { SentenceExplainActions {
fun onImportLocalVideo()
fun onRefreshCatalog()
fun onSeekProgressPreview(progress: Int) fun onSeekProgressPreview(progress: Int)
fun onSeekStart() fun onSeekStart()
fun onSeekStop(progress: Int) fun onSeekStop(progress: Int)
fun onContinuousPlaybackChanged(enabled: Boolean) fun onContinuousPlaybackChanged(enabled: Boolean)
fun onTrainCatalogVideoClick(video: TrainingVideoSummary)
} }
internal interface TestScreenActions : internal interface TestScreenActions :

View File

@@ -33,11 +33,7 @@ internal class SampleScreenFactory(
private val courseScreen = CourseScreen(activity, bindings, screenState, widgets, actions) private val courseScreen = CourseScreen(activity, bindings, screenState, widgets, actions)
private val trainScreen = TrainScreen( private val trainScreen = TrainScreen(
activity = activity, activity = activity,
bindings = bindings,
screenState = screenState,
widgets = widgets,
playerChrome = playerChrome, playerChrome = playerChrome,
actions = actions,
) )
private val testScreen = TestScreen(activity, bindings, screenState, playerChrome, actions) private val testScreen = TestScreen(activity, bindings, screenState, playerChrome, actions)
private val mineScreen = MineScreen(activity, bindings, screenState, widgets, actions) private val mineScreen = MineScreen(activity, bindings, screenState, widgets, actions)
@@ -53,6 +49,10 @@ internal class SampleScreenFactory(
return LinearLayout(activity).apply { return LinearLayout(activity).apply {
orientation = LinearLayout.VERTICAL orientation = LinearLayout.VERTICAL
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT,
)
setBackgroundColor(SampleTheme.background) setBackgroundColor(SampleTheme.background)
addView(widgets.moduleTabBar(actions)) addView(widgets.moduleTabBar(actions))
when (screenState.activeModule) { when (screenState.activeModule) {
@@ -65,24 +65,19 @@ internal class SampleScreenFactory(
} }
fun renderCatalog() { fun renderCatalog() {
when (screenState.activeModule) { if (screenState.activeModule == Module.COURSE) {
Module.COURSE -> courseScreen.renderCatalog() courseScreen.renderCatalog()
Module.TRAIN -> trainScreen.renderCatalogStrip()
else -> Unit
} }
} }
fun showCatalogLoading() { fun showCatalogLoading() {
when (screenState.activeModule) { if (screenState.activeModule == Module.COURSE) {
Module.COURSE -> courseScreen.showLoading() courseScreen.showLoading()
Module.TRAIN -> trainScreen.showCatalogLoading()
else -> Unit
} }
} }
fun setCatalogStatus(text: String) { fun setCatalogStatus(text: String) {
courseScreen.setStatus(text) courseScreen.setStatus(text)
trainScreen.setCatalogStatus(text)
} }
fun refreshMyContent(userApi: UserApi, token: String) { fun refreshMyContent(userApi: UserApi, token: String) {

View File

@@ -4,7 +4,10 @@ import android.app.Activity
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.InsetDrawable
import android.graphics.Typeface import android.graphics.Typeface
import android.text.TextUtils
import android.view.Gravity import android.view.Gravity
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@@ -14,6 +17,7 @@ import android.widget.FrameLayout
import android.widget.ImageButton import android.widget.ImageButton
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.SeekBar import android.widget.SeekBar
import android.widget.Switch import android.widget.Switch
import android.widget.TextView import android.widget.TextView
@@ -40,26 +44,50 @@ internal class PlayerChrome(
override val uiContext get() = activity override val uiContext get() = activity
fun createPlayerSection(): View = fun createPlayerSection(): View =
FrameLayout(activity).apply { LinearLayout(activity).apply {
orientation = LinearLayout.VERTICAL
background = rounded(SampleTheme.surface, 8f, SampleTheme.border) background = rounded(SampleTheme.surface, 8f, SampleTheme.border)
clipToOutline = true clipToOutline = true
layoutParams = LinearLayout.LayoutParams( layoutParams = LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
0, ViewGroup.LayoutParams.WRAP_CONTENT,
1f,
).withMargins(16.dp, 0, 16.dp, 10.dp) ).withMargins(16.dp, 0, 16.dp, 10.dp)
addView(createPlayerView())
addView(createPlayerOverlay())
addView( addView(
createRotationButton().apply { createPlayerOverlay().apply {
layoutParams = FrameLayout.LayoutParams( layoutParams = LinearLayout.LayoutParams(
44.dp, ViewGroup.LayoutParams.MATCH_PARENT,
44.dp, ViewGroup.LayoutParams.WRAP_CONTENT,
Gravity.BOTTOM or Gravity.END, )
).apply { },
marginEnd = 12.dp )
bottomMargin = 12.dp addView(
object : FrameLayout(activity) {
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val width = MeasureSpec.getSize(widthMeasureSpec)
val height = (width * 9) / 16
super.onMeasure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY),
)
} }
}.apply {
layoutParams = LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
)
addView(createPlayerView())
addView(
createRotationButton().apply {
layoutParams = FrameLayout.LayoutParams(
44.dp,
44.dp,
Gravity.BOTTOM or Gravity.END,
).apply {
marginEnd = 12.dp
bottomMargin = 12.dp
}
},
)
}, },
) )
} }
@@ -70,7 +98,8 @@ internal class PlayerChrome(
setPadding(18.dp, 16.dp, 18.dp, 16.dp) setPadding(18.dp, 16.dp, 18.dp, 16.dp)
layoutParams = LinearLayout.LayoutParams( layoutParams = LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT, 0,
1f,
).withMargins(16.dp, 0, 16.dp, 12.dp) ).withMargins(16.dp, 0, 16.dp, 12.dp)
} }
@@ -78,7 +107,15 @@ internal class PlayerChrome(
FrameLayout(activity).apply { FrameLayout(activity).apply {
setBackgroundColor(Color.BLACK) setBackgroundColor(Color.BLACK)
addView(createPlayerView()) addView(createPlayerView())
addView(createPlayerOverlay()) addView(
createPlayerOverlay().apply {
layoutParams = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
Gravity.TOP,
)
},
)
val subtitleContainer = LinearLayout(activity).apply { val subtitleContainer = LinearLayout(activity).apply {
orientation = LinearLayout.VERTICAL orientation = LinearLayout.VERTICAL
layoutParams = FrameLayout.LayoutParams( layoutParams = FrameLayout.LayoutParams(
@@ -106,6 +143,7 @@ internal class PlayerChrome(
textSize = 17f textSize = 17f
typeface = Typeface.DEFAULT_BOLD typeface = Typeface.DEFAULT_BOLD
maxLines = 1 maxLines = 1
ellipsize = TextUtils.TruncateAt.END
} }
bindings.sentenceMetaText = TextView(activity).apply { bindings.sentenceMetaText = TextView(activity).apply {
setTextColor(if (translucent) SampleTheme.accentLight else SampleTheme.accentDeep) setTextColor(if (translucent) SampleTheme.accentLight else SampleTheme.accentDeep)
@@ -157,28 +195,50 @@ internal class PlayerChrome(
background = rounded(SampleTheme.overlayFaint, 0f, null) background = rounded(SampleTheme.overlayFaint, 0f, null)
setPadding(18.dp, 14.dp, 18.dp, 16.dp) setPadding(18.dp, 14.dp, 18.dp, 16.dp)
} }
addView(bindings.lessonTitleText) val topContent = LinearLayout(activity).apply {
if (screenState.activeModule == Module.TRAIN) { orientation = LinearLayout.VERTICAL
addView(createContinuousPlaybackRow(translucent)) addView(bindings.lessonTitleText)
if (screenState.activeModule == Module.TRAIN) {
addView(createContinuousPlaybackRow(translucent))
}
addView(bindings.sentenceMetaText)
addView(LinearLayout(activity).apply {
orientation = LinearLayout.HORIZONTAL
gravity = Gravity.TOP
addView(
bindings.sentenceText,
LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f,
),
)
bindings.explainSentenceButton = createExplainButton(translucent)
addView(
bindings.explainSentenceButton,
LinearLayout.LayoutParams(32.dp, 32.dp).apply { marginStart = 10.dp },
)
})
} }
addView(bindings.sentenceMetaText) addView(
addView(LinearLayout(activity).apply { ScrollView(activity).apply {
orientation = LinearLayout.HORIZONTAL isFillViewport = true
gravity = Gravity.CENTER_VERTICAL isVerticalScrollBarEnabled = false
addView( overScrollMode = View.OVER_SCROLL_NEVER
bindings.sentenceText, addView(
LinearLayout.LayoutParams( topContent,
0, LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT,
1f, ViewGroup.LayoutParams.WRAP_CONTENT,
), ),
) )
bindings.explainSentenceButton = createExplainButton(translucent) },
addView( LinearLayout.LayoutParams(
bindings.explainSentenceButton, ViewGroup.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams(32.dp, 32.dp).apply { marginStart = 10.dp }, if (translucent) ViewGroup.LayoutParams.WRAP_CONTENT else 0,
) if (translucent) 0f else 1f,
}) ),
)
addView(bindings.progressBar) addView(bindings.progressBar)
addView(bindings.statusText) addView(bindings.statusText)
} }
@@ -197,12 +257,7 @@ internal class PlayerChrome(
val overlay = LinearLayout(activity).apply { val overlay = LinearLayout(activity).apply {
orientation = LinearLayout.HORIZONTAL orientation = LinearLayout.HORIZONTAL
gravity = Gravity.CENTER_VERTICAL gravity = Gravity.CENTER_VERTICAL
setPadding(12.dp, 12.dp, 12.dp, 12.dp) setPadding(12.dp, 10.dp, 12.dp, 8.dp)
layoutParams = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
Gravity.TOP,
)
} }
bindings.timeText = widgets.overlayPill("--:-- / --:--").apply { bindings.timeText = widgets.overlayPill("--:-- / --:--").apply {
layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f) layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)
@@ -217,11 +272,27 @@ internal class PlayerChrome(
ImageButton(activity).apply { ImageButton(activity).apply {
setImageResource(R.drawable.ic_screen_rotation) setImageResource(R.drawable.ic_screen_rotation)
imageTintList = ColorStateList.valueOf(Color.WHITE) imageTintList = ColorStateList.valueOf(Color.WHITE)
background = rounded(Color.argb(178, 9, 12, 16), 22.dp.toFloat()) val backgroundSize = 22.dp
val buttonSize = 44.dp
val inset = (buttonSize - backgroundSize) / 2
background = InsetDrawable(
GradientDrawable().apply {
shape = GradientDrawable.OVAL
setColor(Color.argb(102, 9, 12, 16))
setSize(backgroundSize, backgroundSize)
},
inset,
)
scaleType = ImageView.ScaleType.CENTER_INSIDE scaleType = ImageView.ScaleType.CENTER_INSIDE
setPadding(10.dp, 10.dp, 10.dp, 10.dp) setPadding(10.dp, 10.dp, 10.dp, 10.dp)
contentDescription = "横屏" contentDescription = "横屏"
layoutParams = LinearLayout.LayoutParams(44.dp, 44.dp) minimumWidth = 0
minimumHeight = 0
clipToOutline = false
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
foreground = null
}
layoutParams = LinearLayout.LayoutParams(buttonSize, buttonSize)
setOnClickListener { setOnClickListener {
activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
} }

View File

@@ -153,40 +153,4 @@ internal class SampleWidgets(
} }
} }
fun videoCard(
id: String,
title: String,
meta: String,
onClick: () -> Unit,
): View {
val selected = screenState.activeItemId == id
val titleColor = if (selected) SampleTheme.textDark else Color.WHITE
val metaColor = if (selected) SampleTheme.accentDeep else SampleTheme.textMuted
return LinearLayout(activity).apply {
orientation = LinearLayout.VERTICAL
gravity = Gravity.CENTER_VERTICAL
isClickable = true
background = if (selected) {
rounded(SampleTheme.selected, 8f, SampleTheme.accent)
} else {
rounded(SampleTheme.surface, 8f, SampleTheme.border)
}
setPadding(14.dp, 12.dp, 14.dp, 12.dp)
layoutParams = LinearLayout.LayoutParams(218.dp, 84.dp).withMargins(0, 0, 10.dp, 0)
setOnClickListener { onClick() }
addView(TextView(activity).apply {
text = title
setTextColor(titleColor)
textSize = 15f
typeface = Typeface.DEFAULT_BOLD
maxLines = 2
})
addView(TextView(activity).apply {
text = meta
setTextColor(metaColor)
textSize = 12f
setPadding(0, 6.dp, 0, 0)
})
}
}
} }

View File

@@ -5,23 +5,14 @@ import android.graphics.Color
import android.graphics.Typeface import android.graphics.Typeface
import android.view.Gravity import android.view.Gravity
import android.view.View import android.view.View
import android.view.ViewGroup
import android.widget.HorizontalScrollView
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import cn.learningpad.oraltrainer.sample.SampleBindings
import cn.learningpad.oraltrainer.sample.SampleTheme import cn.learningpad.oraltrainer.sample.SampleTheme
import cn.learningpad.oraltrainer.sample.TrainScreenActions
import cn.learningpad.oraltrainer.sample.TrainingScreenState
import cn.learningpad.oraltrainer.sample.UiStyled import cn.learningpad.oraltrainer.sample.UiStyled
internal class TrainScreen( internal class TrainScreen(
private val activity: Activity, private val activity: Activity,
private val bindings: SampleBindings,
private val screenState: TrainingScreenState,
private val widgets: SampleWidgets,
private val playerChrome: PlayerChrome, private val playerChrome: PlayerChrome,
private val actions: TrainScreenActions,
) : UiStyled { ) : UiStyled {
override val uiContext get() = activity override val uiContext get() = activity
@@ -29,47 +20,13 @@ internal class TrainScreen(
root.addView(createHeader()) root.addView(createHeader())
root.addView(playerChrome.createPlayerSection()) root.addView(playerChrome.createPlayerSection())
root.addView(playerChrome.createSentenceSection()) root.addView(playerChrome.createSentenceSection())
root.addView(createCatalogSection())
}
fun renderCatalogStrip() {
if (!bindings.hasCatalogChrome) return
bindings.catalogList.removeAllViews()
bindings.catalogStatusText.text = screenState.catalogStatus
screenState.catalogVideos.forEach { video ->
val enrolled = screenState.courseEnrollment[video.videoHash] == true
bindings.catalogList.addView(
widgets.videoCard(
id = video.videoHash,
title = video.title,
meta = if (enrolled) {
"已选修 · ${video.sentenceCount} 句"
} else {
"未选修 · 点击选修并进入"
},
onClick = { actions.onTrainCatalogVideoClick(video) },
),
)
}
}
fun showCatalogLoading() {
if (!bindings.hasCatalogChrome) return
bindings.catalogList.removeAllViews()
bindings.catalogList.addView(widgets.infoCard("课程加载中..."))
}
fun setCatalogStatus(text: String) {
if (bindings.hasCatalogChrome) {
bindings.catalogStatusText.text = text
}
} }
private fun createHeader(): View { private fun createHeader(): View {
val titleBlock = LinearLayout(activity).apply { return LinearLayout(activity).apply {
orientation = LinearLayout.VERTICAL orientation = LinearLayout.HORIZONTAL
gravity = Gravity.CENTER_VERTICAL gravity = Gravity.CENTER_VERTICAL
layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f) setPadding(18.dp, 16.dp, 18.dp, 12.dp)
addView(TextView(activity).apply { addView(TextView(activity).apply {
text = "八哥口语" text = "八哥口语"
setTextColor(Color.WHITE) setTextColor(Color.WHITE)
@@ -80,52 +37,10 @@ internal class TrainScreen(
addView(TextView(activity).apply { addView(TextView(activity).apply {
text = "外语跟读训练神器" text = "外语跟读训练神器"
setTextColor(SampleTheme.textMuted) setTextColor(SampleTheme.textMuted)
textSize = 20f textSize = 18f
setPadding(1.dp, 4.dp, 0, 0) includeFontPadding = false
setPadding(12.dp, 6.dp, 0, 0)
}) })
} }
return LinearLayout(activity).apply {
orientation = LinearLayout.HORIZONTAL
gravity = Gravity.CENTER_VERTICAL
setPadding(18.dp, 16.dp, 18.dp, 12.dp)
addView(titleBlock)
addView(widgets.headerButton("导入") { actions.onImportLocalVideo() })
addView(widgets.headerButton("刷新") { actions.onRefreshCatalog() })
}
}
private fun createCatalogSection(): View {
bindings.catalogStatusText = TextView(activity).apply {
setTextColor(SampleTheme.textMuted)
textSize = 13f
gravity = Gravity.END
text = "正在同步"
layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)
}
bindings.catalogList = LinearLayout(activity).apply {
orientation = LinearLayout.HORIZONTAL
setPadding(0, 10.dp, 0, 4.dp)
}
val header = LinearLayout(activity).apply {
orientation = LinearLayout.HORIZONTAL
gravity = Gravity.CENTER_VERTICAL
addView(TextView(activity).apply {
text = "课程"
setTextColor(Color.WHITE)
textSize = 16f
typeface = Typeface.DEFAULT_BOLD
})
addView(bindings.catalogStatusText)
}
val scroller = HorizontalScrollView(activity).apply {
isHorizontalScrollBarEnabled = false
addView(bindings.catalogList)
}
return LinearLayout(activity).apply {
orientation = LinearLayout.VERTICAL
setPadding(16.dp, 0, 16.dp, 14.dp)
addView(header)
addView(scroller)
}
} }
} }