This commit is contained in:
litingting 2025-12-01 14:55:15 +08:00
commit 295b446eac
87 changed files with 2586 additions and 0 deletions

15
.gitignore vendored Normal file
View File

@ -0,0 +1,15 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties

1
app/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

BIN
app/LEDScroller Normal file

Binary file not shown.

60
app/build.gradle Normal file
View File

@ -0,0 +1,60 @@
import java.util.Date
import java.text.SimpleDateFormat
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
def timestamp = new Date().format("MM_dd_HH_mm")
android {
namespace 'com.led.scroller.barrage.banner'
compileSdk 36
defaultConfig {
applicationId "com.led.scroller.barrage.banner"
minSdk 24
targetSdk 36
versionCode 1
versionName "1.0"
setProperty(
"archivesBaseName",
"LED Scroller_V" + versionName + "(${versionCode})_$timestamp"
)
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary true
}
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
buildFeatures {
viewBinding true
buildConfig = true
}
packagingOptions {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.17.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.9.4'
implementation 'androidx.appcompat:appcompat:1.7.1'
implementation fileTree(dir: 'libs', include: ['*.jar','*.aar'])
implementation 'com.google.android.material:material:1.13.0'
implementation 'androidx.activity:activity-ktx:1.11.0'
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
}

21
app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1,24 @@
package com.led.scroller.barrage.banner
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.led.scroller.barrage.banner", appContext.packageName)
}
}

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<application
android:name=".LBApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/icon"
android:label="@string/app_name"
android:roundIcon="@mipmap/icon"
android:supportsRtl="true"
android:theme="@style/Theme.LEDBarrage"
tools:targetApi="31" >
<activity
android:name=".ui.MainActivity"
android:exported="true"
android:theme="@style/Theme.LEDBarrage" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ui.ShowBarrageActivity"
android:screenOrientation="landscape" />
</application>
</manifest>

View File

@ -0,0 +1,16 @@
package com.led.scroller.barrage.banner
import android.app.Application
class LBApplication : Application() {
companion object {
lateinit var app: LBApplication
}
override fun onCreate() {
super.onCreate()
app = this
}
}

View File

@ -0,0 +1,36 @@
package com.led.scroller.barrage.banner.app
import android.content.Context
import android.graphics.drawable.GradientDrawable
import androidx.core.content.ContextCompat
import com.led.scroller.barrage.banner.R
fun getShapeDrawable(
content: Context,
startColor: Int,
endColor: Int
): GradientDrawable {
// 创建圆角矩形形状
val shape = GradientDrawable()
shape.shape = GradientDrawable.RECTANGLE
// 设置形状的渐变颜色
shape.colors = intArrayOf(
ContextCompat.getColor(content, startColor),
ContextCompat.getColor(content, endColor)
)
// 设置渐变方向,这里设置为从上到下的方向
shape.orientation = GradientDrawable.Orientation.TOP_BOTTOM
// 设置圆角半径
val cornerRadius = content.resources.getDimension(R.dimen.corner_radius)
shape.cornerRadius = cornerRadius
return shape
}
fun dpToPx(dp: Float, context: Context): Float {
val scale = context.resources.displayMetrics.density
return dp * scale
}

View File

@ -0,0 +1,61 @@
package com.led.scroller.barrage.banner.app
import android.content.SharedPreferences
import androidx.core.content.edit
class SharedPreferenceProviderWallpaper(private val preferences: SharedPreferences) :
VariablesProvider {
override fun getInt(key: String, defaultValue: Int): Int {
return preferences.getInt(key, defaultValue)
}
override fun setInt(key: String, value: Int) {
preferences.edit {
putInt(key, value)
}
}
override fun getLong(key: String, defaultValue: Long): Long {
return preferences.getLong(key, defaultValue)
}
override fun setLong(key: String, value: Long) {
preferences.edit {
putLong(key, value)
}
}
override fun getString(key: String, defaultValue: String): String {
return preferences.getString(key, defaultValue)!!
}
override fun setString(key: String, value: String) {
preferences.edit {
putString(key, value)
}
}
override fun getStringSet(key: String, defaultValue: Set<String>): Set<String> {
return preferences.getStringSet(key, defaultValue)!!
}
override fun setStringSet(key: String, value: Set<String>) {
preferences.edit {
putStringSet(key, value)
}
}
override fun getBoolean(key: String, defaultValue: Boolean): Boolean {
return preferences.getBoolean(key, defaultValue)
}
override fun setBoolean(key: String, value: Boolean) {
preferences.edit {
putBoolean(key, value)
}
}
}
fun SharedPreferences.asStoreProvider(): VariablesProvider {
return SharedPreferenceProviderWallpaper(this)
}

View File

@ -0,0 +1,18 @@
package com.led.scroller.barrage.banner.app
interface VariablesProvider {
fun getInt(key: String, defaultValue: Int): Int
fun setInt(key: String, value: Int)
fun getLong(key: String, defaultValue: Long): Long
fun setLong(key: String, value: Long)
fun getString(key: String, defaultValue: String): String
fun setString(key: String, value: String)
fun getStringSet(key: String, defaultValue: Set<String>): Set<String>
fun setStringSet(key: String, value: Set<String>)
fun getBoolean(key: String, defaultValue: Boolean): Boolean
fun setBoolean(key: String, value: Boolean)
}

View File

@ -0,0 +1,8 @@
package com.led.scroller.barrage.banner.bean
import java.io.Serializable
class FontBean(
var id: Int,
var select: Boolean,
) : Serializable

View File

@ -0,0 +1,9 @@
package com.led.scroller.barrage.banner.bean
import java.io.Serializable
class FontColorBean(
val id: Int, // 颜色资源 ID
val colorString: String, // 颜色值
var select: Boolean,
) : Serializable

View File

@ -0,0 +1,9 @@
package com.led.scroller.barrage.banner.bean
import java.io.Serializable
class FontSizeBean(
var name: String,
var size: Int,
var select: Boolean,
) : Serializable

View File

