Android自定义侧滑Item

源码地址:https://github.com/LanSeLianMa/CustomizeView/tree/master/cehuaitem

使用方式一:XML布局中直接使用

XML 复制代码
<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <com.test.festec.cehuaitem.widget.SideslipContainer
        android:id="@+id/sideslip_container01"
        android:layout_width="match_parent"
        android:layout_height="70dp"
        app:option_width="65dp">

        <com.test.festec.cehuaitem.widget.SideslipContent
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <TextView
                android:background="@color/d5"
                android:paddingTop="20dp"
                android:textAlignment="center"
                android:textSize="20sp"
                android:text="content"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />

        </com.test.festec.cehuaitem.widget.SideslipContent>

        <com.test.festec.cehuaitem.widget.SideslipOption
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/blue"
            android:tag="add"
            android:text="增加" />

        <com.test.festec.cehuaitem.widget.SideslipOption
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/orange"
            android:tag="edit"
            android:text="编辑" />

        <com.test.festec.cehuaitem.widget.SideslipOption
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/red"
            android:tag="delete"
            android:text="删除" />

    </com.test.festec.cehuaitem.widget.SideslipContainer>


</LinearLayout>

Activity中监听

Kotlin 复制代码
class MainActivity : Activity() {

    private lateinit var binding: ActivityMainBinding

    private var options = mutableListOf("增加", "编辑", "删除")
    private var optionBg = mutableListOf(R.color.blue, R.color.orange, R.color.red)

    @SuppressLint("UseCompatLoadingForDrawables")
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.sideslipContainer01.addOnClickListener(object : SideslipContainer.SideslipContainerOnClick {

            override fun optionOnClick(optionTag: Any) {
                Toast.makeText(this@MainActivity,"optionTag:$optionTag", Toast.LENGTH_SHORT).show()
                binding.sideslipContainer01.sideslipRecover()
            }

            override fun contentOnClick() {
                Toast.makeText(this@MainActivity,"content", Toast.LENGTH_SHORT).show()
                binding.sideslipContainer01.sideslipRecover()
            }

        })

    }

}

使用方式二:代码方式使用

先定义一个容器

XML 复制代码
<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">


    <LinearLayout
        android:id="@+id/container"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical" />


</LinearLayout>

Activity中动态添加

Kotlin 复制代码
class MainActivity : Activity() {

    private lateinit var binding: ActivityMainBinding

    private var options = mutableListOf("增加", "编辑", "删除")
    private var optionBg = mutableListOf(R.color.blue, R.color.orange, R.color.red)

    @SuppressLint("UseCompatLoadingForDrawables")
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 创建侧滑容器,配置基础参数
        val sideslip = SideslipContainer(this, DensityUtil.dp2px(this, 65f))
        val layoutParams = LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.MATCH_PARENT,
            DensityUtil.dp2px(this, 70f)
        )
        sideslip.layoutParams = layoutParams

        // 创建侧滑内容
        val content = SideslipContent(this).apply {
            background = resources.getDrawable(R.color.black,null)
        }

        // 加入侧滑容器中
        sideslip.addView(content)

        // 创建选项卡,并加入侧滑容器中
        options.forEachIndexed { index, str ->
            val option = SideslipOption(this,str)
            option.text = str
            option.background = resources.getDrawable(optionBg[index], null)
            sideslip.addView(option)
        }

        // 点击监听
        sideslip.addOnClickListener(object : SideslipContainer.SideslipContainerListener {
            override fun optionOnClick(optionTag: Any) {
                Log.e("TAG","optionTag:$optionTag")
            }

            override fun contentOnClick() {
                Log.e("TAG","content")
            }
        })

        binding.container.addView(sideslip)

    }

}

使用方式三:结合RecyclerView使用

重写LayoutManager

Kotlin 复制代码
// 重写LayoutManager,动态让RecyclerView 禁止/恢复 Y轴滚动
open class CustomLinerLayoutManager(context: Context) : LinearLayoutManager(context) {

    private var isScrollEnabled = true

    fun getEnabled(): Boolean {
        return isScrollEnabled
    }

    fun setScrollEnabled(flag: Boolean) {
        isScrollEnabled = flag
    }

    override fun canScrollVertically(): Boolean {
        return isScrollEnabled && super.canScrollVertically();
    }

}

重写RecyclerView

Kotlin 复制代码
class MyRecyclerView : RecyclerView {

