From a4da01adf47f9dba5acd40f66dd0f0a2d1df3730 Mon Sep 17 00:00:00 2001 From: Li ZongYing Date: Wed, 21 Feb 2024 15:14:06 +0800 Subject: [PATCH] new memu style --- README.md | 5 ++ .../java/com/lizongying/mytv/CardAdapter.kt | 44 +++++----- .../java/com/lizongying/mytv/CardPresenter.kt | 82 ------------------- .../mytv/GrayOverlayItemDecoration.kt | 48 +++++++++++ .../java/com/lizongying/mytv/MainActivity.kt | 3 + .../java/com/lizongying/mytv/MainFragment.kt | 65 ++++++++++++--- .../main/java/com/lizongying/mytv/Request.kt | 6 +- .../lizongying/mytv/api/YSPTokenService.kt | 2 + app/src/main/res/layout/row.xml | 2 +- app/src/main/res/values/colors.xml | 1 + app/src/main/res/values/styles.xml | 4 +- 11 files changed, 136 insertions(+), 126 deletions(-) delete mode 100644 app/src/main/java/com/lizongying/mytv/CardPresenter.kt create mode 100644 app/src/main/java/com/lizongying/mytv/GrayOverlayItemDecoration.kt diff --git a/README.md b/README.md index 6c29bc2..a0ac1ed 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,10 @@ ## 更新日志 +### v1.6.2(通用) + +* 新的频道列表样式 + ### v1.6.0(通用) * 通用(春晚緊急修復) @@ -201,6 +205,7 @@ adb install my-tv.apk * 1.5.0 无法安装,1.5.1 可以安装 * 选中的图标比例能否相差更大 * 节目增加预告 +* 频道列表优化 ## 赞赏 diff --git a/app/src/main/java/com/lizongying/mytv/CardAdapter.kt b/app/src/main/java/com/lizongying/mytv/CardAdapter.kt index cb1bd26..81f3a3c 100644 --- a/app/src/main/java/com/lizongying/mytv/CardAdapter.kt +++ b/app/src/main/java/com/lizongying/mytv/CardAdapter.kt @@ -1,10 +1,10 @@ package com.lizongying.mytv import android.graphics.Color +import android.util.Log import android.view.ContextThemeWrapper import android.view.View import android.view.ViewGroup -import android.view.animation.Animation import android.view.animation.ScaleAnimation import android.widget.ImageView import androidx.leanback.widget.ImageCardView @@ -18,21 +18,28 @@ import com.lizongying.mytv.models.TVViewModel class CardAdapter( private val recyclerView: RecyclerView, private val owner: LifecycleOwner, - private var tvListViewModel: TVListViewModel + private var tvListViewModel: TVListViewModel, + var defaultFocus: Int, ) : RecyclerView.Adapter() { private var listener: ItemListener? = null + private var focused: View? = null + private var defaultFocused = false override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val cardView = object : ImageCardView(ContextThemeWrapper(parent.context, R.style.CustomImageCardTheme)) {} cardView.isFocusable = true cardView.isFocusableInTouchMode = true - return ViewHolder(cardView) } + fun clear() { + focused?.clearFocus() + recyclerView.invalidate() + } + private fun startScaleAnimation(view: View, fromScale: Float, toScale: Float, duration: Long) { val scaleAnimation = ScaleAnimation( fromScale, toScale, @@ -41,19 +48,7 @@ class CardAdapter( ScaleAnimation.RELATIVE_TO_SELF, 0.5f ) scaleAnimation.duration = duration - scaleAnimation.fillAfter = true - scaleAnimation.setAnimationListener(object : Animation.AnimationListener { - override fun onAnimationStart(animation: Animation?) { - } - - override fun onAnimationEnd(animation: Animation?) { - view.tag = toScale - } - - override fun onAnimationRepeat(animation: Animation?) { - } - }) - + scaleAnimation.fillAfter = false view.startAnimation(scaleAnimation) } @@ -63,20 +58,19 @@ class CardAdapter( val tvViewModel = item as TVViewModel val cardView = viewHolder.view as ImageCardView - startScaleAnimation(cardView, 1.0f, 0.9f, 0) + if (!defaultFocused && item.id.value == defaultFocus) { + cardView.requestFocus() + defaultFocused = true + } - val onFocusChangeListener = View.OnFocusChangeListener { view, hasFocus -> + val onFocusChangeListener = View.OnFocusChangeListener { _, hasFocus -> listener?.onItemFocusChange(item, hasFocus) + Log.i(TAG, "defaultFocus $defaultFocus ${item.id.value}") +// if (hasFocus && defaultFocus == item.id.value) { if (hasFocus) { + focused = cardView startScaleAnimation(cardView, 0.9f, 1.0f, 200) - - for (i in 0 until recyclerView.childCount) { - val v = recyclerView.getChildAt(i) - if (v != view && v.tag != 0.9f) { - startScaleAnimation(v, 1.0f, 0.9f, 200) - } - } } } diff --git a/app/src/main/java/com/lizongying/mytv/CardPresenter.kt b/app/src/main/java/com/lizongying/mytv/CardPresenter.kt deleted file mode 100644 index cd6dbf4..0000000 --- a/app/src/main/java/com/lizongying/mytv/CardPresenter.kt +++ /dev/null @@ -1,82 +0,0 @@ -package com.lizongying.mytv - -import android.graphics.Color -import android.view.ContextThemeWrapper -import android.view.ViewGroup -import android.widget.ImageView -import androidx.leanback.widget.ImageCardView -import androidx.leanback.widget.Presenter -import androidx.lifecycle.LifecycleOwner -import com.bumptech.glide.Glide -import com.lizongying.mytv.models.TVViewModel - -class CardPresenter( - private val owner: LifecycleOwner, -) : Presenter() { - - override fun onCreateViewHolder(parent: ViewGroup): ViewHolder { - val cardView = object : - ImageCardView(ContextThemeWrapper(parent.context, R.style.CustomImageCardTheme)) {} - - cardView.isFocusable = true - cardView.isFocusableInTouchMode = true - return ViewHolder(cardView) - } - - override fun onBindViewHolder(viewHolder: ViewHolder, item: Any) { - val tvViewModel = item as TVViewModel - val cardView = viewHolder.view as ImageCardView - - cardView.titleText = tvViewModel.title.value - cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT) - cardView.tag = tvViewModel.videoUrl.value - - when (tvViewModel.title.value) { - "CCTV8K 超高清" -> Glide.with(viewHolder.view.context) - .load(R.drawable.cctv8k) - .centerInside() - .into(cardView.mainImageView) - - "天津卫视" -> Glide.with(viewHolder.view.context) - .load(R.drawable.tianjin) - .centerInside() - .into(cardView.mainImageView) - - "新疆卫视" -> Glide.with(viewHolder.view.context) - .load(R.drawable.xinjiang) - .centerInside() - .into(cardView.mainImageView) - - "兵团卫视" -> Glide.with(viewHolder.view.context) - .load(R.drawable.bingtuan) - .centerInside() - .into(cardView.mainImageView) - - else -> Glide.with(viewHolder.view.context) - .load(tvViewModel.logo.value) - .centerInside() - .into(cardView.mainImageView) - } - - cardView.setBackgroundColor(Color.WHITE) - cardView.setMainImageScaleType(ImageView.ScaleType.CENTER_INSIDE) - - tvViewModel.program.observe(owner) { _ -> - val program = tvViewModel.getProgramOne() - if (program != null) { - cardView.contentText = program.name - } - } - } - - override fun onUnbindViewHolder(viewHolder: ViewHolder) { - val cardView = viewHolder.view as ImageCardView - cardView.mainImage = null - } - - companion object { - private const val TAG = "CardPresenter" - private const val CARD_WIDTH = 300 - private const val CARD_HEIGHT = 101 - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lizongying/mytv/GrayOverlayItemDecoration.kt b/app/src/main/java/com/lizongying/mytv/GrayOverlayItemDecoration.kt new file mode 100644 index 0000000..1663658 --- /dev/null +++ b/app/src/main/java/com/lizongying/mytv/GrayOverlayItemDecoration.kt @@ -0,0 +1,48 @@ +package com.lizongying.mytv + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Rect +import android.view.View +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.RecyclerView + +class GrayOverlayItemDecoration(private val context: Context) : RecyclerView.ItemDecoration() { + + private val grayOverlayPaint = Paint().apply { + color = ContextCompat.getColor(context, R.color.gray_overlay) + style = Paint.Style.FILL + } + + override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { + super.onDrawOver(c, parent, state) + + val childCount = parent.childCount + for (i in 0 until childCount) { + val child = parent.getChildAt(i) + if (!child.hasFocus()) { + // 计算遮罩层的大小 + val overlayRect = Rect( + child.left, + child.top, + child.right, + child.bottom + ) + // 绘制灰色遮罩层 + c.drawRect(overlayRect, grayOverlayPaint) + } + } + } + + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + // 在此处设置偏移量为0,以防止遮罩层影响项的布局 + outRect.setEmpty() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lizongying/mytv/MainActivity.kt b/app/src/main/java/com/lizongying/mytv/MainActivity.kt index a92f6d2..9f3b7b1 100644 --- a/app/src/main/java/com/lizongying/mytv/MainActivity.kt +++ b/app/src/main/java/com/lizongying/mytv/MainActivity.kt @@ -335,6 +335,7 @@ class MainActivity : FragmentActivity() { } override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { + Log.i(TAG, "keyCode $keyCode") when (keyCode) { KeyEvent.KEYCODE_0 -> { showChannel("0") @@ -446,6 +447,7 @@ class MainActivity : FragmentActivity() { } KeyEvent.KEYCODE_DPAD_LEFT -> { + channelUp() // if (mainFragment.isHidden) { // prevSource() // } else { @@ -459,6 +461,7 @@ class MainActivity : FragmentActivity() { } KeyEvent.KEYCODE_DPAD_RIGHT -> { + channelDown() // if (mainFragment.isHidden) { // nextSource() // } else { diff --git a/app/src/main/java/com/lizongying/mytv/MainFragment.kt b/app/src/main/java/com/lizongying/mytv/MainFragment.kt index 678b641..76ad48a 100644 --- a/app/src/main/java/com/lizongying/mytv/MainFragment.kt +++ b/app/src/main/java/com/lizongying/mytv/MainFragment.kt @@ -11,7 +11,7 @@ import android.view.ViewGroup import android.widget.Toast import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope -import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import com.lizongying.mytv.Utils.dpToPx import com.lizongying.mytv.Utils.getDateTimestamp @@ -22,6 +22,7 @@ import com.lizongying.mytv.models.TVViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch + class MainFragment : Fragment(), CardAdapter.ItemListener { private var itemPosition = 0 @@ -56,6 +57,11 @@ class MainFragment : Fragment(), CardAdapter.ItemListener { activity?.let { request.initYSP(it) } sharedPref = (activity as? MainActivity)?.sharedPref!! + itemPosition = sharedPref.getInt(POSITION, 0) + if (itemPosition >= tvListViewModel.size()) { + itemPosition = 0 + } + view?.post { val content = binding.content @@ -75,15 +81,23 @@ class MainFragment : Fragment(), CardAdapter.ItemListener { tvListViewModel.maxNum.add(v.size) val adapter = - CardAdapter(itemBinding.rowItems, viewLifecycleOwner, tvListViewModelCurrent) + CardAdapter( + itemBinding.rowItems, + viewLifecycleOwner, + tvListViewModelCurrent, + itemPosition + ) rowList.add(itemBinding.rowItems) adapter.setItemListener(this) itemBinding.rowHeader.text = k + itemBinding.rowItems.tag = idx.toInt() itemBinding.rowItems.adapter = adapter itemBinding.rowItems.layoutManager = - LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) + GridLayoutManager(context, 6) + itemBinding.rowItems.layoutParams.height = + dpToPx(100 * ((tvListViewModelCurrent.size() + 6 - 1) / 6)) itemBinding.rowItems.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { @@ -92,6 +106,27 @@ class MainFragment : Fragment(), CardAdapter.ItemListener { } }) + itemBinding.rowItems.setOnKeyListener { v, keyCode, event -> + Log.i(TAG, "itemBinding.rowItems.setOnKeyListener ") + false + } + + itemBinding.row.setOnKeyListener { v, keyCode, event -> + Log.i(TAG, "itemBinding.row.setOnKeyListener ") + false + } + + itemBinding.root.setOnKeyListener { v, keyCode, event -> + Log.i(TAG, "itemBinding.root.setOnKeyListener ") + false + } + + + val itemDecoration = context?.let { GrayOverlayItemDecoration(it) } + if (itemDecoration != null) { + itemBinding.rowItems.addItemDecoration(itemDecoration) + } + val layoutParams = itemBinding.row.layoutParams as ViewGroup.MarginLayoutParams layoutParams.topMargin = dpToPx(11F) itemBinding.row.layoutParams = layoutParams @@ -103,12 +138,7 @@ class MainFragment : Fragment(), CardAdapter.ItemListener { mUpdateProgramRunnable = UpdateProgramRunnable() handler.post(mUpdateProgramRunnable) - itemPosition = sharedPref.getInt(POSITION, 0) - if (itemPosition >= tvListViewModel.size()) { - itemPosition = 0 - } tvListViewModel.setItemPosition(itemPosition) - setPosition() tvListViewModel.tvListViewModel.value?.forEach { tvViewModel -> tvViewModel.errInfo.observe(viewLifecycleOwner) { _ -> if (tvViewModel.errInfo.value != null @@ -154,7 +184,6 @@ class MainFragment : Fragment(), CardAdapter.ItemListener { } } } - (activity as MainActivity).fragmentReady() } } @@ -166,11 +195,21 @@ class MainFragment : Fragment(), CardAdapter.ItemListener { override fun onItemFocusChange(tvViewModel: TVViewModel, hasFocus: Boolean) { if (hasFocus) { tvListViewModel.setItemPositionCurrent(tvViewModel.id.value!!) + + val row = tvViewModel.getRowPosition() + + for (i in rowList) { + if (i.tag as Int != row) { + ((i as RecyclerView).adapter as CardAdapter).clear() + } + } + (activity as MainActivity).mainActive() } } override fun onItemClicked(tvViewModel: TVViewModel) { + Log.i(TAG, "onItemClicked") if (this.isHidden) { (activity as? MainActivity)?.switchMainFragment() return @@ -186,10 +225,8 @@ class MainFragment : Fragment(), CardAdapter.ItemListener { fun setPosition() { val tvViewModel = tvListViewModel.getTVViewModel(itemPosition) - Log.i(TAG, "tvViewModel $tvViewModel") - Log.i(TAG, "rowList ${rowList.size}") rowList[tvViewModel!!.getRowPosition()].post { - ((rowList[tvViewModel.getRowPosition()] as RecyclerView).layoutManager as LinearLayoutManager).findViewByPosition( + ((rowList[tvViewModel.getRowPosition()] as RecyclerView).layoutManager as GridLayoutManager).findViewByPosition( tvViewModel.getItemPosition() )?.requestFocus() } @@ -197,10 +234,12 @@ class MainFragment : Fragment(), CardAdapter.ItemListener { fun setPosition(rowPosition: Int, itemPosition: Int) { rowList[rowPosition].post { - ((rowList[rowPosition] as RecyclerView).layoutManager as LinearLayoutManager).findViewByPosition( + ((rowList[rowPosition] as RecyclerView).layoutManager as GridLayoutManager).findViewByPosition( itemPosition )?.requestFocus() } + + ((rowList[rowPosition] as RecyclerView).adapter as CardAdapter).defaultFocus = itemPosition } fun check(tvViewModel: TVViewModel): Boolean { diff --git a/app/src/main/java/com/lizongying/mytv/Request.kt b/app/src/main/java/com/lizongying/mytv/Request.kt index fa9e68e..d30b93f 100644 --- a/app/src/main/java/com/lizongying/mytv/Request.kt +++ b/app/src/main/java/com/lizongying/mytv/Request.kt @@ -263,7 +263,7 @@ class Request { fun fetchAuth(tvModel: TVViewModel) { if (token == "") { - yspTokenService.getInfo() + yspTokenService.getInfo("") .enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { if (response.isSuccessful) { @@ -310,7 +310,7 @@ class Request { fun fetchVideo(tvModel: TVViewModel) { if (token == "") { - yspTokenService.getInfo() + yspTokenService.getInfo("") .enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { if (response.isSuccessful && response.body()?.data?.token != null) { @@ -375,7 +375,7 @@ class Request { } fun fetchToken() { - yspTokenService.getInfo() + yspTokenService.getInfo(token) .enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { if (response.isSuccessful) { diff --git a/app/src/main/java/com/lizongying/mytv/api/YSPTokenService.kt b/app/src/main/java/com/lizongying/mytv/api/YSPTokenService.kt index 3188708..7d08cd1 100644 --- a/app/src/main/java/com/lizongying/mytv/api/YSPTokenService.kt +++ b/app/src/main/java/com/lizongying/mytv/api/YSPTokenService.kt @@ -2,10 +2,12 @@ package com.lizongying.mytv.api import retrofit2.Call import retrofit2.http.GET +import retrofit2.http.Query interface YSPTokenService { @GET("my-tv/v1/info") fun getInfo( + @Query("token") token: String = "", ): Call } \ No newline at end of file diff --git a/app/src/main/res/layout/row.xml b/app/src/main/res/layout/row.xml index 6294a3e..8ddde43 100644 --- a/app/src/main/res/layout/row.xml +++ b/app/src/main/res/layout/row.xml @@ -2,7 +2,7 @@ #30000000 #000 #FFF + #7F000000 \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 5a06b81..5f914ab 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -10,8 +10,8 @@