Android无限层扩展多级recyclerview列表+实时搜索弹窗

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/2767499cd5434afea18c885d0f54a1ae.png
业务逻辑:
点击选择,弹出弹窗,列表数据由后台提供,不限层级,可叠加无限层子级;
点击item展开收起,点击尾部icon单选选中,点击[确定]为最终选中,收起弹窗;
搜索框输入字符时,实时检索数据显示;
搜索出的列表点击直接选中,收起弹窗。

弹窗代码:

class DepartListDialog {class Builder(context: Context) : BasicDialog.Builder<Builder>(context) {private val inputView: EditText? by lazy { findViewById(R.id.et_input) }private val titleView: TextView? by lazy { findViewById(R.id.tv_title) }private val leftView: TextView? by lazy { findViewById(R.id.tv_left) }private val rightView: TextView? by lazy { findViewById(R.id.tv_right) }private val clearView: ImageView? by lazy { findViewById(R.id.iv_del) }private val llEmpty: LinearLayout? by lazy { findViewById(R.id.ll_empty) }private var onLeftClick: ((BasicDialog) -> Unit)? = nullprivate var onRightClick: ((String, BasicDialog) -> Unit)? = nullprivate var isFilterEmail = falseprivate var query = ""private var onItemClick: ((DepartModel, Int, BasicDialog?) -> Unit)? = nullprivate var onItemSelectClick: ((View, Int, DepartModel) -> Unit)? = nullprivate var onSearchItemClick: ((ArrayList<String>, DepartModel, Int, BasicDialog?) -> Unit)? = nullprivate var onInputListener: ((String, BasicDialog?) -> Unit)? = nullprivate val rvSearch: RecyclerView? by lazy { findViewById(R.id.rv_search) }private val recyclerView: RecyclerView? by lazy { findViewById(R.id.recycler_view) }private var recyclerAdapter: DepartTreeAdapter? = nullprivate var treeItemList = arrayListOf<DepartModel>()private val rvSearchAdapter: CommonAdapter<DepartModel>? by lazy {CommonAdapter<DepartModel>(R.layout.item_string).apply {convert = { holder, position, item ->item?.let {var str = item.namevar cList = item.children ?: arrayListOf()while (cList.isNotEmpty()) {str += "/" + cList[0].namecList = cList[0].children ?: arrayListOf()}val tvName = holder.getView<TextView>(R.id.tv_name)setPartText(tvName, str, query,getColor(R.color.color_909090), getColor(R.color.color_303030))}}setOnItemClickListener { adapter, _, position ->adapter.getItem(position)?.let {val ids = arrayListOf<String>()var bean = itids.add(bean.id)var str = bean.namewhile (bean.children != null && bean.children?.isNotEmpty() == true) {bean = bean.children!![0]ids.add(bean.id)str += "/" + bean.name}val nameArr = str.split("/")if (nameArr.size > 2) {bean.name ="……/" + nameArr[nameArr.size - 2] + "/" + nameArr[nameArr.size - 1]} else {bean.name = str}onSearchItemClick?.invoke(ids, bean, position, getDialog())dismiss()}}}}init {setContentView(R.layout.dialog_common_input_data)setAnimStyle(AnimAction.ANIM_BOTTOM)setGravity(Gravity.BOTTOM)setCancelable(false)setCanceledOnTouchOutside(false)setOnClickListener(leftView, rightView, clearView)setRightBtnEnable(false)recyclerAdapter = DepartTreeAdapter(context, treeItemList)recyclerView?.adapter = recyclerAdapterrecyclerAdapter?.setOnItemClickListener(object : DepartTreeAdapter.OnItemClickListener {override fun onItemClick(view: View, position: Int, model: DepartModel) {
//                    showToast("当前点击的数据为 ${model.name}")}})recyclerAdapter?.onItemChildClickListener =object : DepartTreeAdapter.OnItemChildClickListener {override fun onClick(view: View, position: Int, model: DepartModel) {onItemSelectClick?.invoke(view, position, model)setRightBtnEnable(true)}}rvSearch?.adapter = rvSearchAdapterinputView?.addTextChangedListener(object : TextWatcher {override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}override fun afterTextChanged(p0: Editable?) {query = inputView?.text?.trim().toString()if (query.isEmpty()) {rvSearchAdapter?.submitList(arrayListOf())rvSearchAdapter?.notifyDataSetChanged()recyclerView?.visibility = View.VISIBLEval v = if ((recyclerView?.adapter?.itemCount?: 0) < 1) View.VISIBLE else View.GONEllEmpty?.visibility = v} else {onInputListener?.invoke(query, getDialog())recyclerView?.visibility = View.INVISIBLE}clearView?.isVisible = query.isNotEmpty()}})}fun setRightBtnEnable(enable: Boolean): Builder = apply {val color = if (enable) {if (UiUtil.isDarkMode(getContext())) getColor(R.color.color_E16C5F) else getColor(R.color.color_DD594A)} else {if (UiUtil.isDarkMode(getContext())) getColor(R.color.color_606060) else getColor(R.color.color_909090)}rightView?.setTextColor(color)rightView?.isEnabled = enable}fun setData(data: ArrayList<DepartModel>): Builder = apply {recyclerAdapter?.submitList(data)recyclerAdapter?.notifyDataSetChanged()val v = if (data.isEmpty()) View.VISIBLE else View.GONEllEmpty?.visibility = v}fun resetDefault() {recyclerAdapter?.setDefaultAdapter()}fun setSelectItem(index: Int): Builder = apply {recyclerAdapter?.notifyItemChanged(index)}fun setSearchData(data: ArrayList<DepartModel>): Builder = apply {rvSearchAdapter?.submitList(data)rvSearchAdapter?.notifyDataSetChanged()val v =if (data.isEmpty() && recyclerView?.visibility == View.INVISIBLE) View.VISIBLE else View.GONEllEmpty?.visibility = v}fun setSelectSearchItem(index: Int): Builder = apply {rvSearchAdapter?.notifyItemChanged(index)}fun setOnItemClick(onItemClick: ((DepartModel, Int, BasicDialog?) -> Unit)): Builder =apply {this.onItemClick = onItemClick}fun setOnItemSelectClick(onItemSelectClick: ((View, Int, DepartModel) -> Unit)): Builder =apply {this.onItemSelectClick = onItemSelectClick}fun setOnSearchItemClick(onSearchItemClick: ((ArrayList<String>, DepartModel, Int, BasicDialog?) -> Unit)): Builder =apply {this.onSearchItemClick = onSearchItemClick}fun setOnInputListener(onInputListener: ((String, BasicDialog?) -> Unit)): Builder =apply {this.onInputListener = onInputListener}override fun onClick(view: View) {when (view) {leftView -> {getDialog()?.let {onLeftClick?.invoke(it)}dismiss()}rightView -> {val input = inputView?.text?.toString()?.trim() ?: ""getDialog()?.let {onRightClick?.invoke(input, it)}dismiss()}clearView -> {inputView?.text?.clear()}}}// 设置标题fun setTitle(title: String): Builder = apply {titleView?.text = title}// 设置左边的文本fun setLeftText(leftStr: String): Builder = apply {leftView?.text = leftStr}// 设置右边的文本fun setRightText(rightStr: String): Builder = apply {rightView?.text = rightStr}// 设置输入类型fun setInputType(inputType: Int): Builder = apply {inputView?.inputType = inputType}// 设置输入最大长度fun setInputLength(maxLength: Int): Builder = apply {inputView?.filters = arrayOf(InputFilter.LengthFilter(maxLength))}// 设置邮箱字符过滤fun needFilterEmail(): Builder = apply {isFilterEmail = true}// 设置默认输入fun setDefaultInput(str: String): Builder = apply {inputView?.setText(str)}// 设置输入提示语fun setInputHint(hint: String): Builder = apply {inputView?.hint = hint}// 设置左边按钮点击事件fun setLeftClick(onLeftClick: ((BasicDialog) -> Unit)): Builder = apply {this.onLeftClick = onLeftClick}// 设置右边按钮点击事件fun setRightClick(onRightClick: ((String, BasicDialog) -> Unit)): Builder = apply {this.onRightClick = onRightClick}}
}

弹窗布局文件:

<?xml version="1.0" encoding="utf-8"?>
<com.ruffian.library.widget.RRelativeLayout 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:layout_marginTop="@dimen/dp_56"android:layout_gravity="bottom"app:background_normal="?first_bg_color"app:corner_radius_top_left="@dimen/dp_24"app:corner_radius_top_right="@dimen/dp_24"tools:viewBindingIgnore="true"><TextViewandroid:id="@+id/tv_left"android:layout_width="wrap_content"android:layout_height="@dimen/dp_54"android:gravity="center"android:paddingHorizontal="@dimen/dp_17"android:text="@string/cancel"android:textColor="?third_text_color"android:textSize="@dimen/sp_14" /><TextViewandroid:id="@+id/tv_right"android:layout_width="wrap_content"android:layout_height="@dimen/dp_54"android:layout_alignParentEnd="true"android:gravity="center"android:paddingHorizontal="@dimen/dp_17"android:text="@string/confirm"android:enabled="false"android:textColor="?first_btn_bg_color"android:textSize="@dimen/sp_14" /><TextViewandroid:id="@+id/tv_title"android:layout_width="wrap_content"android:layout_height="@dimen/dp_54"android:layout_centerHorizontal="true"android:gravity="center"android:paddingHorizontal="@dimen/dp_17"android:text="@string/depart_select"android:textColor="?first_text_color"android:textSize="@dimen/sp_18" /><Viewandroid:id="@+id/v_divider"android:layout_below="@+id/tv_title"android:layout_marginHorizontal="@dimen/dp_16"android:layout_width="match_parent"android:layout_height="0.5dp"android:background="?first_divider_color" /><com.ruffian.library.widget.RRelativeLayoutandroid:id="@+id/rl_et"android:layout_width="match_parent"android:layout_height="@dimen/dp_32"app:corner_radius="@dimen/dp_4"android:layout_below="@+id/v_divider"android:layout_margin="@dimen/dp_16"app:background_normal="?second_btn_bg_color"><ImageViewandroid:id="@+id/iv_search"android:layout_width="@dimen/dp_16"android:layout_height="@dimen/dp_16"android:layout_centerVertical="true"android:contentDescription="@null"android:layout_marginStart="@dimen/dp_10"android:src="?icon_edit_search" /><EditTextandroid:id="@+id/et_input"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginStart="@dimen/dp_10"android:layout_toStartOf="@+id/iv_del"android:layout_toEndOf="@+id/iv_search"android:background="@color/transparent"android:importantForAutofill="no"android:inputType="text"android:textColor="?first_text_color"android:hint="@string/please_input_depart_name"android:textColorHint="?first_hint_text_color"android:textSize="@dimen/sp_14"tools:ignore="LabelFor" /><ImageViewandroid:id="@+id/iv_del"android:layout_width="@dimen/dp_32"android:layout_height="@dimen/dp_32"android:layout_alignParentEnd="true"android:contentDescription="@null"android:paddingHorizontal="@dimen/dp_8"android:paddingVertical="@dimen/dp_8"android:visibility="invisible"android:src="?icon_clear_edit" /></com.ruffian.library.widget.RRelativeLayout><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recycler_view"android:layout_below="@+id/rl_et"app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"android:layout_width="match_parent"android:layout_height="wrap_content"/><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/rv_search"android:layout_below="@+id/rl_et"app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="?first_bg_color"/><LinearLayoutandroid:visibility="gone"android:id="@+id/ll_empty"android:layout_below="@+id/rl_et"android:layout_width="match_parent"android:layout_height="@dimen/dp_280"android:background="?first_bg_color"android:gravity="center"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent"><ImageViewandroid:layout_width="@dimen/dp_180"android:layout_height="@dimen/dp_160"android:src="?img_empty"/></LinearLayout></com.ruffian.library.widget.RRelativeLayout>

树形结构的适配器:

class DepartTreeAdapter(private val mContext: Context,private val treeItemList: ArrayList<DepartModel>
) :RecyclerView.Adapter<DepartTreeAdapter.KTTreeViewHolder>() {private var selectPosition = RecyclerView.NO_POSITIONprivate var onItemClickListener: OnItemClickListener? = nullvar onItemChildClickListener: OnItemChildClickListener? = nullprivate var settedAdapter = falsefun setSelectPosition(position: Int) {selectPosition = positionnotifyDataSetChanged()}fun submitList(data: ArrayList<DepartModel>) {treeItemList.clear()treeItemList.addAll(data)notifyDataSetChanged()}fun setDefaultAdapter() {notifyDataSetChanged()settedAdapter = true}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): KTTreeViewHolder {val binding =ItemDepartmentBinding.inflate(LayoutInflater.from(parent.context), parent, false)return KTTreeViewHolder(binding)}override fun onBindViewHolder(holder: KTTreeViewHolder, position: Int) {val treeItem = treeItemList[position]holder.bind(treeItem)}override fun getItemCount(): Int {return treeItemList.size}inner class KTTreeViewHolder(val binding: ItemDepartmentBinding) :RecyclerView.ViewHolder(binding.root) {fun bind(treeItem: DepartModel) {// 根据层级设置左边距val leftPadding = (treeItem.level - 1) * 50binding.llIcon.setPadding(leftPadding, 0, 0, 0)binding.tvName.text = treeItem.nameval icon =if (TestActivity.lastSelectBean?.id == treeItem.id) R.mipmap.ic_selected else R.mipmap.ic_unselectbinding.ivSelect.setImageResource(icon)//设置默认已选中数据的adapterif (settedAdapter && TestActivity.lastSelectBean?.id == treeItem.id) {settedAdapter = falseTestActivity.curAdapter = this@DepartTreeAdapter}// 根据展开状态设置箭头图标if (treeItem.children != null && treeItem.children?.isNotEmpty() == true) {if (treeItem.isOpen) {binding.ivNext.rotation = 180f} else {binding.ivNext.rotation = 0f}} else {binding.llIcon.visibility = View.INVISIBLE}// 设置点击事件itemView.setOnClickListener {// 切换展开状态treeItem.isOpen = !treeItem.isOpen// 局部刷新,只刷新当前点击的位置和其子项的数据notifyItemChanged(bindingAdapterPosition)onItemClickListener?.onItemClick(itemView, bindingAdapterPosition, treeItem)
//                setSelectPosition(bindingAdapterPosition)}// 如果没有数据子列表不显示binding.recyclerView.visibility = if (treeItem.isOpen) View.VISIBLE else View.GONE// 如果有子数据,设置适配器并显示箭头if (treeItem.children != null && treeItem.children?.isNotEmpty() == true) {binding.ivNext.visibility = View.VISIBLEbinding.recyclerView.visibility = if (treeItem.isOpen) View.VISIBLE else View.GONE// 计算子项的级别,加上适当的偏移量//给了level 不用计算
//                val childLevel = treeItem.level + 1
//                treeItem.children!!.forEach { child ->
//                    child.level = childLevel
//                }binding.recyclerView.layoutManager = LinearLayoutManager(mContext)val childAdapter = DepartTreeAdapter(mContext, treeItem.children!!)binding.recyclerView.adapter = childAdapterchildAdapter.setOnItemClickListener(object : OnItemClickListener {override fun onItemClick(view: View, position: Int, model: DepartModel) {onItemClickListener?.onItemClick(view, position, model)}})childAdapter.onItemChildClickListener = object : OnItemChildClickListener {override fun onClick(view: View, position: Int, model: DepartModel) {TestActivity.lastSelectBean = modelTestActivity.curAdapter?.notifyDataSetChanged()childAdapter.notifyDataSetChanged()TestActivity.curAdapter = childAdapterif (model.name.contains("……")) {onItemChildClickListener?.onClick(view, position, model)} else {var name = ""if (model.level > 2) {name = "……/"}name += "${treeItem.name}/${model.name}"val selModel = DepartModel(model.id, name)onItemChildClickListener?.onClick(view, position, selModel)}}}} else {    // 没有子数据,隐藏箭头binding.ivNext.visibility = View.GONEbinding.recyclerView.visibility = View.GONE}binding.ivSelect.setOnClickListener {TestActivity.lastSelectBean = treeItemTestActivity.curAdapter?.notifyDataSetChanged()notifyDataSetChanged()TestActivity.curAdapter = this@DepartTreeAdapteronItemChildClickListener?.onClick(it, bindingAdapterPosition, treeItem)}// 根据是否带有右箭头来设置背景颜色
//            itemView.setBackgroundColor(
//                ContextCompat.getColor(
//                    mContext,
//                    if (binding.ivNext.visibility == View.VISIBLE
//                        && bindingAdapterPosition == selectPosition
//                    ) R.color.color_public_bg else R.color.white
//                )
//            )}}fun setOnItemClickListener(listener: OnItemClickListener) {this.onItemClickListener = listener}interface OnItemClickListener {fun onItemClick(view: View, position: Int, model: DepartModel)}interface OnItemChildClickListener {fun onClick(view: View, position: Int, model: DepartModel)}
}

adapter绑定的item布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="wrap_content"xmlns:app="http://schemas.android.com/apk/res-auto"android:orientation="vertical"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center_vertical"android:paddingVertical="@dimen/dp_16"android:paddingHorizontal="@dimen/dp_16"><LinearLayoutandroid:id="@+id/ll_icon"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageViewandroid:id="@+id/iv_next"android:layout_width="@dimen/dp_12"android:layout_height="@dimen/dp_12"android:src="?ic_expand_normal" /></LinearLayout><TextViewandroid:id="@+id/tv_name"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_marginStart="@dimen/dp_6"android:layout_weight="1"android:textColor="?first_text_color"android:textSize="@dimen/sp_14" /><ImageViewandroid:id="@+id/iv_select"android:layout_width="@dimen/dp_24"android:layout_height="@dimen/dp_24"android:src="?ic_unselect" /></LinearLayout><Viewandroid:id="@+id/v_divider"android:layout_width="match_parent"android:layout_height="0.5dp"android:background="?first_divider_color"android:layout_marginHorizontal="@dimen/dp_16" /><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recyclerView"app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"android:layout_width="match_parent"android:layout_height="wrap_content" />
</LinearLayout>

实体类:

data class DepartModel(var id: String = "",var name: String = "",var pid: String = "",var children: ArrayList<DepartModel>? = arrayListOf(),var level: Int = 1,var isOpen: Boolean = false
)

页面选中后的UI需求如下:
在这里插入图片描述

TestActivity中设置:

    private var departData = arrayListOf<DepartModel>()private lateinit var builder: DepartListDialog.Builderprivate var tmpSelectDepart = DepartModel()private var selectDepart = DepartModel()//编辑页面中传入的已选择部门idprivate var department_id = ""//编辑页面中传入的已选择部门list(包含已选择的所有父级子级)private var department_list = arrayListOf<IdModel>()override fun initView() {builder = DepartListDialog.Builder(this)//如果是编辑页面进入,取出已选择数据选中if (isEditPage) {if (department_list.size > 0) {department_list.reverse()var departName = ""department_list.forEach {if (department_list.indexOf(it) > 1) return@forEachdepartName = it.name + "/" + departNameif (it.id.equals(department_id)) {tmpSelectDepart = DepartModel(id = it.id, name = it.name)}}if (department_list.size > 2) {departName = "……/${departName}"}departName = departName.substring(0, departName.length - 1)setDepartTxt(departName)tmpSelectDepart.name = departName}selectDepart.id = department_idlastSelectBean = DepartModel(id = department_id)}}override fun observeViewModel() {viewModel.departList.observe(this) {departData = ArrayList(it)if (isEditPage) {//从[编辑]跳转,把选中的item展开if (department_list.size > 1) {val ids = arrayListOf<String>()department_list.forEach { selDepart -> ids.add(selDepart.id) }departData.forEach { dd ->if (ids.contains(dd.id)) {//find选中部门的最外层级//循环把所有子级都展开dd.isOpen = truevar cList = dd.children ?: arrayListOf()var i = 0//大于10级就不读了while (i++ < 10 && cList.isNotEmpty()) {//子级中find选中部门的内层级cList.forEach { childDd ->if (ids.contains(childDd.id)) {//find选中部门的内层级
//                                    if(childDd.id==department_id){
//                                        childDd.isSelect=true
//                                    }//子集也打开childDd.isOpen = true//接着赋值新的list循环cList = childDd.children ?: arrayListOf()}}}}}}}builder.setData(departData)if (jumpType == 2) {builder.resetDefault()}showDepartDialog()}viewModel.departSearchList.observe(this) {builder.setSearchData(ArrayList(it))}}private fun showDepartDialog() {if (isEditPage && lastSelectBean?.id?.isNotEmpty() == true) builder.setRightBtnEnable(true)builder.setOnItemSelectClick { view, pos, item ->tmpSelectDepart = item}.setRightClick { s, basicDialog ->if (tmpSelectDepart.id != selectDepart.id) {//切换了部门resetPos()}selectDepart = tmpSelectDepartsetDepartTxt(selectDepart.name)}.setOnSearchItemClick { ids, departModel, pos, basicDialog ->if (departModel.id != selectDepart.id) {//切换了部门resetPos()}tmpSelectDepart = departModelselectDepart = departModellastSelectBean = DepartModel(id = departModel.id, name = departModel.name)setDepartTxt(departModel.name)//将选中状态更新到部门列表builder.resetDefault()//从搜索中选中的,将数据open状态全部重置,使弹窗重新打开就显示出选中itemdepartData.forEach { dd ->if (ids.contains(dd.id)) {//find选中部门的最外层级//循环把所有子级都展开dd.isOpen = truevar cList = dd.children ?: arrayListOf()var i = 0//大于10级就不读了while (i++ < 10 && cList.isNotEmpty()) {//子级中find选中部门的内层级cList.forEach { childDd ->if (ids.contains(childDd.id)) {//find选中部门的内层级//子集也打开childDd.isOpen = true//接着赋值新的list循环cList = childDd.children ?: arrayListOf()} else {childDd.isOpen = false}}}} else {dd.isOpen = false}}}.setOnInputListener { s, basicDialog ->viewModel.searchDepartmentList("0", s)}.addOnDismissListener(object : BasicDialog.OnDismissListener {override fun onDismiss(dialog: BasicDialog?) {setProgressShow(true)builder.setSearchData(arrayListOf())builder.setDefaultInput("")}}).setInputType(InputType.TYPE_CLASS_TEXT).show()setProgressShow(false)}/*** 设置部门名称文字变色*/private fun setDepartTxt(content: String) {val weekStr = content.substring(0, content.lastIndexOf("/") + 1)val weekColor =  ContextCompat.getColor(this, R.color.color_BFBFBF)val normalColor = ContextCompat.getColor(this, R.color.color_303030)setPartText(binding.tvDepart, content, weekStr, normalColor, weekColor)}companion object {var curAdapter: DepartTreeAdapter? = nullvar lastSelectBean: DepartModel? = null}

这里解释下为什么放了静态的curAdapter和lastSelectBean吧(如果不需要选中效果的话可以不要这块儿)

因为嵌套了多层子级RecyclerView和配套adapter,如果本来选中第一层,改选第二层,那么现在你去notifyAdapter的时候,只能通知到当前第二层的子Adapter,原来第一层已选中的Adapter没法通知刷新到,UI上就无法取消选择,所以直接把它放在Activity里暂存,这样Adapter更新后,Activity里的依然是上次选择时用的那一个。

lastSelectBean为什么不放在Adapter里呢,原因跟上面一样,Adapter一加载子级就是新的Adapter了,lastSelectBean也就变成新的了。

当然这种方法很der哈,但是咱就是工资也很der,这样很合理在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/4183.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot+ClickHouse集成

前面已经完成ClickHouse的搭建&#xff0c;创建账号&#xff0c;创建数据库&#xff0c;保存数据库等&#xff0c;接下来就是在SpringBoot项目中集成ClickHouse。 一&#xff0c;引入依赖 <!-- SpringBoot集成ClickHouse --> <dependency><groupId>com.baom…

【基于轻量型架构的WEB开发】课程 12.5 数据回写 Java EE企业级应用开发教程 Spring+SpringMVC+MyBatis

12.5 数据回写 12.5.1 普通字符串的回写 接下来通过HttpServletResponse输出数据的案例&#xff0c;演示普通字符串的回写&#xff0c;案例具体实现步骤如下。 1 创建一个数据回写类DataController&#xff0c;在DataController类中定义 showDataByResponse()方法&#xff…

Java实现JWT登录认证

文章目录 什么是JWT?为什么需要令牌?如何实现?添加依赖&#xff1a;JwtUtils.java&#xff08;生成、解析Token的工具类&#xff09;jwt配置&#xff1a;登录业务逻辑&#xff1a;其他关联代码&#xff1a;测试&#xff1a; 什么是JWT? JWT&#xff08;Json Web Token&…

光伏无人机踏勘,照亮光伏未来!

光伏电站选址地分散在各地&#xff0c;想要精准获取该地的地形特点与屋顶面积等信息&#xff0c;传统的人工踏勘耗时耗力且精度无法保证&#xff0c;难以满足现代光伏项目的规模快发发展需求。光伏无人机踏勘&#xff0c;照亮光伏未来&#xff01; 在光伏无人机智能踏勘设计系统…

Vue全栈开发旅游网项目(7)-搜索界面开发及其接口联调

1.搜索界面开发 1.1 模糊查询 文件地址&#xff1a;pycharm- class SightListView(ListView):paginate_by 5def get_queryset(self):#is_validTrue&#xff1a;表中is_valid列&#xff0c;有值则被查询出来query Q(is_validTrue)#1.获得热门景点is_hot self.request.GET.…

『 Linux 』网络传输层 - TCP(二)

文章目录 TCP六个标志位TCP的连接三次握手 四次挥手为什么是三次握手和四次挥手 重传机制 TCP六个标志位 在TCP协议报文的报头中存在一个用于标志TCP报文类型的标志位(不考虑保留标志位),这些标志位以比特位选项的方式存在,即对应标志位为0则表示为假,对应标志位为1则为真; SYN…

Django学习-项目部署

WSGI定义&#xff1a; uWSGI定义&#xff1a; 安装uWSGI&#xff1a; 配置uWSGI&#xff1a; uWSGI常见问题汇总&#xff1a; 安装nginx&#xff1a; 配置&#xff1a; 启动/停止dnginx 修改uWSGI配置&#xff1a; 常见问题解决方法&#xff1a; nginx静态文件配置&#xff…

迅为RK3588开发板Android多屏显示之多屏同显和多屏异显

迅为RK3588开发板是一款低功耗、高性能的处理器&#xff0c;适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用&#xff0c;RK3588支持8K视频编解码&#xff0c;内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像…

QML项目实战:自定义Button

目录 一.添加模块 ​1.QtQuick.Controls 2.1 2.QtGraphicalEffects 1.12 二.自定义Button 1.颜色背景设置 2.设置渐变色背景 3.文本设置 4.点击设置 5.阴影设置 三.效果 1.当enabled为true 2.按钮被点击时 3.当enabled为false 四.代码 一.添加模块 1.QtQuick.Con…

基于C#实现Windows后台窗口操作与图像处理技术分析

在Windows编程中&#xff0c;操作后台窗口是一项复杂而有用的技术。它可以用来自动化用户界面测试、应用程序机器人等场景。本文将深入探讨如何在C#中绑定后台窗口、获取后台窗口界面图片&#xff0c;以及在图片中寻找指定图标并获取坐标。本技术文章结合最先进的资料与实践经验…

数据库基础(1) . 关系型数据库

1.数据库 database 1.1.数据持久化 数据持久化&#xff08;Data Persistence&#xff09;指的是将程序中的数据保存到某种持久化的存储介质&#xff08;如硬盘、SSD、磁带等&#xff09;上的过程&#xff0c;使得即使在程序终止后&#xff0c;数据依然可以被保留下来并在下次…

Python学习的自我理解和想法(27)

学的是b站的课程&#xff08;千锋教育&#xff09;&#xff0c;跟老师写程序&#xff0c;不是自创的代码&#xff01; 今天是学Python的第27天&#xff0c;学的内容是python操作pptx和pdf&#xff0c;但是这节博客只会介绍如何新建pptx和加密pdf。开学了&#xff0c;时间不多&…

鸿蒙移动应用开发-------初始arkts

一. 什么是arkts ArkTS是HarmonyOS优选的主力应用开发语言。 ArkTS围绕应用开发在TypeScript&#xff08;简称TS&#xff09;生态基础上做了进一步扩展&#xff0c;保持了TS的基本风格&#xff0c;同时通过规范定义强化开发期静态检查和分析&#xff0c;提升程序执行稳定性和…

Linux(CentOS)安装 JDK

1、下载 JDK 官网&#xff1a;https://www.oracle.com/ 2、上传 JDK 文件到 CentOS&#xff0c;使用FinalShell远程登录工具&#xff0c;并且使用 root 用户登录 3、解压 JDK 创建目录 /export/server mkdir -p /export/server 解压到目录 /export/server tar -zxvf jdk-17…

qt QStandardItemModel详解

1、概述 QStandardItemModel是Qt框架中提供的一个基于项的模型类&#xff0c;用于存储和管理数据&#xff0c;这些数据可以以表格的形式展示在视图控件&#xff08;如QTableView、QTreeView等&#xff09;中。QStandardItemModel支持丰富的数据操作&#xff0c;包括添加、删除…

SpringBoot框架在在线教育领域的应用

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 图4-1系统工作原理…

【论文分享】基于多源大数据的高密度城市健康资源可达性与公平性评价

评估城市健康设施的可达性和公平性对于有效配置城市健康资源至关重要。本次我们给大家带来一篇SCI论文的全文翻译。该论文从新的视角定义和分类城市中的健康相关设施&#xff0c;考虑居民的主动和被动健康寻求行为&#xff0c;构建一个综合性框架来评估健康设施的邻近性、互补性…

Vue学习之路17----事件

可以自定义事件让子组件向父组件传值 1.使用emit 2.使用props 3.使用mitt 其实mitt和第一种方法类似&#xff0c;都用emitt事件&#xff0c;但是mitt不局限于父子之间通信&#xff0c;他可以在任意2个组件之间通信&#xff0c; 虽然需要安装&#xff0c;但mitt很小&#xff…

基于梯度的快速准确头部运动补偿方法在锥束CT中的应用|文献速递-基于深度学习的病灶分割与数据超分辨率

Title 题目 A gradient-based approach to fast and accurate head motion compensation in cone-beam CT 基于梯度的快速准确头部运动补偿方法在锥束CT中的应用 01 文献速递介绍 锥束计算机断层扫描&#xff08;CBCT&#xff09;系统在灵活性方面比螺旋多排探测器计算机断…