diff --git a/MXAdapt/src/main/java/com/mx/adapt/MXBaseSimpleAdapt.kt b/MXAdapt/src/main/java/com/mx/adapt/MXBaseSimpleAdapt.kt index 9db181283ea26d7382b4414321ffe339ff9e50b9..2cceddceb1f7688274a0e76be69956467ad57c59 100644 --- a/MXAdapt/src/main/java/com/mx/adapt/MXBaseSimpleAdapt.kt +++ b/MXAdapt/src/main/java/com/mx/adapt/MXBaseSimpleAdapt.kt @@ -53,6 +53,11 @@ abstract class MXBaseSimpleAdapt(val list: ArrayList = arrayListOf()) : } catch (e: Exception) { e.printStackTrace() } + try { + bindView(position, holder.binding) + } catch (e: Exception) { + e.printStackTrace() + } } /** @@ -77,7 +82,8 @@ abstract class MXBaseSimpleAdapt(val list: ArrayList = arrayListOf()) : * 绑定View * @param position 绑定View的位置 */ - abstract fun bindView(position: Int, binding: ViewBinding, record: T) + open fun bindView(position: Int, binding: ViewBinding, record: T) {} + open fun bindView(position: Int, binding: ViewBinding) {} /** * Item点击回调 diff --git a/MXAdapt/src/main/java/com/mx/adapt/scroller/IMXSelect.kt b/MXAdapt/src/main/java/com/mx/adapt/scroller/IMXSelect.kt new file mode 100644 index 0000000000000000000000000000000000000000..3d1f14bbcbcd0fd6a7ef03d354a47f2ef85ddeab --- /dev/null +++ b/MXAdapt/src/main/java/com/mx/adapt/scroller/IMXSelect.kt @@ -0,0 +1,5 @@ +package com.mx.adapt.scroller + +interface IMXSelect { + fun onSelect(index: Int) +} \ No newline at end of file diff --git a/MXAdapt/src/main/java/com/mx/adapt/scroller/MXScrollerAdapt.kt b/MXAdapt/src/main/java/com/mx/adapt/scroller/MXScrollerAdapt.kt new file mode 100644 index 0000000000000000000000000000000000000000..2303b954361ea44383e1a7137ac8a030f0520f79 --- /dev/null +++ b/MXAdapt/src/main/java/com/mx/adapt/scroller/MXScrollerAdapt.kt @@ -0,0 +1,66 @@ +package com.mx.adapt.scroller + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import androidx.viewbinding.ViewBinding +import com.mx.adapt.MXBaseSimpleAdapt +import com.mx.adapt.MXBaseViewHolder +import com.mx.adapt.databinding.LayoutMxScrollerSpaceBinding + +class MXScrollerAdapt(val scrollView: MXScrollerView) : MXBaseSimpleAdapt() { + var adapt: MXBaseSimpleAdapt<*>? = null + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MXBaseViewHolder { + if (viewType != Int.MAX_VALUE / 2) { + adapt?.onCreateViewHolder(parent, viewType)?.let { + return it + } + } + return MXBaseViewHolder( + LayoutMxScrollerSpaceBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ), viewType + ) + } + + override fun createItem( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): ViewBinding { + if (viewType != Int.MAX_VALUE / 2) { + adapt?.createItem(inflater, parent, viewType)?.let { + return it + } + } + return LayoutMxScrollerSpaceBinding.inflate(inflater, parent, false) + } + + override fun onBindViewHolder(holder: MXBaseViewHolder, position: Int) { + if (position >= scrollView.itemOffset && position < (itemCount - scrollView.itemOffset)) { + adapt?.onBindViewHolder(holder, position - scrollView.itemOffset) + } + val lp = (holder.binding.root.layoutParams as RecyclerView.LayoutParams) + lp.height = scrollView.itemHeight + holder.binding.root.layoutParams = lp + holder.itemView.setOnClickListener { + itemClickCall?.invoke(position, Any()) + } + } + + override fun getItemViewType(position: Int): Int { + val maxCount = itemCount + if (position < scrollView.itemOffset || position >= (maxCount - scrollView.itemOffset)) { + return Int.MAX_VALUE / 2 + } + + return super.getItemViewType(position) + } + + override fun getItemCount(): Int { + return (adapt?.itemCount ?: 0) + scrollView.itemOffset * 2 + } +} \ No newline at end of file diff --git a/MXAdapt/src/main/java/com/mx/adapt/scroller/MXScrollerView.kt b/MXAdapt/src/main/java/com/mx/adapt/scroller/MXScrollerView.kt new file mode 100644 index 0000000000000000000000000000000000000000..9710b8cd3c7296ab35011e72c6cd6b5ebad39b2d --- /dev/null +++ b/MXAdapt/src/main/java/com/mx/adapt/scroller/MXScrollerView.kt @@ -0,0 +1,143 @@ +package com.mx.adapt.scroller + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.RectF +import android.util.AttributeSet +import android.view.ViewTreeObserver +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.LinearSnapHelper +import androidx.recyclerview.widget.RecyclerView +import com.mx.adapt.MXBaseSimpleAdapt +import kotlin.math.max + +class MXScrollerView @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null +) : RecyclerView(context, attrs) { + private val manager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) + private var dashColor: Int? = Color.parseColor("#33999999") + private var itemSize = 5 + internal val itemOffset: Int + get() = (itemSize - 1) / 2 + internal val itemHeight get() = getAllHeight() / itemSize + + + private var itemSelect: IMXSelect? = null + private val delegateAdapt = MXScrollerAdapt(this) + + init { + LinearSnapHelper().attachToRecyclerView(this) + layoutManager = manager + adapter = delegateAdapt + delegateAdapt.setItemClick { position, _ -> + manager.scrollToPositionWithOffset(position - itemOffset, 0) + notifySelectChange(position - itemOffset) + } + addOnScrollListener(object : OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + if (newState == SCROLL_STATE_IDLE) { + notifySelectChange(manager.findFirstCompletelyVisibleItemPosition()) + } + } + + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + notifySelectChange(manager.findFirstCompletelyVisibleItemPosition()) + } + }) + + viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener { + override fun onPreDraw(): Boolean { + delegateAdapt?.notifyDataSetChanged() + viewTreeObserver.removeOnPreDrawListener(this) + return true + } + }) + } + + override fun onDraw(c: Canvas?) { + super.onDraw(c) + dashColor?.let { dash -> + val itemHeight = itemHeight + val itemOffset = itemOffset + c?.drawRect( + RectF( + 0f, + itemHeight * itemOffset.toFloat(), + width.toFloat(), + itemHeight * (itemOffset + 1).toFloat() + ), + Paint().apply { + color = dash + style = Paint.Style.FILL + }) + } + } + + private fun getAllHeight() = height - paddingTop - paddingBottom + + fun setDashColor(color: Int) { + dashColor = color + postInvalidate() + } + + fun setItemSize(size: Int) { + if (size % 2 == 0) { + throw Exception("数量不能为双数!") + } + itemSize = size + delegateAdapt.notifyDataSetChanged() + } + + fun setAdapt(adapt: MXBaseSimpleAdapt<*>) { + adapt.registerAdapterDataObserver(TypeObserver()) + delegateAdapt.adapt = adapt + } + + fun setSelectIndex(index: Int) { + val position = max(0, index) + manager.scrollToPositionWithOffset(position, position) + post { manager.scrollToPositionWithOffset(position, position) } + notifySelectChange(position) + } + + fun setOnSelectListener(listener: IMXSelect?) { + itemSelect = listener + notifySelectChange(manager.findFirstCompletelyVisibleItemPosition() - itemOffset) + } + + private inner class TypeObserver : AdapterDataObserver() { + override fun onChanged() { + delegateAdapt.notifyDataSetChanged() + } + + override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { + delegateAdapt.notifyItemRangeInserted(positionStart + itemOffset, itemCount) + } + + override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) { + delegateAdapt.notifyItemRangeRemoved(positionStart + itemOffset, itemCount) + } + + override fun onItemRangeChanged(positionStart: Int, itemCount: Int) { + delegateAdapt.notifyItemRangeChanged(positionStart + itemOffset, itemCount) + } + + override fun onItemRangeChanged(positionStart: Int, itemCount: Int, payload: Any?) { + delegateAdapt.notifyItemRangeChanged(positionStart + itemOffset, itemCount, payload) + } + + override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) { + delegateAdapt.notifyItemMoved(fromPosition + itemOffset, toPosition + itemOffset) + } + } + + private var currentSelectIndex = -1 + private fun notifySelectChange(index: Int) { + val targetIndex = if (index < 0) 0 else index + if (targetIndex == currentSelectIndex) return + currentSelectIndex = targetIndex + itemSelect?.onSelect(targetIndex) + } +} \ No newline at end of file diff --git a/MXAdapt/src/main/res/layout/layout_mx_scroller_space.xml b/MXAdapt/src/main/res/layout/layout_mx_scroller_space.xml new file mode 100644 index 0000000000000000000000000000000000000000..64dd827a523c1e1509a55e9dcf6055745d3e253c --- /dev/null +++ b/MXAdapt/src/main/res/layout/layout_mx_scroller_space.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 45e96954d1afc17b6682d542731a45adf2a06d3d..16cc61b326305e0548479891c27f9c66f4cb2a03 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,6 +20,7 @@ + diff --git a/app/src/main/java/com/mx/recycle_adapt/MainActivity.kt b/app/src/main/java/com/mx/recycle_adapt/MainActivity.kt index fa816a3669bad64b673e5407fd99223e94344343..d0d5e649aed82afcce40e7e30142f086744da5a5 100644 --- a/app/src/main/java/com/mx/recycle_adapt/MainActivity.kt +++ b/app/src/main/java/com/mx/recycle_adapt/MainActivity.kt @@ -7,6 +7,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.mx.recycle_adapt.activity.AnyTypeActivity import com.mx.recycle_adapt.activity.BannerActivity import com.mx.recycle_adapt.activity.IndexedActivity +import com.mx.recycle_adapt.activity.ScrollerViewActivity import com.mx.recycle_adapt.adapt.HomeAdapt import kotlinx.android.synthetic.main.activity_main.* @@ -19,6 +20,7 @@ class MainActivity : AppCompatActivity() { val adapt = HomeAdapt( arrayListOf( HomePages("普通的一个RecycleView", IndexedActivity::class.java), + HomePages("滚动选择View", ScrollerViewActivity::class.java), HomePages("多类型RecycleView", AnyTypeActivity::class.java), HomePages("Banner", BannerActivity::class.java), ) diff --git a/app/src/main/java/com/mx/recycle_adapt/activity/ScrollerViewActivity.kt b/app/src/main/java/com/mx/recycle_adapt/activity/ScrollerViewActivity.kt new file mode 100644 index 0000000000000000000000000000000000000000..79ccf02abb7c1d6e897f1ed7f50bfbf242db2509 --- /dev/null +++ b/app/src/main/java/com/mx/recycle_adapt/activity/ScrollerViewActivity.kt @@ -0,0 +1,64 @@ +package com.mx.recycle_adapt.activity + +import android.graphics.Color +import android.os.Bundle +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.viewbinding.ViewBinding +import com.mx.adapt.MXBaseSimpleAdapt +import com.mx.adapt.scroller.IMXSelect +import com.mx.recycle_adapt.R +import com.mx.recycle_adapt.databinding.AdaptSimpleIndexBinding +import kotlinx.android.synthetic.main.activity_scroller_view.* + +class ScrollerViewActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_scroller_view) + val adapt = SpanCountAdapt() + adapt.list.addAll((0 until 13)) + adapt.notifyDataSetChanged() + + scrollerView.setItemSize(5) + scrollerView.setAdapt(adapt) + + var currentIndex = 0 + scrollerView.setOnSelectListener(object : IMXSelect { + override fun onSelect(index: Int) { + infoTxv.text = index.toString() + currentIndex = index + } + }) + scrollerView.setSelectIndex(10) + scrollerView.setDashColor(Color.parseColor("#33465FCD")) + + addTxv.setOnClickListener { + adapt.list.add(currentIndex, currentIndex) + adapt.notifyDataSetChanged() + } + } + + + private class SpanCountAdapt : MXBaseSimpleAdapt() { + init { + setItemClick { position, record -> + list.removeAt(position) + notifyItemRemoved(position) + } + } + + override fun createItem( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): AdaptSimpleIndexBinding { + return AdaptSimpleIndexBinding.inflate(inflater, parent, false) + } + + override fun bindView(position: Int, binding: ViewBinding, record: Int) { + binding as AdaptSimpleIndexBinding + binding.indexTxv.text = "" + (record + 1) + } + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_scroller_view.xml b/app/src/main/res/layout/activity_scroller_view.xml new file mode 100644 index 0000000000000000000000000000000000000000..29a6a5705c59369d5b36c69e8796dcdb422f79ec --- /dev/null +++ b/app/src/main/res/layout/activity_scroller_view.xml @@ -0,0 +1,30 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/adapt_simple_index.xml b/app/src/main/res/layout/adapt_simple_index.xml index cd1849439f825cbb0e292b321b240104c06b2782..bf39d6d8a4cdb6b0d610be74c2516b1848d39ad5 100644 --- a/app/src/main/res/layout/adapt_simple_index.xml +++ b/app/src/main/res/layout/adapt_simple_index.xml @@ -7,8 +7,8 @@ android:id="@+id/indexTxv" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_gravity="center" android:gravity="center" - android:padding="20dp" android:text="1" android:textColor="#000000" android:textSize="16sp" /> diff --git a/local.properties b/local.properties index 76366c1c1662f856ef18afc6d0b67b1800c26584..ccc0961b641cc0e2d00cddd1a57f31c53ded4b3f 100644 --- a/local.properties +++ b/local.properties @@ -4,5 +4,5 @@ # Location of the SDK. This is only used by Gradle. # For customization when using a Version Control System, please read the # header note. -#Mon Mar 13 19:28:20 CST 2023 -sdk.dir=D\:\\DevTools\\AndroidSDK +#Wed Mar 29 16:24:36 CST 2023 +sdk.dir=D\:\\DevTools\\WinAndroidSdk