package com.example.fluidcolorfulframe
import android.content.Contextimport android.graphics.Canvasimport android.graphics.drawable.Drawableimport android.util.AttributeSetimport androidx.appcompat.widget.AppCompatImageViewimport androidx.core.view.setPadding/** * * @author wangzhichao * @since 2022/9/4 /class MyImageView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null) : AppCompatImageView(context, attrs) { private val drawable = FluidColorfulFrameDrawable() init { setPadding(5.dp.toInt()) drawable.callback = this } override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) drawable.setBounds(0, 0, w, h) drawable.startFluid() } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) drawable.draw(canvas) } override fun onDetachedFromWindow() { super.onDetachedFromWindow() drawable.cancelFluid() } override fun invalidateDrawable(dr: Drawable) { super.invalidateDrawable(dr) if (dr === drawable) { invalidate() } }}package com.example.fluidcolorfulframeimport android.animation.ObjectAnimatorimport android.animation.ValueAnimatorimport android.graphics.import android.graphics.drawable.Drawableimport android.util.Logimport android.view.animation.LinearInterpolatorimport androidx.core.graphics.toColorInt/ * * @author wangzhichao * @since 2022/9/4 /private const val TAG = "FluidColorfulFrame"class FluidColorfulFrameDrawable : Drawable() { private val paint = Paint(Paint.ANTI_ALIAS_FLAG) private lateinit var bounds: RectF private val rectF = RectF() private val defaultRadius: Float = 10.dp private val defaultStrokeWidth: Float = 5.dp private val colors: IntArray private val positions: FloatArray private val mtx = Matrix() private var degree: Float = 0f set(value) { field = value Log.d(TAG, “degree setter called”) invalidateSelf() } init { paint.style = Paint.Style.STROKE paint.strokeWidth = defaultStrokeWidth colors = intArrayOf( “#FF0000FF”.toColorInt(), // 蓝 0f “#FF000000”.toColorInt(), // 黑 0.02f “#FF000000”.toColorInt(), // 黑 0.25f “#FFFF0000”.toColorInt(), // 红 0.27f “#FFFF0000”.toColorInt(), // 红 0.37f “#FF00FF00”.toColorInt(), // 绿 0.39f “#FF0000FF”.toColorInt(), // 蓝 0.49f “#FFFFFF00”.toColorInt(), // 黄 0.51f “#FF000000”.toColorInt(), // 黑 0.53f “#FF000000”.toColorInt(), // 黑 0.75f “#FFFF0000”.toColorInt(), // 红 0.77f “#FFFF0000”.toColorInt(), // 红 0.87f “#FFFFFF00”.toColorInt(), // 黄 0.91f “#FF0000FF”.toColorInt(), // 蓝 0.96f ) positions = floatArrayOf( 0f, 0.02f, 0.25f, 0.27f, 0.37f, 0.39f, 0.49f, 0.51f, 0.53f, 0.75f, 0.77f, 0.87f, 0.91f, 0.96f, ) } override fun setBounds(left: Int, top: Int, right: Int, bottom: Int) { super.setBounds(left, top, right, bottom) bounds = RectF(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat()) rectF.left = defaultStrokeWidth / 2 rectF.top = defaultStrokeWidth / 2 rectF.right = bounds.width() - defaultStrokeWidth / 2 rectF.bottom = bounds.height() - defaultStrokeWidth / 2 paint.shader = SweepGradient(bounds.centerX(), bounds.centerY(), colors, positions) } override fun draw(canvas: Canvas) { Log.d(TAG, "draw: ") mtx.reset() mtx.setRotate(degree, bounds.centerX(), bounds.centerY()) (paint.shader as SweepGradient).setLocalMatrix(mtx) canvas.drawRoundRect(rectF, defaultRadius, defaultRadius, paint) } override fun setAlpha(alpha: Int) { paint.alpha = alpha } override fun setColorFilter(colorFilter: ColorFilter?) { paint.colorFilter = colorFilter } override fun getOpacity(): Int { return PixelFormat.TRANSLUCENT } private var fluidAnim: ObjectAnimator? = null fun startFluid() { fluidAnim = ObjectAnimator.ofFloat(this, “degree”, 0f, 360f).apply { duration = 2000L interpolator = LinearInterpolator() repeatCount = ValueAnimator.INFINITE start() } } fun cancelFluid() { fluidAnim?.cancel() }}package com.example.fluidcolorfulframeimport android.content.res.Resourcesimport android.util.TypedValue/* * * @author wangzhichao * @since 2022/9/4 */val Float.dp get() = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, this, Resources.getSystem().displayMetrics)val Int.dp get() = this.toFloat().dp
class VideoClipAdapter(private val videoClipList: List) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
companion object {
const val VIEW_TYPE_ITEM = 0
const val VIEW_TYPE_DATE_SEPARATOR = 1
const val VIEW_TYPE_BUTTON = 2
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
VIEW_TYPE_ITEM -> {
VideoClipViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_video_clip, parent, false))
}
VIEW_TYPE_DATE_SEPARATOR -> {
DateSeparatorViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_date_separator, parent, false))
}
VIEW_TYPE_BUTTON -> {
ButtonViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_button, parent, false))
}
else -> throw IllegalArgumentException("Unknown view type")
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = getItemList()[position]
when (getItemViewType(position)) {
VIEW_TYPE_ITEM -> {
val videoClip = item as VideoClipWithTime
holder as VideoClipViewHolder
holder.bind(videoClip)
}
VIEW_TYPE_DATE_SEPARATOR -> {
val videoClip = item as VideoClipWithTime
holder as DateSeparatorViewHolder
holder.bind(videoClip)
}
VIEW_TYPE_BUTTON -> {
val buttonItem = item as ButtonItem
holder as ButtonViewHolder
holder.bind(buttonItem)
}
}
}
override fun getItemViewType(position: Int): Int {
return when (val item = getItemList()[position]) {
is VideoClipWithTime -> {
if (item.type == ItemType.DATE_SEPARATOR) VIEW_TYPE_DATE_SEPARATOR else VIEW_TYPE_ITEM
}
is ButtonItem -> VIEW_TYPE_BUTTON
else -> throw IllegalArgumentException("Unknown item type")
}
}
override fun getItemCount(): Int {
return getItemList().size
}
// 获取需要显示的所有项(考虑日期分隔符和按钮)
private fun getItemList(): List<Any> {
val items = mutableListOf<Any>()
val groupedItems = videoClipList.groupBy { it.dateText } // 按日期分组
val visibleItemsMap = mutableMapOf<String, Int>() // 每次获取列表时,使用局部的 visibleItemsMap
groupedItems.forEach { (_, group) ->
// 先添加日期分隔符
items.add(VideoClipWithTime(ItemType.DATE_SEPARATOR, group[0].dateText, NormalItem("", "")))
// 获取当前日期下的可见项数量
var visibleCount = visibleItemsMap[group[0].dateText] ?: maxSingle
val visibleItems = group.take(visibleCount)
items.addAll(visibleItems)
// 如果项数超过 `maxSingle`,则添加一个按钮
if (group.size > visibleCount) {
items.add(ButtonItem(group[0].dateText))
}
// 更新每组的可见项数量
visibleItemsMap[group[0].dateText] = visibleCount
}
return items
}
// 增加更多项的逻辑
private fun expandItems(group: List<VideoClipWithTime>, dateText: String) {
val visibleCount = maxSingle
val newVisibleCount = (visibleCount + plusSingle).coerceAtMost(group.size)
notifyDataSetChanged()
}
// ViewHolder for VideoClipWithTime (item)
inner class VideoClipViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private val nameTextView: TextView = view.findViewById(R.id.nameTextView)
private val timeTextView: TextView = view.findViewById(R.id.timeTextView)
fun bind(videoClip: VideoClipWithTime) {
nameTextView.text = videoClip.bean.name
timeTextView.text = videoClip.bean.time
}
}
// ViewHolder for DateSeparator (item)
inner class DateSeparatorViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private val dateTextView: TextView = view.findViewById(R.id.dateTextView)
fun bind(videoClip: VideoClipWithTime) {
dateTextView.text = videoClip.dateText
}
}
// ViewHolder for Button
inner class ButtonViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val button: Button = view.findViewById(R.id.expand_button)
fun bind(buttonItem: ButtonItem) {
button.text = buttonItem.text
button.setOnClickListener {
// 获取当前日期的所有项
val group = videoClipList.filter { it.dateText == buttonItem.text }
expandItems(group, buttonItem.text)
}
}
}
}
8819

被折叠的 条评论
为什么被折叠?