    constructor(context: Context) : super(context)
    constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet)

    init {
        addOnScrollListener(object : RecyclerView.OnScrollListener() {

            /**
             *
             * public static final int SCROLL_STATE_IDLE = 0;  :  RecyclerView 当前未滚动。
             *
             * public static final int SCROLL_STATE_DRAGGING = 1;  :  RecyclerView 当前正在被外部输入(例如用户触摸输入)拖动。
             *
             * public static final int SCROLL_STATE_SETTLING = 2;  :  RecyclerView 当前正在动画到最终位置,而不是在外部控制。
             */
            override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
                if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
                    childrenRecover()
                }
            }
        })
    }

    // view初始化
    private var viewInit = false

    // 上一会触摸的子View
    var originalChild: SideslipContainer? = null

    // 当前触摸的子View
    var currentChild: SideslipContainer? = null

    private var customLayoutManager: CustomLinerLayoutManager? = null

    private var childMoveCallback = object : ChildOnTouchCallback {
        override fun currentChildMove() {
            childrenRecover()
        }

        override fun originalChild(originalSideslip: SideslipContainer?) {
            originalChild = originalSideslip
        }

        override fun currentChild(currentContainer: SideslipContainer?) {
            currentChild = currentContainer
        }

        override fun listStopYScroll() {
            // Log.e("TAG", "List停止滚动")
            if (customLayoutManager!!.getEnabled()) {
                customLayoutManager?.setScrollEnabled(false)
            }
        }

        override fun listRecoverYScroll() {
            // Log.e("TAG", "List恢复滚动")
            if (!(customLayoutManager!!.getEnabled())) {
                customLayoutManager?.setScrollEnabled(true)
            }
        }

    }

    // 子View恢复
    private fun childrenRecover() {
        children.forEach {
            (it as SideslipContainer).sideslipRecover()
        }
    }

    override fun onViewAdded(child: View?) {
        // Log.e("TAG","onViewAdded")
        val sideslipContainer = (child as SideslipContainer)
        sideslipContainer.addOnChildMoveCallback(childMoveCallback)
    }

    // 当复用item,彻底超出屏幕,不可见时执行
    override fun onViewRemoved(child: View?) {
        // Log.e("TAG","onViewRemoved")
        (child as SideslipContainer).sideslipStateRest()
    }

    override fun onWindowFocusChanged(hasWindowFocus: Boolean) {
        // Log.e("TAG","onWindowFocusChanged")
        super.onWindowFocusChanged(hasWindowFocus)
        if (!viewInit) {
            customLayoutManager = (layoutManager as CustomLinerLayoutManager)
        }
        viewInit = true
    }

    interface ChildOnTouchCallback {

        // 有子View侧滑了
        fun currentChildMove()

        // 上一个触摸的子View
        fun originalChild(originalSideslip: SideslipContainer?)

        // 当前触摸的子View
        fun currentChild(currentContainer: SideslipContainer?)

        // 列表停止Y轴滚动
        fun listStopYScroll()

        // 列表恢复Y轴滚动
        fun listRecoverYScroll()

    }

}

RecyclerView适配器

Kotlin 复制代码
class MyListAdapter(
    var context: Context,
    var data: MutableList<Info>
) : RecyclerView.Adapter<MyListAdapter.MyViewHolder>() {

    private var options = mutableListOf("增加", "编辑", "删除")
    private var optionBg =
        mutableListOf(R.color.blue, R.color.orange, R.color.red)

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val itemView = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false)
        val sideslip = sideslipContainer(itemView)
        return MyViewHolder(sideslip)
    }

    @SuppressLint("UseCompatLoadingForDrawables", "ResourceAsColor")
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val sideslip = holder.itemView as SideslipContainer

        // 根据不同权限,添加不同的选项卡
        val optionsView = mutableListOf<SideslipOption>()
        if (data[position].level == 0) {
            optionsView.clear()
        } else if (data[position].level == 1) {
            val option = SideslipOption(context, options[1])
            option.text = options[1]
            option.background = context.resources.getDrawable(optionBg[1], null)
            optionsView.add(option)
        } else {
            options.forEachIndexed { index, str ->
                val option = SideslipOption(context, str)
                option.text = str
                option.background = context.resources.getDrawable(optionBg[index], null)
                optionsView.add(option)
            }
        }
        sideslip.addMultipleOption(optionsView)

        // 点击回调
        sideslip.addOnClickListener(object : SideslipContainer.SideslipContainerOnClick {
            override fun optionOnClick(optionTag: Any) {
                Toast.makeText(context,"${holder.adapterPosition} -- optionTag:$optionTag",Toast.LENGTH_SHORT).show()
                sideslip.sideslipRecover()
            }

            override fun contentOnClick() {
                Toast.makeText(context,"${holder.adapterPosition} - content",Toast.LENGTH_SHORT).show()
                sideslip.sideslipRecover()
            }
        })
        holder.idTv.text = data[position].id.toString()
        holder.titleTv.text = data[position].title
        holder.describeTv.text = data[position].describe
    }

    override fun getItemCount(): Int {
        return data.size
    }

    @SuppressLint("UseCompatLoadingForDrawables")
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    private fun sideslipContainer(itemView: View): SideslipContainer {
        // 创建侧滑容器,配置基础参数
        val sideslip = SideslipContainer(context)
        sideslip.setOptionWidth(DensityUtil.dp2px(context, 65f))

        val layoutParams = LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.MATCH_PARENT,
            DensityUtil.dp2px(context, 70f)
        )
        sideslip.layoutParams = layoutParams

        // 创建侧滑内容
        val content = SideslipContent(context)
        content.addView(itemView)

        // 加入侧滑容器中
        sideslip.addView(content)

        // 创建选项卡,并加入侧滑容器中
        // options.forEachIndexed { index, str ->
        //    val option = SideslipOption(context, str)
        //    option.text = str
        //    option.background = context.resources.getDrawable(optionBg[index], null)
        //    sideslip.addView(option)
        // }
        return sideslip
    }

    class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        var idTv: TextView
        var titleTv: TextView
        var describeTv: TextView

        init {
            idTv = itemView.findViewById(R.id.id_tv)
            titleTv = itemView.findViewById(R.id.title_tv)
            describeTv = itemView.findViewById(R.id.describe_tv)
        }
    }
}

