Android 使用Kotlin封装RecyclerView
文章目录
- 1.概述
- 2.运行效果图
- 3.代码实现
- 3.1 扩展RecyclerView
- 3.2 扩展Adapter
- 3.3 RecyclerView装饰绘制
- 3.3.1 以图片实现分割线
- 3.3.2 画网格线
- 3.3.3空白的分割线
- 3.3.4 不同方向上的分割线
- 3.4 使用方法
1.概述
在一个开源项目上看到了一个Android Kotlin版的RecyclerView封装,个人觉得非常方便,所以就将这个封装摘了出来,记录下,方便以后使用,这个开源的项目叫DanDanPlayForAndroid点击链接可以查看具体的开源项目代码。
2.运行效果图
3.代码实现
3.1 扩展RecyclerView
我们可以通过Kotlin的扩展函数扩展RecycleView的布局方式,设置数据等功能,方便我们调用。代码如下:
fun RecyclerView.vertical(reverse: Boolean = false
): LinearLayoutManager {return LinearLayoutManager(context,LinearLayoutManager.VERTICAL,reverse)
}fun RecyclerView.horizontal(reverse: Boolean = false
): LinearLayoutManager {return LinearLayoutManager(context,LinearLayoutManager.HORIZONTAL,reverse)
}fun RecyclerView.grid(spanCount: Int
): GridLayoutManager {return GridLayoutManager(context, spanCount)
}fun RecyclerView.gridEmpty(spanCount: Int): GridLayoutManager {return GridLayoutManager(context, spanCount).also {it.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {override fun getSpanSize(position: Int): Int {if (position == RecyclerView.NO_POSITION) {return 1}val viewType = adapter?.getItemViewType(position)if (viewType != -1) {return 1}return spanCount}}}
}fun RecyclerView.setData(itemData: List<Any>) {(adapter as RVBaseAdapter).setData(itemData)
}fun RecyclerView.requestIndexChildFocus(index: Int): Boolean {scrollToPosition(index)val targetTag = "tag_focusable_item"val indexView = layoutManager?.findViewByPosition(index)if (indexView != null) {indexView.findViewWithTag<View>(targetTag)?.requestFocus()return true}post {layoutManager?.findViewByPosition(index)?.findViewWithTag<View>(targetTag)?.requestFocus()}return true
}
3.2 扩展Adapter
在扩展Adapter之前,我们需要先定义一个我们自己的Adapter,然后再基于我们自己的Adapter去做扩展,代码如下:
class RVBaseAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {companion object{// the data of empty layoutval EMPTY_ITEM = Any()// view type of empty layoutconst val VIEW_TYPE_EMPTY = -1// number of max itemprivate const val NUMBER_OF_MAX_VIEW_TYPE = Int.MAX_VALUE -1}val itemData: MutableList<Any> = mutableListOf()private val typeHolders = SparseArrayCompat<BaseViewHolderCreator<out ViewDataBinding>>()override fun onCreateViewHolder(parent: ViewGroup,viewType: Int): RecyclerView.ViewHolder {return BaseViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.context),getHolderCreator(viewType).getResourceId(),parent,false))}private fun getHolderCreator(viewType: Int): BaseViewHolderCreator<out ViewDataBinding> {return typeHolders.get(viewType)?: throw java.lang.RuntimeException()}override fun getItemCount(): Int {return itemData.size}override fun onBindViewHolder(holder: RecyclerView.ViewHolder,position: Int) {getHolderCreator(holder.itemViewType).apply {initItemBinding(holder.itemView)onBindViewHolder(itemData[position],position,this)}}fun setData(dataList: List<Any>) {itemData.clear()itemData.addAll(dataList)// show the empty layout when data is emptyif(itemData.isEmpty() && typeHolders.containsKey(VIEW_TYPE_EMPTY)){itemData.add(EMPTY_ITEM)}notifyDataSetChanged()}fun register(creator: BaseViewHolderCreator<out ViewDataBinding>, customViewType: Int? = null) {apply {var viewType = customViewType ?: typeHolders.size()while (typeHolders.get(viewType) != null) {viewType++require(viewType < NUMBER_OF_MAX_VIEW_TYPE) {"the number of view type has reached the maximum limit"}}require(viewType < NUMBER_OF_MAX_VIEW_TYPE) {"the number of view type has reached the maximum limit"}typeHolders.put(viewType, creator)}}override fun getItemViewType(position: Int): Int {if(itemData[position] == EMPTY_ITEM&& typeHolders.containsKey(VIEW_TYPE_EMPTY)){return VIEW_TYPE_EMPTY}// only one viewHolderif(typeHolders.size() == 1){return typeHolders.keyAt(0)}// more than one viewHolderfor (i in 0 until typeHolders.size()){if(typeHolders.keyAt(i) == VIEW_TYPE_EMPTY){continue}val holder = typeHolders.valueAt(i)if(holder.isForViewType(itemData[position],position)){return typeHolders.keyAt(i)}}throw java.lang.IllegalStateException("no holder added that matches at position: $position in data source")}
}
与上面代码相关联的抽象类:
class BaseViewHolder(binding: ViewDataBinding) :RecyclerView.ViewHolder(binding.root) {
}
abstract class BaseViewHolderCreator<V : ViewDataBinding> {abstract fun isForViewType(data: Any?, position: Int): Booleanabstract fun getResourceId(): Intabstract fun onBindViewHolder(data: Any?,position: Int,creator: BaseViewHolderCreator<out ViewDataBinding>)lateinit var itemDataBinding: Vfun initItemBinding(itemView: View) {this.itemDataBinding = DataBindingUtil.getBinding(itemView)!!}
}
抽象类的实现:
class BaseViewHolderDSL<T : Any, V : ViewDataBinding>(private val resourceId: Int,private val clazz: KClass<T>
) : BaseViewHolderCreator<V>() {private var checkViewType: ((data: Any, position: Int) -> Boolean)? = nullprivate var viewHolder: ((data: T, position: Int, creator:BaseViewHolderCreator<out ViewDataBinding>) -> Unit)? = nullprivate var emptyViewHolder: (() -> Unit)? = nulloverride fun isForViewType(data: Any?, position: Int): Boolean {if(data == null){return false}if(checkViewType != null){return checkViewType!!.invoke(data,position)}return clazz.isInstance(data)}/*** judge the type of current item data according to position*/fun checkType(viewType:(data:Any,position:Int) ->Boolean){this.checkViewType = viewType}fun initView(holder:(data:T,position:Int,holder:BaseViewHolderCreator<out ViewDataBinding>)->Unit){this.viewHolder = holder}override fun getResourceId(): Int {return resourceId}override fun onBindViewHolder(data: Any?,position: Int,creator: BaseViewHolderCreator<out ViewDataBinding>) {// empty layoutif(data == RVBaseAdapter.EMPTY_ITEM){emptyViewHolder?.invoke()return}data ?: returnviewHolder?.invoke(data as T,position,creator)}
}
RVBaseAdapter类的扩展
fun buildAdapter(init: RVBaseAdapter.() -> Unit): RVBaseAdapter {return RVBaseAdapter().apply {init()}
}inline fun <reified T : Any, V : ViewDataBinding> RVBaseAdapter.addItem(resourceID: Int,init: BaseViewHolderDSL<T, V>.() -> Unit
) {register(BaseViewHolderDSL<T, V>(resourceID, T::class).apply { init() })
}inline fun RVBaseAdapter.addEmptyView(resourceID: Int,init: (BaseViewHolderDSL<Any, LayoutEmptyBinding>.() -> Unit) = {}
) {register(BaseViewHolderDSL<Any, LayoutEmptyBinding>(resourceID, Any::class).apply {init()},customViewType = RVBaseAdapter.VIEW_TYPE_EMPTY)setData(listOf(RVBaseAdapter.EMPTY_ITEM))
}
3.3 RecyclerView装饰绘制
RecyclerView可以继承自ItemDecoration类绘制自己想要的分割线和装饰,这里做了几个例子,代码如下:
3.3.1 以图片实现分割线
/*** 分割线(以图片实现)*/
class MyItemDecoration(divider: Drawable, dividerSize: Int) :
RecyclerView.ItemDecoration() {private val mDivider = dividerprivate val mDividerSize = dividerSizeoverride fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {canvas.save()//居中显示val top = (parent.height - mDividerSize) / 2val bottom = top + mDividerSizeval mBounds = Rect()//只在中间绘制for (i in 0 until parent.childCount - 1) {val child = parent.getChildAt(i)parent.layoutManager!!.getDecoratedBoundsWithMargins(child, mBounds)val right = mBounds.right + child.translationX.roundToInt()val left = right - mDividerSizemDivider.setBounds(left, top, right, bottom)mDivider.draw(canvas)}canvas.restore()}override fun getItemOffsets(outRect: Rect,view: View,parent: RecyclerView,state: RecyclerView.State) {outRect.set(0, 0, mDividerSize, 0)}
}
3.3.2 画网格线
class ItemGridDecorationDrawable : ItemDecoration {private var leftRight: Intprivate var topBottom: Intprivate var mDivider: Drawable?constructor(spacePx: Int) {leftRight = spacePxtopBottom = spacePxmDivider = ColorDrawable(Color.WHITE)}constructor(leftRight: Int, topBottom: Int) {this.leftRight = leftRightthis.topBottom = topBottommDivider = ColorDrawable(Color.WHITE)}constructor(leftRight: Int, topBottom: Int, mColor: Int) {this.leftRight = leftRightthis.topBottom = topBottommDivider = ColorDrawable(mColor)}override fun onDraw(c: Canvas,parent: RecyclerView,state: RecyclerView.State) {val layoutManager = parent.layoutManager as GridLayoutManager? ?: returnval lookup = layoutManager.spanSizeLookupif (mDivider == null || layoutManager.childCount == 0) {return}//判断总的数量是否可以整除val spanCount = layoutManager.spanCountvar left: Intvar right: Intvar top: Intvar bottom: Intval childCount = parent.childCountif (layoutManager.orientation == GridLayoutManager.VERTICAL) {for (i in 0 until childCount) {val child = parent.getChildAt(i)//将带有颜色的分割线处于中间位置val centerLeft =((layoutManager.getLeftDecorationWidth(child) + layoutManager.getRightDecorationWidth(child)).toFloat()* spanCount / (spanCount + 1) + 1 - leftRight) / 2val centerTop =(layoutManager.getBottomDecorationHeight(child)+ 1 - topBottom) / 2f//得到它在总数里面的位置val position = parent.getChildAdapterPosition(child)//获取它所占有的比重val spanSize = lookup.getSpanSize(position)//获取每排的位置val spanIndex = lookup.getSpanIndex(position, layoutManager.spanCount)//判断是否为第一排val isFirst =layoutManager.spanSizeLookup.getSpanGroupIndex(position,spanCount) == 0//画上边的,第一排不需要上边的,只需要在最左边的那项的时候画一次就好if (!isFirst && spanIndex == 0) {left = layoutManager.getLeftDecorationWidth(child)right = parent.width - layoutManager.getLeftDecorationWidth(child)top = (child.top - centerTop).toInt() - topBottombottom = top + topBottommDivider!!.setBounds(left, top, right, bottom)mDivider!!.draw(c)}//最右边的一排不需要右边的val isRight = spanIndex + spanSize == spanCountif (!isRight) { //计算右边的left = (child.right + centerLeft).toInt()right = left + leftRighttop = child.topif (!isFirst) {top -= centerTop.toInt()}bottom = (child.bottom + centerTop).toInt()mDivider!!.setBounds(left, top, right, bottom)mDivider!!.draw(c)}}} else {for (i in 0 until childCount) {val child = parent.getChildAt(i)//将带有颜色的分割线处于中间位置val centerLeft =(layoutManager.getRightDecorationWidth(child) + 1 - leftRight) / 2fval centerTop =((layoutManager.getTopDecorationHeight(child) + layoutManager.getBottomDecorationHeight(child)).toFloat()
* spanCount / (spanCount + 1) - topBottom) / 2//得到它在总数里面的位置val position = parent.getChildAdapterPosition(child)//获取它所占有的比重val spanSize = lookup.getSpanSize(position)//获取每排的位置val spanIndex = lookup.getSpanIndex(position, layoutManager.spanCount)//判断是否为第一列val isFirst =layoutManager.spanSizeLookup.getSpanGroupIndex(position, spanCount) == 0//画左边的,第一排不需要左边的,只需要在最上边的那项的时候画一次就好if (!isFirst && spanIndex == 0) {left = (child.left - centerLeft).toInt() - leftRightright = left + leftRighttop = layoutManager.getRightDecorationWidth(child)bottom = parent.height - layoutManager.getTopDecorationHeight(child)mDivider!!.setBounds(left, top, right, bottom)mDivider!!.draw(c)}//最下的一排不需要下边的val isRight = spanIndex + spanSize == spanCountif (!isRight) { //计算右边的left = child.leftif (!isFirst) {left -= centerLeft.toInt()}right = (child.right + centerTop).toInt()top = (child.bottom + centerLeft).toInt()bottom = top + leftRightmDivider!!.setBounds(left, top, right, bottom)mDivider!!.draw(c)}}}}override fun getItemOffsets(outRect: Rect,view: View,parent: RecyclerView,state: RecyclerView.State) {val layoutManager = parent.layoutManager as GridLayoutManager? ?: returnval lp =view.layoutParams as GridLayoutManager.LayoutParamsval childPosition = parent.getChildAdapterPosition(view)val spanCount = layoutManager.spanCountif (layoutManager.orientation == GridLayoutManager.VERTICAL) { //判断是否在第一排if (layoutManager.spanSizeLookup.getSpanGroupIndex(childPosition,spanCount) == 0) { //第一排的需要上面outRect.top = topBottom}outRect.bottom = topBottom//这里忽略和合并项的问题,只考虑占满和单一的问题if (lp.spanSize == spanCount) { //占满outRect.left = leftRightoutRect.right = leftRight} else {outRect.left =((spanCount - lp.spanIndex).toFloat() / spanCount * leftRight).toInt()outRect.right =(leftRight.toFloat() * (spanCount + 1) / spanCount - outRect.left).toInt()}} else {if (layoutManager.spanSizeLookup.getSpanGroupIndex(childPosition,spanCount) == 0) { //第一排的需要leftoutRect.left = leftRight}outRect.right = leftRight//这里忽略和合并项的问题,只考虑占满和单一的问题if (lp.spanSize == spanCount) { //占满outRect.top = topBottomoutRect.bottom = topBottom} else {outRect.top =((spanCount - lp.spanIndex).toFloat() / spanCount * topBottom).toInt()outRect.bottom =(topBottom.toFloat() * (spanCount + 1) / spanCount - outRect.top).toInt()}}}
}
3.3.3空白的分割线
/*** 空白的分割线**/
class ItemDecorationSpace : ItemDecoration {private var top: Intprivate var left: Intprivate var right: Intprivate var bottom: Intprivate var spanCount: Intconstructor(space: Int) : this(space, space, space, space)constructor(spaceLR: Int, spaceTB: Int) : this(spaceTB, spaceLR, spaceLR,spaceTB)constructor(top: Int, left: Int, right: Int, bottom: Int) {this.top = topthis.left = leftthis.right = rightthis.bottom = bottomspanCount = 0}constructor(top: Int, left: Int, right: Int, bottom: Int, spanCount: Int) {this.top = topthis.left = leftthis.right = rightthis.bottom = bottomthis.spanCount = spanCount}override fun getItemOffsets(outRect: Rect, view: View,parent: RecyclerView, state: RecyclerView.State) {outRect.top = topoutRect.left = leftoutRect.bottom = bottomif (spanCount != 0) {val position = parent.getChildLayoutPosition(view)if ((position + 1) % spanCount == 0) {outRect.right = 0} else {outRect.right = right}} else {outRect.right = right}}
}
3.3.4 不同方向上的分割线
/*** 不同方向上的分割线*/class ItemDecorationOrientation : ItemDecoration {private val dividerPx: Intprivate val headerPx: Intprivate val footerPx: Intprivate val orientation: Intconstructor(dividerPx: Int, @RecyclerView.Orientation orientation: Int) : this(dividerPx,dividerPx,orientation)constructor(dividerPx: Int,headerFooterPx: Int,@RecyclerView.Orientation orientation: Int) : this(dividerPx, headerFooterPx, headerFooterPx, orientation)constructor(dividerPx: Int,headerPx: Int,footerPx: Int,@RecyclerView.Orientation orientation: Int) {this.dividerPx = dividerPxthis.headerPx = headerPxthis.footerPx = footerPxthis.orientation = orientation}override fun getItemOffsets(outRect: Rect,view: View,parent: RecyclerView,state: RecyclerView.State) {if (orientation == RecyclerView.VERTICAL) {getItemOffsetsVertical(outRect, view, parent)} else {getItemOffsetsHorizontal(outRect, view, parent)}}private fun getItemOffsetsVertical(outRect: Rect, view: View,parent: RecyclerView) {val itemCount = parent.adapter?.itemCount ?: returnval position = parent.getChildAdapterPosition(view)if (position == 0) {outRect.top = headerPx} else {outRect.top = position * dividerPx / itemCount}if (position == itemCount - 1) {outRect.bottom = footerPx} else {outRect.bottom = dividerPx - (position + 1) * dividerPx / itemCount}}private fun getItemOffsetsHorizontal(outRect: Rect, view: View, parent:RecyclerView) {val itemCount = parent.adapter?.itemCount ?: returnval position = parent.getChildAdapterPosition(view)if (position == 0) {outRect.left = headerPx} else {outRect.left = position * dividerPx / itemCount}if (position == itemCount - 1) {outRect.right = footerPx} else {outRect.right = dividerPx - (position + 1) * dividerPx / itemCount}}
}
3.4 使用方法
使用的时候去掉代码中对应的注释,体验各种风格
class RecyclerViewActivity : AppCompatActivity() {private lateinit var dataBinding: ActivityRecyclerViewBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)initDataBinding()initRV()val dataList = listOf<UserData>(UserData("walt zhong", 21),UserData("walt xian", 22),UserData("walt jian", 31),UserData("walt x", 22),UserData("walt y", 41),UserData("walt z", 26),UserData("walt 2", 29),)// val dataList = emptyList<UserData>()dataBinding.rvList.setData(dataList)}private fun initRV() {dataBinding.rvList.apply {// layoutManager = gridEmpty(3) //网格布局// layoutManager = vertical(false) // 垂直布局layoutManager = horizontal(false) // 水平布局adapter = buildAdapter {addEmptyView(R.layout.layout_empty)addItem<UserData, RvItemBinding>(R.layout.rv_item) {initView { data, position, _ ->itemDataBinding.apply {tvName.text = data.nametvAge.text = data.age.toString()itemLayout.setOnClickListener {Log.d("zhongxj", "click item: $position")}}}}}// val pxValue = dp2px(5)
//
// addItemDecoration(
// ItemGridDecorationDrawable(
// pxValue,
// pxValue,
// R.color.purple_200
// )
// )// addItemDecoration(
// ItemDecorationSpace(
// pxValue
// )
// )// addItemDecoration(
// ItemDecorationOrientation(
// dividerPx = pxValue,
// headerFooterPx = 0,
// orientation = RecyclerView.HORIZONTAL
// )
// )val dividerSize = dp2px(16)val divider = ContextCompat.getDrawable(context, R.drawable.ic_arrow)if(divider != null){addItemDecoration(MyItemDecoration(divider,dividerSize))}}}private fun initDataBinding() {dataBinding = DataBindingUtil.setContentView(this,R.layout.activity_recycler_view)dataBinding.lifecycleOwner = this@RecyclerViewActivity}/*** 单位转换,将DP转为PX*/fun dp2px(dpValue: Int): Int {val scale = Resources.getSystem().displayMetrics.densityreturn (dpValue * scale + 0.5f).toInt()}
}data class UserData(var name:String,var age:Int)
布局文件:
RcyclerViewActivity布局
<?xml version="1.0" encoding="utf-8"?>
<layout 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"><data></data><LinearLayoutandroid:background="#eeeeee"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".RecyclerViewActivity"><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/rv_list"android:layout_width="match_parent"android:layout_height="wrap_content" /></LinearLayout>
</layout>
RecyclerView item布局
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"><data></data><LinearLayoutandroid:background="@color/white"android:padding="10dp"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="100dp"android:id="@+id/item_layout"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="20sp"android:text="walt"android:id="@+id/tv_name"/><TextViewandroid:layout_marginTop="10dp"android:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="20sp"android:text="24"android:id="@+id/tv_age"/></LinearLayout>
</layout>
没有数据时的空布局
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><ImageViewandroid:id="@+id/empty_iv"android:layout_width="200dp"android:layout_height="200dp"android:src="@mipmap/ic_empty_data"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.382" /><TextViewandroid:id="@+id/empty_tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="5dp"android:text="没有数据"android:textColor="@color/black"android:textSize="16sp"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/empty_iv" /></androidx.constraintlayout.widget.ConstraintLayout>
</layout>
里面对应的图片读者自己找喜欢的替换上就可以啦,本文主要是记录,代码也不难,读者可以自行跟着敲一遍,加深映像,熟悉这种封装方法,后面可以使用在项目的其他部分的封装。
相关文章:

Android 使用Kotlin封装RecyclerView
文章目录 1.概述2.运行效果图3.代码实现3.1 扩展RecyclerView 3.2 扩展Adapter3.3 RecyclerView装饰绘制3.3.1 以图片实现分割线3.3.2 画网格线3.3.3空白的分割线3.3.4 不同方向上的分割线 3.4 使用方法 1.概述 在一个开源项目上看到了一个Android Kotlin版的RecyclerView封装…...

WPF 实现点击按钮跳转页面功能
方法1. 配置环境 首先添加prism依赖项,配置好所有文件。需要配置的有两个文件:App.xaml.cs和App.xaml App.xaml.cs using System.Data; using System.Linq; using System.Threading.Tasks; using System.Windows;namespace PrismDemo {/// <summa…...
关于http网络通信数据包封装的过程
当我们谈论网络通信时,数据在从源到目的地传输的过程中会通过多层网络协议。在每一层,都会添加一些头信息(和有时尾信息)来帮助处理和传输数据。这个过程被称为"封装"(Encapsulation)。简单来说&…...

关于RabbitMQ你了解多少?
关于RabbitMQ你了解多少? 文章目录 关于RabbitMQ你了解多少?基础篇同步和异步MQ技术选型介绍和安装数据隔离SpringAMQP快速入门Work queues交换机Fanout交换机Direct交换机Topic交换机 声明队列和交换机MQ消息转换器 高级篇消息可靠性问题发送者的可靠性…...
Vulkan-着色器及编译SPIR-V
1.着色器模块介绍 Vulkan着色器代码一定要用字节码格式,而不是人类可读的语法如GLSL和HLSL。这个字节码就是SPIR-V,设计用于Vulkan和OpenCL。这是一个可以用于编写图形和计算着色器的格式,但是我们主要关注的是Vulkan的图形管线。使用字节码格…...

从MVC到DDD,该如何下手重构?
作者:付政委 博客:bugstack.cn 沉淀、分享、成长,让自己和他人都能有所收获!😄 大家好,我是技术UP主小傅哥。多年的 DDD 应用,使我开了技术的眼界! MVC 旧工程腐化严重,…...
论文阅读:基于隐马尔可夫模型的蛋白质多序列比对方法研究
本文来自chatpaper Basic Information: • Title: Research on Protein Multiple Sequence Alignment Method Based on Hidden Markov Model (基于隐马尔可夫模型的蛋白质多序列比对方法研究) • Authors: Zhan Qing • Affiliation: Harbin Institute of Technology (哈尔滨工…...
Vim同时打开多个文件
分屏模式 在 Vim 中,可以同时打开多个文件并使用分屏模式来查看它们。以下是一些常见的方法和命令: 在启动 Vim 时打开多个文件 使用 -o 选项打开文件并水平分屏: vim -o file1.txt file2.txt使用 -O 选项打开文件并垂直分屏: v…...
SpringCloudStreamkafka接收jsonarray字符串失败
文章目录 场景现象问题处理 场景现象 kafka作为消息队列,作为前端设备数据到后端消费的渠道,也被多个不同微服务消费一个服务与前端边缘计算设备建立socket消息,接收实时交通事件推送,再将事件发送到kafka里面。此处使用的是Spri…...

面向对象特性分析大全集
面向对象特性分析 先进行专栏介绍 面向对象总析前提小知识分类浅析封装浅析继承浅析多态面向对象编程优点abc 核心思想实际应用总结 封装概念详解关键主要目的核心思想优点12 缺点12 Java代码实现封装特性 继承概念详解语法示例关键主要目的核心思想优点12 缺点12 Java代码实现…...

【数据结构】队列和栈
大家中秋节快乐,玩了好几天没有学习,今天分享的是栈以及队列的相关知识,以及栈和队列相关的面试题 1.栈 1.1栈的概念及结构 栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作…...
WordPress主题开发( 十)之—— 条件标签函数(上)
这里写目录标题 什么是条件标签函数?条件标签函数的使用场景使用条件标签函数的注意事项常用的条件标签函数主页示例:is_front_page() 示例:管理后台is_admin() 示例:单个文章页面is_single() 示例:is_single(17) 示例:is_single(Hello World) 示例:is_single(hello…...
vue学习-10vue整合SpringBoot跨域请求
在Vue.js应用整合Spring Boot后端时,需要处理跨域请求。跨域请求通常发生在前端应用运行在不同的域名或端口上时,而后端服务运行在不同的域名或端口上。以下是一种处理跨域请求的常见方式: 后端(Spring Boot)配置 在…...

ElasticSearch - 基于 JavaRestClient 查询文档(match、精确、复合查询,以及排序、分页、高亮)
目录 一、基于 JavaRestClient 查询文档 1.1、查询 API 演示 1.1.1、查询基本框架 DSL 请求的对应格式 响应的解析 1.1.2、全文检索查询 1.1.3、精确查询 1.1.4、复合查询 1.1.5、排序和分页 1.1.6、高亮 一、基于 JavaRestClient 查询文档 1.1、查询 API 演示 1.1.…...
简易实现通讯录(2.0)
这篇文章是在上期实现的通讯录基础上,增加了自动增容的功能,也解决了一开始通讯录自动开辟一个空间,可能会浪费空间,或者是信息过多无法增容的痛点,由于我们使用的是malloc这类函数来开辟空间,我们也需要来…...
Jasypt 实现自定义加解密
如下文章已经讲解了, Jasypt 是什么,怎么集成 Jasypt,怎么使用 Jasypt。 Jasypt 开源加密库使用教程_jasyptstringencryptor-CSDN博客Jasypt 加密框架概述1、Jasypt Spring Boot 为 spring boot 应用程序中的属性源提供加密支持,…...

Leetcode 554. 砖墙
文章目录 题目代码(9.25 首刷自解) 题目 Leetcode 554. 砖墙 代码(9.25 首刷自解) class Solution { public:int leastBricks(vector<vector<int>>& wall) {unordered_map<int, int> mp;int count 0;for…...

Python 内置函数详解 (3) 进制转换
近期在外旅游,本篇是出发前定时发布的,不完整,旅游回来后再补充。 Python 内置函数 Python3.11共有75个内置函数,其来历和分类请参考:Python 新版本有75个内置函数,你不会不知道吧_Hann Yang的博客-CSDN博客 函数列表 abs aiter all …...

SPSS列联表分析
前言: 本专栏参考教材为《SPSS22.0从入门到精通》,由于软件版本原因,部分内容有所改变,为适应软件版本的变化,特此创作此专栏便于大家学习。本专栏使用软件为:SPSS25.0 本专栏所有的数据文件可在个人主页—…...

聊聊并发编程——并发容器和阻塞队列
目录 一.ConcurrentHashMap 1.为什么要使用ConcurrentHashMap? 2.ConcurrentHashMap的类图 3.ConcurrentHashMap的结构图 二.阻塞队列 Java中的7个阻塞队列 ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。 LinkedBlockingQueue…...

【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...

聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...

uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...

MySQL:分区的基本使用
目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区(Partitioning)是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分(分区)可以独立存储、管理和优化,…...
LangFlow技术架构分析
🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...

如何在Windows本机安装Python并确保与Python.NET兼容
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...

聚六亚甲基单胍盐酸盐市场深度解析:现状、挑战与机遇
根据 QYResearch 发布的市场报告显示,全球市场规模预计在 2031 年达到 9848 万美元,2025 - 2031 年期间年复合增长率(CAGR)为 3.7%。在竞争格局上,市场集中度较高,2024 年全球前十强厂商占据约 74.0% 的市场…...
第22节 Node.js JXcore 打包
Node.js是一个开放源代码、跨平台的、用于服务器端和网络应用的运行环境。 JXcore是一个支持多线程的 Node.js 发行版本,基本不需要对你现有的代码做任何改动就可以直接线程安全地以多线程运行。 本文主要介绍JXcore的打包功能。 JXcore 安装 下载JXcore安装包&a…...