@ -0,0 +1,41 @@
package com.led.scroller.barrage.banner.sp
import android.content.Context
import com.led.scroller.barrage.banner.R
import com.led.scroller.barrage.banner.app.asStoreProvider
class AppSP(context: Context) {
private val store = BaseSP(
context
.getSharedPreferences(context.packageName, Context.MODE_PRIVATE)
.asStoreProvider()
)
var fontResId: Int by store.int(
key = FONT_RES_ID,
defaultValue = R.font.font_medium
)
var fontColorResId: Int by store.int(
key = FONT_COLOR_RES_ID,
defaultValue = R.color.font_color_1
)
var fontSize: Int by store.int(
key = FONT_Size,
defaultValue = 80
)
var bgColorResId: Int by store.int(
key = BG_COLOR_RES_ID,
defaultValue = R.color.default_preview_bg_color
)
companion object {
const val FONT_RES_ID = "font_res_id"
const val FONT_COLOR_RES_ID = "font_color_res_id"
const val FONT_Size = "font_size"
const val BG_COLOR_RES_ID = "bg_color_res_id"
}
}

View File

@ -0,0 +1,99 @@
package com.led.scroller.barrage.banner.sp
import com.led.scroller.barrage.banner.app.VariablesProvider
import kotlin.reflect.KProperty
class BaseSP(val provider: VariablesProvider) {
interface Delegate<T> {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T)
}
fun int(key: String, defaultValue: Int): Delegate<Int> {
return object : Delegate<Int> {
override fun getValue(thisRef: Any?, property: KProperty<*>): Int {
return provider.getInt(key, defaultValue)
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
provider.setInt(key, value)
}
}
}
fun long(key: String, defaultValue: Long): Delegate<Long> {
return object : Delegate<Long> {
override fun getValue(thisRef: Any?, property: KProperty<*>): Long {
return provider.getLong(key, defaultValue)
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Long) {
provider.setLong(key, value)
}
}
}
fun string(key: String, defaultValue: String): Delegate<String> {
return object : Delegate<String> {
override fun getValue(thisRef: Any?, property: KProperty<*>): String {
return provider.getString(key, defaultValue)
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
provider.setString(key, value)
}
}
}
fun stringSet(key: String, defaultValue: Set<String>): Delegate<Set<String>> {
return object : Delegate<Set<String>> {
override fun getValue(thisRef: Any?, property: KProperty<*>): Set<String> {
return provider.getStringSet(key, defaultValue)
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Set<String>) {
provider.setStringSet(key, value)
}
}
}
fun boolean(key: String, defaultValue: Boolean): Delegate<Boolean> {
return object : Delegate<Boolean> {
override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean {
return provider.getBoolean(key, defaultValue)
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) {
provider.setBoolean(key, value)
}
}
}
fun <T : Enum<T>> enum(key: String, defaultValue: T, values: Array<T>): Delegate<T> {
return object : Delegate<T> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
val name = provider.getString(key, defaultValue.name)
return values.find { name == it.name } ?: defaultValue
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
provider.setString(key, value.name)
}
}
}
fun <T> typedString(key: String, from: (String) -> T?, to: (T?) -> String): Delegate<T?> {
return object : Delegate<T?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
val value = provider.getString(key, to(null))
return from(value)
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
provider.setString(key, to(value))
}
}
}
}

View File

@ -0,0 +1,93 @@
package com.led.scroller.barrage.banner.ui
import android.annotation.SuppressLint
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import com.led.scroller.barrage.banner.R
import com.led.scroller.barrage.banner.bean.FontColorBean
import com.led.scroller.barrage.banner.databinding.FragmentBackgroundBinding
import com.led.scroller.barrage.banner.databinding.FragmentTextBinding
import com.led.scroller.barrage.banner.sp.AppSP
import com.led.scroller.barrage.banner.ui.adapter.FontColorAdapter
class BackgroundFragment : Fragment() {
private lateinit var binding: FragmentBackgroundBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentBackgroundBinding.inflate(layoutInflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initView()
}
override fun onResume() {
super.onResume()
// 确保监听器在Fragment可见时重新设置
if (backgroundFragmentClickListener == null) {
Log.d("BackgroundFragment", "监听器为空,可能需要重新设置")
}
}
private fun initView() {
initBgColorAdapter()
}
private fun initBgColorAdapter() {
val sp = AppSP(requireActivity())
val bgColorId = sp.bgColorResId
val bgColorList: MutableList<FontColorBean> = mutableListOf()
// 获取颜色资源数组
val colorArray = resources.obtainTypedArray(R.array.BgColorArray)
// 遍历颜色资源数组
for (i in 0 until colorArray.length()) {
val colorResId = colorArray.getResourceId(i, 0)
val colorValue = ContextCompat.getColor(requireContext(), colorResId)
// 将颜色值转换为十六进制字符串
val hexColor = String.format("#%06X", 0xFFFFFF and colorValue)
val fontColor = FontColorBean(colorResId, hexColor, colorResId == bgColorId)
bgColorList.add(fontColor)
}
colorArray.recycle()
val fontColorAdapter = FontColorAdapter(requireActivity(), bgColorList)
fontColorAdapter.setOnItemClickListener(object : FontColorAdapter.OnItemClickListener {
@SuppressLint("NotifyDataSetChanged")
override fun onItemClick(position: Int) {
val bean = bgColorList[position]
for (fontColor in bgColorList) {
fontColor.select = false
}
bgColorList[position].select = true
AppSP(requireActivity()).bgColorResId = bean.id
fontColorAdapter.notifyDataSetChanged()
backgroundFragmentClickListener?.onBgColorClick()
}
})
binding.colorRv.layoutManager = GridLayoutManager(requireActivity(), 7)
binding.colorRv.adapter = fontColorAdapter
}
private var backgroundFragmentClickListener: BackgroundFragmentClickListener? = null
fun setBackgroundFragmentClickListener(listener: BackgroundFragmentClickListener) {
backgroundFragmentClickListener = listener
}
interface BackgroundFragmentClickListener {
fun onBgColorClick()
}
}

View File

