package com.hfsmzt.sjmfztmh.splash.ui.fragment
import android.content.Context
import android.content.Intent
import android.graphics.Typeface
import android.os.Bundle
import android.view.KeyEvent
import android.view.View
import android.view.WindowManager
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.TextView
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.dream.baselib.base.BaseFragment
import com.dream.baselib.utils.AesUtil
import com.dream.baselib.utils.GsonTools
import com.dream.baselib.utils.LogUtil
import com.dream.common.adn.AdConstant
import com.dream.common.adn.AdManager
import com.dream.common.adn.HalfScreenAdCallback
import com.hfsmzt.sjmfztmh.R
import com.hfsmzt.sjmfztmh.databinding.FragmentFontBinding
import com.hfsmzt.sjmfztmh.splash.adapter.EmoBigAdapter
import com.hfsmzt.sjmfztmh.splash.adapter.FontAdapter
import com.hfsmzt.sjmfztmh.splash.base.BaseFragmentFontBeautify
import com.hfsmzt.sjmfztmh.splash.data.FontPair
import com.hfsmzt.sjmfztmh.splash.data.ResolveData
import com.hfsmzt.sjmfztmh.splash.model.FontViewModel
import com.hfsmzt.sjmfztmh.splash.ui.ActivityFontDetailsInstall
import com.hfsmzt.sjmfztmh.splash.ui.SearchActivity
import com.hfsmzt.sjmfztmh.splash.util.FontCacheManager
import com.hfsmzt.sjmfztmh.splash.util.FontManagerUtil
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.Serializable
import java.net.URLEncoder
class FontFragment : BaseFragmentFontBeautify<FragmentFontBinding>() {
private lateinit var fontViewModel: FontViewModel
private lateinit var fontAdapter: FontAdapter
private var originalPrefix = ""
private var fontPairs = mutableListOf<FontPair>()
private var bottomNavigationView: View? = null
override fun outStatusBars() = binding.main
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
bottomNavigationView = activity?.findViewById(R.id.bottom_navigation)
activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
}
override fun initView() {
preloadCurrentFont()
fontAdapter = FontAdapter(mutableListOf(),originalPrefix) { fontPair, _ ->
val fontUrl = "$originalPrefix${fontPair.fileName}"
Intent(requireContext(), ActivityFontDetailsInstall::class.java).apply {
putExtra("display_name", fontPair.displayName)
putExtra("file_name", fontPair.fileName)
putExtra("font_url", fontUrl)
startActivity(this)
}
}
binding.rvFontList.apply {
layoutManager = LinearLayoutManager(context)
adapter = fontAdapter
addItemDecoration(
DividerItemDecoration(context, DividerItemDecoration.VERTICAL).apply {
setDrawable(ContextCompat.getDrawable(context, R.drawable.divider)!!)
})
addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState == RecyclerView.SCROLL_STATE_DRAGGING || newState == RecyclerView.SCROLL_STATE_SETTLING) {
fontAdapter.refreshAllVisibleFonts()
}
}
})
}
AdManager.showHalfScreenAd(AdConstant.HALFSCREEN_AD_ON_TAB_1, requireActivity(), null)
}
private fun preloadCurrentFont() {
val context = requireContext()
if (FontManagerUtil.isGlobalFontEnabledStatus(context)) {
val currentTypeface = FontManagerUtil.getCurrentTypeface()
if (currentTypeface == null) {
// 若当前字体未加载,异步加载并缓存
CoroutineScope(Dispatchers.IO).launch {
val prefs = context.getSharedPreferences(FontManagerUtil.PREFS_NAME, Context.MODE_PRIVATE)
val fontUrl = prefs.getString(FontManagerUtil.KEY_FONT_URL, null)
val fileName = prefs.getString(FontManagerUtil.KEY_FILE_NAME, null)
if (!fontUrl.isNullOrEmpty() && !fileName.isNullOrEmpty()) {
val fontFile = FontManagerUtil.downloadFont(context, fontUrl, fileName)
if (fontFile != null) {
FontManagerUtil.setCurrentFont(context, fileName, fontUrl, Typeface.createFromFile(fontFile))
}
}
}
}
}
}
override fun initData() {
fontViewModel = ViewModelProvider(this).get(FontViewModel::class.java)
if (FontCacheManager.hasFontListCache(requireContext())) {
val cachedData = FontCacheManager.getCachedFontListData(requireContext())
if (cachedData != null) {
updateFontList(cachedData)
preloadFonts(cachedData)
} else {
initViewModelAndLoadData()
}
} else {
initViewModelAndLoadData()
}
}
private fun preloadFonts(resolveData: ResolveData) {
val minSize = minOf(resolveData.fontsCN.size, resolveData.fontsEN.size)
val pairsToPreload = mutableListOf<FontPair>()
for (i in 0 until minSize) {
pairsToPreload.add(
FontPair(
displayName = resolveData.fontsCN[i].removeSuffix(".ttf"),
fileName = resolveData.fontsEN[i]
)
)
}
// 预加载
FontManagerUtil.preloadAllFonts(requireContext(), pairsToPreload, resolveData.prefix)
// 预加载完成后刷新界面
binding.rvFontList.post {
fontAdapter.refreshAllVisibleFonts()
}
}
private fun initViewModelAndLoadData() {
fontViewModel = ViewModelProvider(this).get(FontViewModel::class.java)
fontViewModel.getEncryption()
}
override fun observeData() {
fontViewModel._encryptionData.observe(viewLifecycleOwner) { encryptionData ->
val compatible = AesUtil.decryptCbcCompatible(
encryptionData.d, AesUtil.obtainSecret(), encryptionData.i
)
LogUtil.d("FontFragment", "解密后的数据: $compatible")
val resolveData = GsonTools.fromJson(compatible, ResolveData::class.java) ?: run {
Toast.makeText(context, "数据解析失败", Toast.LENGTH_SHORT).show()
return@observe
}
FontCacheManager.cacheFontListData(requireContext(), resolveData)
updateFontList(resolveData)
}
}
private fun updateFontList(resolveData: ResolveData) {
originalPrefix = resolveData.prefix
fontPairs.clear()
val minSize = minOf(resolveData.fontsCN.size, resolveData.fontsEN.size)
for (i in 0 until minSize) {
fontPairs.add(
FontPair(
displayName = resolveData.fontsCN[i].removeSuffix(".ttf"),
fileName = resolveData.fontsEN[i]
)
)
}
// 直接更新适配器数据,不再需要搜索模式判断
fontAdapter.updateData(fontPairs)
refreshFonts()
}
override fun setListeners() {
// 修改搜索按钮监听器
binding.rlFont.setOnClickListener {
startSearchActivity()
}
}
private fun startSearchActivity() {
Intent(requireContext(), SearchActivity::class.java).apply {
putExtra("font_pairs", ArrayList(fontPairs)) // 传递字体数据
putExtra("original_prefix", originalPrefix)
startActivity(this)
}
}
override fun onStart() {
super.onStart()
refreshFonts()
}
private fun refreshFonts() {
reapplyFont()
fontAdapter.refreshAllVisibleFonts()
}
override fun showLoading() {}
override fun onFontChanged() {
super.onFontChanged()
fontAdapter.refreshAllVisibleFonts()
}
override fun onResume() {
super.onResume()
// refreshFonts()
}
}package com.hfsmzt.sjmfztmh.splash.adapter
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Typeface
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageButton
import android.widget.LinearLayout
import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.hfsmzt.sjmfztmh.R
import com.hfsmzt.sjmfztmh.splash.data.FontPair
import com.hfsmzt.sjmfztmh.splash.util.FontManagerUtil
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class FontAdapter(
private var fontPairs: List<FontPair>,
private val originalPrefix: String,
private val onItemClick: (FontPair, TextView) -> Unit
) : RecyclerView.Adapter<FontAdapter.FontViewHolder>() {
inner class FontViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val llFontItem: LinearLayout = itemView.findViewById(R.id.ll_font_item)
val tvFontSample: TextView = itemView.findViewById(R.id.tv_font_sample)
val tvFontName: TextView = itemView.findViewById(R.id.tv_font_name)
val ibInstall: ImageButton = itemView.findViewById(R.id.ib_install)
}
// 更新数据:接收List<String>(字体文件名列表)
@SuppressLint("NotifyDataSetChanged")
fun updateData(newFonts: List<FontPair>) {
fontPairs = newFonts
notifyDataSetChanged()
}
// 新增:刷新所有可见项字体(配合Fragment调用)
fun refreshAllVisibleFonts() {
recyclerView?.let { rv ->
// 获取布局管理器
val layoutManager = rv.layoutManager as? LinearLayoutManager
layoutManager?.let { lm ->
// 获取第一个和最后一个可见项的位置
val firstVisiblePosition = lm.findFirstVisibleItemPosition()
val lastVisiblePosition = lm.findLastVisibleItemPosition()
// 遍历所有可见项
for (i in firstVisiblePosition..lastVisiblePosition) {
if (i >= 0 && i < itemCount) {
val viewHolder = rv.findViewHolderForAdapterPosition(i) as? FontViewHolder
viewHolder?.let { holder ->
// 应用字体到ViewHolder,需要传递position参数
applyFontToViewHolder(holder, i)
}
}
}
} ?: run {
// 如果没有布局管理器,遍历所有项(备用方案)
for (i in 0 until itemCount) {
val viewHolder = rv.findViewHolderForAdapterPosition(i) as? FontViewHolder
viewHolder?.let { holder ->
applyFontToViewHolder(holder, i)
}
}
}
}
}
private fun applyFontToViewHolder(holder: FontViewHolder, position: Int) {
val context = holder.itemView.context
val currentTypeface = FontManagerUtil.getCurrentTypeface()
// 字体名称:应用全局字体
if (FontManagerUtil.isGlobalFontEnabledStatus(context) && currentTypeface != null) {
holder.tvFontName.typeface = currentTypeface
} else {
holder.tvFontName.typeface = Typeface.DEFAULT
}
// 示例文本:应用当前字体项的字体
// if (position in 0 until fontPairs.size) {
// val targetFontPair = fontPairs[position]
// val targetTypeface = FontManagerUtil.getTypefaceByName(context, targetFontPair.fileName)
//
// // 确保示例文本始终显示美化字体
// if (targetTypeface != null) {
// holder.tvFontSample.typeface = targetTypeface
// holder.tvFontSample.text = "让文字跳出框架,成为视觉的诗"
// } else {
// // 如果字体未加载,使用默认字体但保持文本内容
// holder.tvFontSample.typeface = Typeface.DEFAULT
// holder.tvFontSample.text = "让文字跳出框架,成为视觉的诗"
//
// // 可选:异步加载字体并在加载完成后刷新
// loadFontAsync(context, targetFontPair, holder)
// }
// }
if (position in 0 until fontPairs.size) {
val targetFontPair = fontPairs[position]
// 先设置文本内容
holder.tvFontSample.text = "让文字跳出框架,成为视觉的诗"
// 同步获取字体,避免异步导致的闪烁
val targetTypeface = FontManagerUtil.getTypefaceByName(context, targetFontPair.fileName)
if (targetTypeface != null) {
// 如果字体已缓存,直接应用
holder.tvFontSample.typeface = targetTypeface
} else {
// 使用默认字体但启动异步加载
holder.tvFontSample.typeface = Typeface.DEFAULT
loadFontAsync(context, targetFontPair, holder)
}
// 字体名称应用全局字体(同步)
if (FontManagerUtil.isGlobalFontEnabledStatus(context)) {
val currentTypeface = FontManagerUtil.getCurrentTypeface()
holder.tvFontName.typeface = currentTypeface ?: Typeface.DEFAULT
} else {
holder.tvFontName.typeface = Typeface.DEFAULT
}
}
}
// 异步加载字体
private fun loadFontAsync(context: Context, fontPair: FontPair, holder: FontViewHolder) {
CoroutineScope(Dispatchers.IO).launch {
// 这里需要你知道如何获取字体URL
val fontUrl = "$originalPrefix${fontPair.fileName}"
if (fontUrl.isNotEmpty()) {
val fontFile = FontManagerUtil.downloadFont(context, fontUrl, fontPair.fileName)
if (fontFile != null) {
withContext(Dispatchers.Main) {
val typeface = Typeface.createFromFile(fontFile)
holder.tvFontSample.typeface = typeface
}
}
}
}
}
// 保存RecyclerView引用(用于刷新可见项)
private var recyclerView: RecyclerView? = null
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
this.recyclerView = recyclerView
}
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
super.onDetachedFromRecyclerView(recyclerView)
this.recyclerView = null
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FontViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_font, parent, false)
return FontViewHolder(view)
}
override fun onBindViewHolder(holder: FontViewHolder, position: Int) {
val fontPair = fontPairs[position]
// 1. 显示字体名称(接口返回的中文名称)
holder.tvFontName.text = fontPair.displayName
// 2. 示例文本固定为"让文字跳出框架,成为视觉的诗",并应用字体美化
holder.tvFontSample.text = "让文字跳出框架,成为视觉的诗" // 固定文本
// 3. 绑定点击事件(LinearLayout和安装按钮均触发)
// 3.1 安装按钮点击(原有逻辑不变)
holder.ibInstall.setOnClickListener {
onItemClick(fontPair, holder.tvFontSample)
}
// 3.2 整个LinearLayout点击(进入详情页,复用同一回调)
holder.llFontItem.setOnClickListener {
onItemClick(fontPair, holder.tvFontSample)
}
// 4. 绑定数据时应用字体(示例文本用当前字体,名称用全局字体)
applyFontToViewHolder(holder, position)
}
override fun getItemCount(): Int = fontPairs.size
}
最新发布