Activity中绑定数据

Kotlin 复制代码
class ListActivity : Activity() {

    private lateinit var binding: ListLayoutBinding

    private val data: MutableList<Info> = mutableListOf(
        Info(0, "title", "content", 2),
        Info(1, "title", "content", 1),
        Info(2, "title", "content", 2),
        Info(3, "title", "content", 2),
        Info(4, "title", "content", 2),
        Info(5, "title", "content", 1),
        Info(6, "title", "content", 1),
        Info(7, "title", "content", 2),
        Info(8, "title", "content", 2),
        Info(9, "title", "content", 1),
        Info(10, "title", "content", 1),
        Info(11, "title", "content", 2),
        Info(12, "title", "content", 2),
        Info(13, "title", "content", 1),
        Info(14, "title", "content", 1),
        Info(15, "title", "content", 2),
        Info(16, "title", "content", 2),
        Info(17, "title", "content", 2),
        Info(18, "title", "content", 2),
        Info(19, "title", "content", 1),
        Info(20, "title", "content", 2),
        Info(21, "title", "content", 1),
        Info(22, "title", "content", 2),
        Info(23, "title", "content", 2),
        Info(24, "title", "content", 2),
        Info(25, "title", "content", 1),
        Info(26, "title", "content", 2),
        Info(27, "title", "content", 1),
        Info(28, "title", "content", 2),
        Info(29, "title", "content", 2),
        Info(30, "title", "content", 2),
        Info(31, "title", "content", 1),
        Info(32, "title", "content", 1),
        Info(33, "title", "content", 2),
        Info(34, "title", "content", 2),
        Info(35, "title", "content", 1),
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ListLayoutBinding.inflate(layoutInflater)
        setContentView(binding.root)
        val adapter = MyListAdapter(this, data)
        val linearLayoutManager = CustomLinerLayoutManager(this)
        binding.root.layoutManager = linearLayoutManager
        binding.root.adapter = adapter
    }

}
相关推荐
许杰小刀3 小时前
ctfshow-web文件包含(web78-web86)
android·前端·android studio
为何创造硅基生物4 小时前
ESP32 IDF 编译时出现gitee 登录,导致编译报错
gitee
恋猫de小郭9 小时前
Android 上为什么主题字体对 Flutter 不生效,对 Compose 生效?Flutter 中文字体问题修复
android·前端·flutter
三少爷的鞋9 小时前
不要让调用方承担你本该承担的复杂度 —— Android Data 层设计原则
android
李李李勃谦9 小时前
Flutter 框架跨平台鸿蒙开发 - 创意灵感收集
android·flutter·harmonyos
fengci.10 小时前
ctfshow其他(web396-web407)
android
JJay.10 小时前
Android 17 大屏适配变化解
android
TE-茶叶蛋11 小时前
结合登录页-PHP基础知识点解析
android·开发语言·php
alexhilton11 小时前
Jetpack Compose元球边缘效果
android·kotlin·android jetpack