@ -0,0 +1,18 @@
package com.led.scroller.barrage.banner.ui
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.led.scroller.barrage.banner.sp.AppSP
abstract class BaseActivity : AppCompatActivity() {
private val appSp by lazy { AppSP(this) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
fun showToast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
}

View File

@ -0,0 +1,267 @@
package com.led.scroller.barrage.banner.ui
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.app.ComponentCaller
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.util.TypedValue
import android.view.animation.LinearInterpolator
import androidx.activity.enableEdgeToEdge
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.Fragment
import com.led.scroller.barrage.banner.R
import com.led.scroller.barrage.banner.databinding.ActivityMainBinding
import com.led.scroller.barrage.banner.sp.AppSP
import com.led.scroller.barrage.banner.ui.adapter.TabFragmentPagerAdapter
class MainActivity : BaseActivity(), TextFragment.TextFragmentClickListener,
BackgroundFragment.BackgroundFragmentClickListener {
private var animSet: AnimatorSet? = null
private var animOne: ObjectAnimator? = null
private lateinit var binding: ActivityMainBinding
private var mFragments: MutableList<Fragment> = mutableListOf()
private var mStrings: MutableList<String> = mutableListOf()
private lateinit var tabFragmentPagerAdapter: TabFragmentPagerAdapter
private val textFragment = TextFragment()
private val backgroundFragment = BackgroundFragment()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
immersionBar()
initView()
initTab()
animatorRightToLeft()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
Log.d("MainActivity", "onSaveInstanceState")
}
private fun immersionBar() {
//自适应沉浸式
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView)
windowInsetsController.isAppearanceLightStatusBars = false//状态栏文字颜色
}
private fun initTab() {
mStrings.add(getString(R.string.text))
mStrings.add(getString(R.string.background))
mFragments.add(textFragment)
mFragments.add(backgroundFragment)
tabFragmentPagerAdapter =
TabFragmentPagerAdapter(supportFragmentManager, mFragments, mStrings)
binding.viewPager.adapter = tabFragmentPagerAdapter
binding.tabLayout.setupWithViewPager(binding.viewPager)
// 设置页面变化监听器确保Fragment在显示时正确设置监听器
binding.viewPager.addOnPageChangeListener(object :
androidx.viewpager.widget.ViewPager.OnPageChangeListener {
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int
) {
}
override fun onPageSelected(position: Int) {
Log.d("MainActivity", "页面被选中: $position")
// 当页面被选中时,确保监听器已设置
val fragment =
supportFragmentManager.findFragmentByTag("android:switcher:${binding.viewPager.id}:$position")
Log.d("MainActivity", "找到的Fragment: $fragment")
when (fragment) {
is TextFragment -> {
Log.d("MainActivity", "设置TextFragment监听器页面切换")
fragment.setTextFragmentClickListener(this@MainActivity)
}
is BackgroundFragment -> {
Log.d("MainActivity", "设置BackgroundFragment监听器页面切换")
fragment.setBackgroundFragmentClickListener(this@MainActivity)
}
}
}
override fun onPageScrollStateChanged(state: Int) {}
})
}
@SuppressLint("ClickableViewAccessibility")
private fun initView() {
//屏蔽事件
binding.horizontalScrollview.setOnTouchListener { v, _ ->
v.parent.requestDisallowInterceptTouchEvent(true)
true
}
binding.startBtn.setOnClickListener {
val content = binding.contentEdit.text.toString()
if (content.isNotEmpty()) {
val intent = Intent(this, ShowBarrageActivity::class.java)
intent.putExtra(ShowBarrageActivity.CONTENT_KEY, content)
startActivity(intent)
} else {
showToast(getString(R.string.type_something))
}
}
binding.contentEdit.addTextChangedListener {
updatePreviewText()
}
}
private var content = ""
private fun updatePreviewText() {
content = binding.contentEdit.text.toString()
val sp = AppSP(this)
binding.previewLayout.setBackgroundColor(getColor(sp.bgColorResId))
binding.previewText.text = content
binding.previewText.setTextColor(getColor(sp.fontColorResId))
binding.previewText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, sp.fontSize.toFloat())
val typeface = ResourcesCompat.getFont(this, sp.fontResId)
binding.previewText.typeface = typeface
}
private fun animatorRightToLeft() {
val textPaint = binding.previewText.paint
val textPaintWidth = textPaint.measureText(content) //获取文字的宽度。
val screenWidth = resources.displayMetrics.widthPixels //获取屏幕的宽度。
animSet = AnimatorSet()
animSet?.interpolator = LinearInterpolator() //线性
animOne = ObjectAnimator.ofFloat(
binding.previewText,
"translationX",
screenWidth.toString().toFloat(),
-textPaintWidth
) //X轴移动屏幕宽度为起始值也就是会从最右边开始到看不到文字。
animOne?.duration = 5000
animOne?.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
animatorRightToLeft()
}
})
animSet?.play(animOne)
animSet?.start()
}
override fun onPause() {
super.onPause()
pauseAnimation()
}
override fun onResume() {
super.onResume()
Log.d("MainActivity", "onResume()被调用")
resumeAnimation()
updatePreviewText()
// 延迟重置监听器确保Fragment完全恢复
binding.viewPager.post {
Log.d("MainActivity", "延迟重置Fragment监听器")
resetFragmentListeners()
// 强制重新创建适配器作为最后手段
if (tabFragmentPagerAdapter != null) {
Log.d("MainActivity", "重新创建适配器")
tabFragmentPagerAdapter = TabFragmentPagerAdapter(supportFragmentManager, mFragments, mStrings)
binding.viewPager.adapter = tabFragmentPagerAdapter
binding.tabLayout.setupWithViewPager(binding.viewPager)
// 重新设置监听器
binding.viewPager.postDelayed({
resetFragmentListeners()
}, 100)
}
}
}
override fun onRestart() {
super.onRestart()
Log.d("MainActivity", "onRestart()被调用")
}
override fun onStart() {
super.onStart()
Log.d("MainActivity", "onStart()被调用")
}
private fun resetFragmentListeners() {
Log.d("MainActivity", "开始重置Fragment监听器")
// 方法1尝试通过ViewPager获取当前Fragment实例
val currentTextFragment = supportFragmentManager.findFragmentByTag("android:switcher:${binding.viewPager.id}:0") as? TextFragment
val currentBackgroundFragment = supportFragmentManager.findFragmentByTag("android:switcher:${binding.viewPager.id}:1") as? BackgroundFragment
Log.d("MainActivity", "当前TextFragment实例: $currentTextFragment, 保存的实例: $textFragment")
Log.d("MainActivity", "当前BackgroundFragment实例: $currentBackgroundFragment, 保存的实例: $backgroundFragment")
// 方法2设置到当前活动的Fragment实例
currentTextFragment?.let { fragment ->
Log.d("MainActivity", "设置当前TextFragment监听器")
fragment.setTextFragmentClickListener(this)
}
currentBackgroundFragment?.let { fragment ->
Log.d("MainActivity", "设置当前BackgroundFragment监听器")
fragment.setBackgroundFragmentClickListener(this)
}
// 方法3同时设置到保存的实例作为备份
textFragment.setTextFragmentClickListener(this)
backgroundFragment.setBackgroundFragmentClickListener(this)
// 方法4强制刷新ViewPager的当前页面
val currentItem = binding.viewPager.currentItem
if (currentItem == 0) {
currentTextFragment?.setTextFragmentClickListener(this)
} else if (currentItem == 1) {
currentBackgroundFragment?.setBackgroundFragmentClickListener(this)
}
Log.d("MainActivity", "Fragment监听器重置完成")
}
private fun pauseAnimation() {
animSet?.pause()
}
private fun resumeAnimation() {
animSet?.resume()
}
override fun onFontItemClick() {
Log.d("MainActivity", "收到onFontItemClick回调")
updatePreviewText()
}
override fun onFontColorItemClick() {
Log.d("MainActivity", "收到onFontColorItemClick回调")
updatePreviewText()
}
override fun onFontSizeItemClick() {
Log.d("MainActivity", "收到onFontSizeItemClick回调")
updatePreviewText()
}
override fun onBgColorClick() {
Log.d("MainActivity", "收到onBgColorClick回调")
updatePreviewText()
}
}

View File

@ -0,0 +1,101 @@
package com.led.scroller.barrage.banner.ui
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.os.Bundle
import android.util.Log
import android.util.TypedValue
import android.view.View
import android.view.animation.LinearInterpolator
import androidx.activity.ComponentActivity
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import com.led.scroller.barrage.banner.databinding.ActivityShowBarrageBinding
import com.led.scroller.barrage.banner.sp.AppSP
import androidx.core.view.isVisible
class ShowBarrageActivity : ComponentActivity() {
companion object {
const val CONTENT_KEY = "content_key"
}
private var animSet: AnimatorSet? = null
private var animOne: ObjectAnimator? = null
private lateinit var binding: ActivityShowBarrageBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityShowBarrageBinding.inflate(layoutInflater)
setContentView(binding.root)
val controller = WindowCompat.getInsetsController(window, window.decorView)
hideSystemBars(controller)
initView()
val content = intent.getStringExtra(CONTENT_KEY)
animatorRightToLeft(content.toString())
}
private fun hideSystemBars(controller: WindowInsetsControllerCompat) {
// 让导航栏和状态栏完全隐藏
controller.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
controller.hide(WindowInsetsCompat.Type.systemBars())
}
private fun initView() {
binding.root.setOnClickListener {
if (binding.backImg.isVisible) {
binding.backImg.visibility = View.GONE
} else {
binding.backImg.visibility = View.VISIBLE
}
}
binding.backImg.setOnClickListener {
Log.d("ShowBarrageActivity", "返回按钮点击调用finish()")
finish()
}
}
override fun onBackPressed() {
Log.d("ShowBarrageActivity", "系统返回键被按下")
super.onBackPressed()
}
override fun finish() {
Log.d("ShowBarrageActivity", "finish()被调用")
super.finish()
}
private fun animatorRightToLeft(content: String) {
val sp = AppSP(this)
binding.previewLayout.setBackgroundColor(getColor(sp.bgColorResId))
binding.previewText.text = content
binding.previewText.setTextColor(getColor(sp.fontColorResId))
binding.previewText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, sp.fontSize.toFloat())
// 加载字体文件
val typeface = ResourcesCompat.getFont(this, sp.fontResId)
binding.previewText.typeface = typeface
val textPaint = binding.previewText.paint
val textPaintWidth = textPaint.measureText(content) //获取文字的宽度。
val screenWidth = resources.displayMetrics.widthPixels //获取屏幕的宽度。
animSet = AnimatorSet()
animSet?.interpolator = LinearInterpolator() //线性
animOne = ObjectAnimator.ofFloat(
binding.previewText,
"translationX",
screenWidth.toString().toFloat(),
-textPaintWidth
) //X轴移动屏幕宽度为起始值也就是会从最右边开始到看不到文字。
animOne?.duration = 5000
animOne?.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
animatorRightToLeft(content)
}
})
animSet?.play(animOne)
animSet?.start()
}
}

View File

@ -0,0 +1,168 @@
package com.led.scroller.barrage.banner.ui
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.drawable.GradientDrawable
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import com.led.scroller.barrage.banner.R
import com.led.scroller.barrage.banner.bean.FontColorBean
import com.led.scroller.barrage.banner.bean.FontBean
import com.led.scroller.barrage.banner.bean.FontSizeBean
import com.led.scroller.barrage.banner.databinding.FragmentTextBinding
import com.led.scroller.barrage.banner.sp.AppSP
import com.led.scroller.barrage.banner.ui.adapter.FontColorAdapter
import com.led.scroller.barrage.banner.ui.adapter.FontAdapter
import com.led.scroller.barrage.banner.ui.adapter.FontSizeAdapter
class TextFragment : Fragment() {
private lateinit var binding: FragmentTextBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentTextBinding.inflate(layoutInflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initView()
}
override fun onResume() {
super.onResume()
// 确保监听器在Fragment可见时重新设置
if (textFragmentClickListener == null) {
Log.d("TextFragment", "监听器为空,可能需要重新设置")
}
}
private fun initView() {
initFontAdapter()
initColorAdapter()
initFontSizeAdapter()
}
private fun initFontAdapter() {
val fontList: MutableList<FontBean> = mutableListOf()
val sp = AppSP(requireActivity())
val fontID = sp.fontResId
fontList.add(FontBean(R.font.font_medium, R.font.font_medium == fontID))
fontList.add(FontBean(R.font.header, R.font.header == fontID))
fontList.add(FontBean(R.font.damion, R.font.damion == fontID))
fontList.add(FontBean(R.font.grandma, R.font.grandma == fontID))
fontList.add(FontBean(R.font.playball, R.font.playball == fontID))
fontList.add(FontBean(R.font.pork, R.font.pork == fontID))
fontList.add(FontBean(R.font.silver, R.font.silver == fontID))
val fontAdapter = FontAdapter(requireActivity(), fontList)
fontAdapter.setOnItemClickListener(object : FontAdapter.OnItemClickListener {
@SuppressLint("NotifyDataSetChanged")
override fun onItemClick(position: Int) {
val bean = fontList[position]
for (font in fontList) {
font.select = false
}
fontList[position].select = true
AppSP(requireActivity()).fontResId = bean.id
fontAdapter.notifyDataSetChanged()
Log.d("TextFragment", "触发onFontItemClick, 监听器: $textFragmentClickListener")
textFragmentClickListener?.onFontItemClick()
}
})
binding.fontRv.layoutManager = GridLayoutManager(requireActivity(), 7)
binding.fontRv.adapter = fontAdapter
}
private fun initColorAdapter() {
val sp = AppSP(requireActivity())
val fontColorId = sp.fontColorResId
val fontColorList: MutableList<FontColorBean> = mutableListOf()
// 获取颜色资源数组
val colorArray = resources.obtainTypedArray(R.array.FontColorArray)
// 遍历颜色资源数组
for (i in 0 until colorArray.length()) {
val colorResId = colorArray.getResourceId(i, 0)
val colorValue = ContextCompat.getColor(requireContext(), colorResId)
// 将颜色值转换为十六进制字符串
val hexColor = String.format("#%06X", 0xFFFFFF and colorValue)
val fontColor = FontColorBean(colorResId, hexColor, colorResId == fontColorId)
fontColorList.add(fontColor)
}
colorArray.recycle()
val fontColorAdapter = FontColorAdapter(requireActivity(), fontColorList)
fontColorAdapter.setOnItemClickListener(object : FontColorAdapter.OnItemClickListener {
@SuppressLint("NotifyDataSetChanged")
override fun onItemClick(position: Int) {
val bean = fontColorList[position]
for (fontColor in fontColorList) {
fontColor.select = false
}
fontColorList[position].select = true
AppSP(requireActivity()).fontColorResId = bean.id
fontColorAdapter.notifyDataSetChanged()
Log.d("TextFragment", "触发onFontColorItemClick, 监听器: $textFragmentClickListener")
textFragmentClickListener?.onFontColorItemClick()
}
})
binding.colorRv.layoutManager = GridLayoutManager(requireActivity(), 7)
binding.colorRv.adapter = fontColorAdapter
}
private fun initFontSizeAdapter() {
val sp = AppSP(requireActivity())
val fontSize = sp.fontSize
val fontSizeList: MutableList<FontSizeBean> = mutableListOf()
// 获取字符串资源数组
val fontSizeNameArray = resources.getStringArray(R.array.FontSizeNameArray)
val fontSizeArray = resources.getIntArray(R.array.FontSizeArray)
// 确保两个数组的长度相同
if (fontSizeNameArray.size == fontSizeArray.size) {
for (i in fontSizeNameArray.indices) {
val name = fontSizeNameArray[i]
val size = fontSizeArray[i]
val fontSizeBean = FontSizeBean(name, size, fontSize == size)
fontSizeList.add(fontSizeBean)
}
}
val fontSizeAdapter = FontSizeAdapter(requireActivity(), fontSizeList)
fontSizeAdapter.setOnItemClickListener(object : FontSizeAdapter.OnItemClickListener {
@SuppressLint("NotifyDataSetChanged")
override fun onItemClick(position: Int) {
val bean = fontSizeList[position]
for (size in fontSizeList) {
size.select = false
}
fontSizeList[position].select = true
AppSP(requireActivity()).fontSize = bean.size
fontSizeAdapter.notifyDataSetChanged()
Log.d("TextFragment", "触发onFontSizeItemClick, 监听器: $textFragmentClickListener")
textFragmentClickListener?.onFontSizeItemClick()
}
})
binding.sizeRv.layoutManager = GridLayoutManager(requireActivity(), fontSizeList.size)
binding.sizeRv.adapter = fontSizeAdapter
}
private var textFragmentClickListener: TextFragmentClickListener? = null
fun setTextFragmentClickListener(listener: TextFragmentClickListener) {
Log.d("TextFragment", "设置监听器: $listener")
textFragmentClickListener = listener
}
interface TextFragmentClickListener {
fun onFontItemClick()
fun onFontColorItemClick()
fun onFontSizeItemClick()
}
}

View File

@ -0,0 +1,61 @@
package com.led.scroller.barrage.banner.ui.adapter
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Typeface
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.content.res.ResourcesCompat
import androidx.recyclerview.widget.RecyclerView
import com.led.scroller.barrage.banner.R
import com.led.scroller.barrage.banner.bean.FontBean
import com.led.scroller.barrage.banner.databinding.AdapterFontItemBinding
class FontAdapter(
private val context: Context,
private val fontList: MutableList<FontBean>,
) :
RecyclerView.Adapter<FontAdapter.FontViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FontViewHolder {
val inflater = LayoutInflater.from(context)
val binding = AdapterFontItemBinding.inflate(inflater, parent, false)
return FontViewHolder(binding)
}
@SuppressLint("UseCompatLoadingForDrawables")
override fun onBindViewHolder(holder: FontViewHolder, position: Int) {
val bean = fontList[position]
val typeface = ResourcesCompat.getFont(context, bean.id)
holder.binding.textView.typeface = typeface
if (bean.select) {
holder.binding.selectView.background =
context.getDrawable(R.drawable.drw_round_stroke_selected)
} else {
holder.binding.selectView.background =
context.getDrawable(R.drawable.drw_round_stroke_unselected)
}
holder.itemView.setOnClickListener {
if (itemClickListener != null) {
itemClickListener?.onItemClick(position)
}
}
}
override fun getItemCount(): Int {
return fontList.size
}
inner class FontViewHolder(val binding: AdapterFontItemBinding) :
RecyclerView.ViewHolder(binding.root)
private var itemClickListener: OnItemClickListener? = null
fun setOnItemClickListener(listener: OnItemClickListener) {
itemClickListener = listener
}
interface OnItemClickListener {
fun onItemClick(position: Int)
}
}

View File

@ -0,0 +1,58 @@
package com.led.scroller.barrage.banner.ui.adapter
import android.annotation.SuppressLint
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.led.scroller.barrage.banner.R
import com.led.scroller.barrage.banner.bean.FontColorBean
import com.led.scroller.barrage.banner.databinding.AdapterColorItemBinding
class FontColorAdapter(
private val context: Context,
private val colorList: MutableList<FontColorBean>,
) :
RecyclerView.Adapter<FontColorAdapter.ColorViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ColorViewHolder {
val inflater = LayoutInflater.from(context)
val binding = AdapterColorItemBinding.inflate(inflater, parent, false)
return ColorViewHolder(binding)
}
@SuppressLint("UseCompatLoadingForDrawables")
override fun onBindViewHolder(holder: ColorViewHolder, position: Int) {
val bean = colorList[position]
holder.binding.bgView.setCircleColor(bean.colorString)
if (bean.select) {
holder.binding.selectView.background =
context.getDrawable(R.drawable.drw_round_stroke_selected)
} else {
holder.binding.selectView.background =
context.getDrawable(R.drawable.drw_round_stroke_unselected)
}
holder.itemView.setOnClickListener {
if (itemClickListener != null) {
itemClickListener?.onItemClick(position)
}
}
}
override fun getItemCount(): Int {
return colorList.size
}
inner class ColorViewHolder(val binding: AdapterColorItemBinding) :
RecyclerView.ViewHolder(binding.root)
private var itemClickListener: OnItemClickListener? = null
fun setOnItemClickListener(listener: OnItemClickListener) {
itemClickListener = listener
}
interface OnItemClickListener {
fun onItemClick(position: Int)
}
}

View File

@ -0,0 +1,61 @@
package com.led.scroller.barrage.banner.ui.adapter
import android.annotation.SuppressLint
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.provider.FontsContractCompat
import androidx.recyclerview.widget.RecyclerView
import com.led.scroller.barrage.banner.R
import com.led.scroller.barrage.banner.bean.FontColorBean
import com.led.scroller.barrage.banner.bean.FontSizeBean
import com.led.scroller.barrage.banner.databinding.AdapterColorItemBinding
import com.led.scroller.barrage.banner.databinding.AdapterFontItemBinding
class FontSizeAdapter(
private val context: Context,
private val fontSizeList: MutableList<FontSizeBean>,
) :
RecyclerView.Adapter<FontSizeAdapter.FontSizeViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FontSizeViewHolder {
val inflater = LayoutInflater.from(context)
val binding = AdapterFontItemBinding.inflate(inflater, parent, false)
return FontSizeViewHolder(binding)
}
@SuppressLint("UseCompatLoadingForDrawables")
override fun onBindViewHolder(holder: FontSizeViewHolder, position: Int) {
val bean = fontSizeList[position]
holder.binding.textView.text = bean.name
if (bean.select) {
holder.binding.selectView.background =
context.getDrawable(R.drawable.drw_round_stroke_selected)
} else {
holder.binding.selectView.background =
context.getDrawable(R.drawable.drw_round_stroke_unselected)
}
holder.itemView.setOnClickListener {
if (itemClickListener != null) {
itemClickListener?.onItemClick(position)
}
}
}
override fun getItemCount(): Int {
return fontSizeList.size
}
inner class FontSizeViewHolder(val binding: AdapterFontItemBinding) :
RecyclerView.ViewHolder(binding.root)
private var itemClickListener: OnItemClickListener? = null
fun setOnItemClickListener(listener: OnItemClickListener) {
itemClickListener = listener
}
interface OnItemClickListener {
fun onItemClick(position: Int)
}
}

View File

@ -0,0 +1,22 @@
package com.led.scroller.barrage.banner.ui.adapter
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
class TabFragmentPagerAdapter(
fm: FragmentManager,
private val mFragments: List<Fragment>,
private val mTiles: List<String>
) : FragmentStatePagerAdapter(fm) {
override fun getCount(): Int = mFragments.size
override fun getItem(i: Int): Fragment {
return mFragments[i]
}
override fun getPageTitle(position: Int): CharSequence {
return mTiles[position]
}
}

View File

@ -0,0 +1,60 @@
package com.led.scroller.barrage.banner.ui.view
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View
class CircleView : View {
private var circleColor: Int = Color.BLACK
private val paint: Paint = Paint()
constructor(context: Context) : super(context) {
init(null)
}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
init(attrs)
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
init(attrs)
}
private fun init(attrs: AttributeSet?) {
// 初始化画笔
paint.isAntiAlias = true // 设置抗锯齿
paint.style = Paint.Style.FILL
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val centerX = width / 2.toFloat()
val centerY = height / 2.toFloat()
val radius = (width.coerceAtMost(height) / 2).toFloat()
// 设置圆的颜色
paint.color = circleColor
// 绘制圆形
canvas.drawCircle(centerX, centerY, radius, paint)
}
fun setCircleColor(color: Int) {
circleColor = color
invalidate() // 通知 View 进行重绘
}
fun setCircleColor(color: String) {
circleColor = Color.parseColor(color)
invalidate() // 通知 View 进行重绘
}
}

View File

@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@ -0,0 +1,25 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:pathData="M16,16m-16,0a16,16 0,1 1,32 0a16,16 0,1 1,-32 0"
android:strokeAlpha="0.2"
android:fillColor="#000000"
android:fillAlpha="0.2"/>
<path
android:pathData="M15.642,10.942L10.583,16L15.642,21.058"
android:strokeLineJoin="round"
android:strokeWidth="1.85556"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
<path
android:pathData="M21.417,16H10.725"
android:strokeLineJoin="round"
android:strokeWidth="1.85556"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="16dp"/>
<stroke
android:width="2dp"
android:color="@color/default_preview_bg_color" />
</shape>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="12dp" />
<gradient
android:endColor="@color/btn_end_color"
android:startColor="@color/btn_start_color" />
</shape>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="12dp" />
<stroke
android:width="1dp"
android:color="@color/white_20" />
</shape>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/default_preview_bg_color" />
<corners android:radius="16dp" />
</shape>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size
android:width="56dp"
android:height="56dp" />
<solid android:color="@color/black" />
</shape>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<stroke
android:width="1.5dp"
android:color="@color/default_preview_bg_color" />
</shape>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<stroke
android:width="1.5dp"
android:color="@color/transparent" />
</shape>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/main_tab_bg_color" />
<corners
android:topLeftRadius="16dp"
android:topRightRadius="16dp" />
</shape>

View File

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 388 KiB

View File

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:pathData="M16.267,3.631L6.474,11.787M8.781,14.103L11.016,16.326C11.153,16.467 11.325,16.569 11.514,16.623C11.703,16.677 11.903,16.681 12.094,16.634C12.286,16.589 12.463,16.496 12.608,16.363C12.754,16.23 12.863,16.062 12.924,15.875L16.594,4.88C16.671,4.674 16.687,4.451 16.64,4.236C16.594,4.021 16.486,3.825 16.331,3.669C16.175,3.514 15.978,3.406 15.763,3.36C15.548,3.313 15.325,3.329 15.119,3.405L4.116,7.074C3.922,7.14 3.75,7.257 3.616,7.411C3.483,7.566 3.393,7.754 3.357,7.955C3.319,8.138 3.327,8.328 3.381,8.507C3.435,8.686 3.532,8.849 3.665,8.981L6.474,11.788L6.381,15.343L8.781,14.104V14.103Z"
android:strokeLineJoin="round"
android:strokeWidth="1.66667"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,20 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:pathData="M15.469,6.021C15.707,6.157 15.906,6.353 16.043,6.591C16.18,6.829 16.252,7.099 16.25,7.374V12.432C16.25,12.994 15.942,13.512 15.446,13.785L10.758,16.75C10.526,16.878 10.265,16.944 10,16.944C9.735,16.944 9.474,16.878 9.242,16.75L4.554,13.785C4.311,13.652 4.108,13.456 3.967,13.218C3.826,12.98 3.751,12.709 3.75,12.432V7.373C3.75,6.811 4.058,6.294 4.554,6.021L9.242,3.257C9.481,3.125 9.75,3.056 10.023,3.056C10.296,3.056 10.565,3.125 10.804,3.257L15.492,6.021H15.469Z"
android:strokeLineJoin="round"
android:strokeWidth="1.66667"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
<path
android:pathData="M7.917,10C7.917,10.553 8.136,11.083 8.527,11.473C8.918,11.864 9.447,12.083 10,12.083C10.552,12.083 11.083,11.864 11.473,11.473C11.864,11.083 12.083,10.553 12.083,10C12.083,9.448 11.864,8.918 11.473,8.527C11.083,8.136 10.552,7.917 10,7.917C9.447,7.917 8.918,8.136 8.527,8.527C8.136,8.918 7.917,9.448 7.917,10Z"
android:strokeLineJoin="round"
android:strokeWidth="1.66667"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="2dp" />
<size
android:width="48dp"
android:height="3dp" />
<gradient
android:centerColor="@color/white"
android:endColor="@color/transparent"
android:startColor="@color/transparent" />
</shape>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,132 @@
<?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:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/theme_color"
android:orientation="vertical">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="24dp"
app:cardCornerRadius="16dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="200dp">
<LinearLayout
android:id="@+id/preview_layout"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="@color/default_preview_bg_color"
android:gravity="center">
<HorizontalScrollView
android:id="@+id/horizontal_scrollview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="true"
android:scrollbars="none">
<TextView
android:id="@+id/preview_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/font_medium"
android:singleLine="true"
android:text="@string/app_name"
android:textColor="@color/default_preview_text_color"
android:textSize="80dp" />
</HorizontalScrollView>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/drw_bg_stroke_preview" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="1"
android:background="@drawable/drw_frame_edit_bg"
android:gravity="center_vertical">
<EditText
android:id="@+id/content_edit"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="12dp"
android:layout_marginTop="6dp"
android:layout_marginEnd="6dp"
android:layout_marginBottom="6dp"
android:layout_weight="1"
android:background="@null"
android:fontFamily="@font/font_medium"
android:hint="@string/type_something"
android:text="@string/app_name"
android:textColor="@color/white"
android:textColorHint="@color/text_color_desc"
android:textSize="16sp" />
<LinearLayout
android:id="@+id/start_btn"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_margin="6dp"
android:background="@drawable/drw_btn_bg"
android:gravity="center"
android:paddingStart="16dp"
android:paddingEnd="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/Play"
android:fontFamily="@font/font_medium"
android:textColor="@color/black" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="16dp"
android:background="@drawable/drw_tab_bg"
android:orientation="vertical"
android:paddingTop="16dp">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
style="@style/TabLayoutIndicatorStyle"
android:layout_width="match_parent"
android:layout_height="30dp" />
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/preview_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/default_preview_bg_color"
android:gravity="center">
<HorizontalScrollView
android:id="@+id/horizontal_scrollview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="true"
android:clickable="false"
android:focusable="false"
android:scrollbars="none">
<TextView
android:id="@+id/preview_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/font_medium"
android:singleLine="true"
android:text="@string/app_name"
android:textColor="@color/default_preview_text_color"
android:textSize="80dp" />
</HorizontalScrollView>
</LinearLayout>
<ImageView
android:id="@+id/back_img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:layout_margin="16dp"
android:src="@drawable/back_shadow_bg_icon" />
</RelativeLayout>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_margin="6dp"
android:gravity="center">
<com.led.scroller.barrage.banner.ui.view.CircleView
android:id="@+id/bg_view"
android:layout_width="32dp"
android:layout_height="32dp" />
<View
android:id="@+id/select_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/drw_round_stroke_selected" />
</RelativeLayout>
</LinearLayout>

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_margin="6dp"
android:gravity="center">
<TextView
android:id="@+id/text_view"
android:layout_width="32dp"
android:layout_height="32dp"
android:background="@drawable/drw_round_black"
android:fontFamily="@font/font_medium"
android:gravity="center"
android:text="Aa"
android:textColor="@color/white"
android:textSize="10sp" />
<View
android:id="@+id/select_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/drw_round_stroke_selected" />
</RelativeLayout>
</LinearLayout>

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="6dp"
android:layout_marginBottom="8dp"
android:fontFamily="@font/font_medium"
android:text="@string/color"
android:textColor="@color/white"
android:textSize="14sp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/color_rv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/transparent"
android:overScrollMode="never"
android:scrollbars="none" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:layout_marginBottom="8dp"
android:fontFamily="@font/font_medium"
android:text="@string/font"
android:textColor="@color/white"
android:textSize="14sp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/font_rv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/transparent"
android:overScrollMode="never"
android:scrollbars="none" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:layout_marginBottom="8dp"
android:fontFamily="@font/font_medium"
android:text="@string/color"
android:textColor="@color/white"
android:textSize="14sp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/color_rv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/transparent"
android:overScrollMode="never"
android:scrollbars="none" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:layout_marginBottom="8dp"
android:fontFamily="@font/font_medium"
android:text="@string/size"
android:textColor="@color/white"
android:textSize="14sp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/size_rv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/transparent"
android:overScrollMode="never"
android:scrollbars="none" />
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</LinearLayout>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 KiB

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<array name="FontColorArray">
<item>@color/font_color_1</item>
<item>@color/font_color_2</item>
<item>@color/font_color_3</item>
<item>@color/font_color_4</item>
<item>@color/font_color_5</item>
<item>@color/font_color_6</item>
<item>@color/font_color_7</item>
<item>@color/font_color_8</item>
<item>@color/font_color_9</item>
<item>@color/font_color_10</item>
<item>@color/font_color_11</item>
<item>@color/font_color_12</item>
</array>
<array name="BgColorArray">
<item>@color/default_preview_bg_color</item>
<item>@color/bg_color_1</item>
<item>@color/bg_color_2</item>
<item>@color/bg_color_3</item>
<item>@color/bg_color_4</item>
<item>@color/bg_color_5</item>
<item>@color/bg_color_6</item>
<item>@color/bg_color_7</item>
<item>@color/bg_color_8</item>
<item>@color/bg_color_9</item>
<item>@color/bg_color_10</item>
<item>@color/bg_color_11</item>
<item>@color/bg_color_12</item>
</array>
<array name="FontSizeNameArray">
<item>@string/xs</item>
<item>@string/s</item>
<item>@string/m</item>
<item>@string/l</item>
<item>@string/xl</item>
</array>
<array name="FontSizeArray">
<item>48</item>
<item>64</item>
<item>80</item>
<item>96</item>
<item>112</item>
</array>
</resources>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="black_20">#33000000</color>
<color name="white">#FFFFFFFF</color>
<color name="white_20">#33FFFFFF</color>
<color name="theme_color">#000000</color>
<color name="default_preview_bg_color">#26FFCB</color>
<color name="default_preview_text_color">#000000</color>
<color name="text_color">#ffffff</color>
<color name="text_color_desc">#999999</color>
<color name="btn_start_color">#EDFFFB</color>
<color name="btn_end_color">#26FFCB</color>
<color name="main_tab_bg_color">#222222</color>
<color name="transparent">#00ffffff</color>
<color name="font_color_1">#000000</color>
<color name="font_color_2">#ffffff</color>
<color name="font_color_3">#51C7FC</color>
<color name="font_color_4">#FFCE00</color>
<color name="font_color_5">#FF9600</color>
<color name="font_color_6">#FF2351</color>
<color name="font_color_7">#1AEC94</color>
<color name="font_color_8">#1E1AEA</color>
<color name="font_color_9">#E91D1D</color>
<color name="font_color_10">#5552DA</color>
<color name="font_color_11">#29FEBD</color>
<color name="font_color_12">#FF15C9</color>
<color name="bg_color_1">#000000</color>
<color name="bg_color_2">#ffffff</color>
<color name="bg_color_3">#51C7FC</color>
<color name="bg_color_4">#FFCE00</color>
<color name="bg_color_5">#FF9600</color>
<color name="bg_color_6">#FF2351</color>
<color name="bg_color_7">#1AEC94</color>
<color name="bg_color_8">#1E1AEA</color>
<color name="bg_color_9">#E91D1D</color>
<color name="bg_color_10">#5552DA</color>
<color name="bg_color_11">#29FEBD</color>
<color name="bg_color_12">#FF15C9</color>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="corner_radius">16dp</dimen>
</resources>

View File

@ -0,0 +1,16 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_name">LED Scroller</string>
<string name="type_something">Type something…</string>
<string name="text">Text</string>
<string name="background">Background</string>
<string name="font">Font</string>
<string name="color">Color</string>
<string name="size">Size</string>
<string name="speed">Speed</string>
<string name="xs">XS</string>
<string name="s">S</string>
<string name="m">M</string>
<string name="l">L</string>
<string name="xl">XL</string>
<string name="Play">Play</string>
</resources>

View File

@ -0,0 +1,21 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="TabLayoutIndicatorStyle" parent="Widget.Design.TabLayout">
<item name="tabRippleColor">@color/transparent</item>
<item name="tabTextColor">@color/text_color_desc</item>
<item name="tabIndicatorFullWidth">false</item>
<item name="tabIndicatorColor">#00000000</item>
<item name="tabIndicatorHeight">4dp</item>
<item name="tabSelectedTextColor">@color/white</item>
<item name="tabMode">fixed</item>
<item name="tabTextAppearance">@style/TabLayoutNormalTextStyle</item>
<item name="tabGravity">fill</item>
<item name="tabIndicator">@drawable/tab_slider</item>
</style>
<style name="TabLayoutNormalTextStyle">
<item name="android:textSize">16sp</item>
<item name="android:fontFamily">@font/font_medium</item>
</style>
</resources>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.LEDBarrage" parent="Theme.MaterialComponents.Light.NoActionBar">
<item name="colorPrimary">@color/purple_500</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/black</item>
</style>
</resources>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample backup rules file; uncomment and customize as necessary.
See https://developer.android.com/guide/topics/data/autobackup
for details.
Note: This file is ignored for devices older that API 31
See https://developer.android.com/about/versions/12/backup-restore
-->
<full-backup-content>
<!--
<include domain="sharedpref" path="."/>
<exclude domain="sharedpref" path="device.xml"/>
-->
</full-backup-content>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample data extraction rules file; uncomment and customize as necessary.
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
for details.
-->
<data-extraction-rules>
<cloud-backup>
<!-- TODO: Use <include> and <exclude> to control what is backed up.
<include .../>
<exclude .../>
-->
</cloud-backup>
<!--
<device-transfer>
<include .../>
<exclude .../>
</device-transfer>
-->
</data-extraction-rules>

View File

@ -0,0 +1,17 @@
package com.led.scroller.barrage.banner
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

6
build.gradle Normal file
View File

@ -0,0 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id 'com.android.application' version '8.9.1' apply false
id 'com.android.library' version '8.9.1' apply false
id 'org.jetbrains.kotlin.android' version '2.2.21' apply false
}

23
gradle.properties Normal file
View File

@ -0,0 +1,23 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,6 @@
#Wed Nov 19 10:36:22 CST 2025
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

185
gradlew vendored Normal file
View File

@ -0,0 +1,185 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

89
gradlew.bat vendored Normal file
View File

@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

6
keystore.properties Normal file
View File

@ -0,0 +1,6 @@
app_name=LED Scroller
package_name=com.led.scroller.barrage.banner
keystoreFile=app/LEDScroller
key_alias=LEDScrollerkey0
key_store_password=LEDScroller
key_password=LEDScroller

16
settings.gradle Normal file
View File

@ -0,0 +1,16 @@
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "LED Scroller"
include ':app'