当前位置: 首页 > article >正文

Android 轻松实现 增强版灵活的 滑动式表格视图

表格视图组件,支持:
1.  无标题模式:只有数据行也可以正常滑动
2.  两种滑动模式:固定第一列 或 全部滑动
3.  全面的样式自定义能力
4.  智能列宽计算

 1. 无标题模式支持


设置无标题:调用 

setHeaderData(null)

setHeaderData(emptyList())

自动调整:
    隐藏标题行相关视图
    智能计算列宽时忽略标题行
    保持数据行正常显示和滑动

 2. 两种滑动模式


固定第一列模式:

 tableView.setScrollMode(FlexibleTableView.ScrollMode.FIXED_FIRST_COLUMN)

    第一列垂直固定
    标题行和内容区域可水平滚动
    适合需要固定标识列的场景

全滑动模式:

    tableView.setScrollMode(FlexibleTableView.ScrollMode.FULL_SCROLL)

   - 整个表格可水平滚动
   - 标题行和内容区域同步滚动
   - 适合所有列同等重要的场景

3. 智能列宽计算


等宽模式:

    tableView.setEqualColumnWidth(true)

  - 所有列使用相同宽度

  - 宽度取所有列内容最大宽度

自适应模式:

 tableView.setEqualColumnWidth(false)

每列根据内容计算宽度
可设置最小宽度保证可读性最小宽度设置:

  // 设置第一列最小宽度tableView.setFirstColumnMinWidth(120) // 120dp// 设置其他列最小宽度tableView.setOtherColumnMinWidth(90) // 90dp

4. 全面的样式自定义

   //标题行样式:tableView.setHeaderTextColor(Color.WHITE)tableView.setHeaderBackgroundColor(Color.BLUE)//第一列样式:tableView.setFirstColumnTextColor(Color.DKGRAY)tableView.setFirstColumnBackgroundColor(Color.LTGRAY)//内容区域样式:tableView.setContentTextColor(Color.BLACK)tableView.setContentBackgroundColor(Color.WHITE)//网格线样式:tableView.setGridLineColor(Color.GRAY)

使用方法示例

class MainActivity : AppCompatActivity() {private lateinit var tableView: FlexibleTableViewoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)tableView = findViewById(R.id.tableView)// 1. 设置表格模式tableView.setScrollMode(FlexibleTableView.ScrollMode.FIXED_FIRST_COLUMN)// 2. 设置表格列宽配置tableView.setEqualColumnWidth(true) // 所有列等宽tableView.setFirstColumnMinWidth(120) // 第一列最小宽度120dptableView.setOtherColumnMinWidth(90)  // 其他列最小宽度90dp// 3. 设置样式tableView.setHeaderTextColor(Color.WHITE)tableView.setHeaderBackgroundColor(Color.parseColor("#3F51B5"))tableView.setFirstColumnTextColor(Color.DKGRAY)tableView.setFirstColumnBackgroundColor(Color.parseColor("#E8EAF6"))tableView.setContentTextColor(Color.BLACK)tableView.setContentBackgroundColor(Color.parseColor("#F5F5F5"))tableView.setGridLineColor(Color.parseColor("#9E9E9E"))// 4. 场景1: 有标题行的情况setupWithHeaders()// 5. 场景2: 无标题行的情况setupWithoutHeaders()// 6. 添加切换按钮setupModeSwitchButton()}private fun setupWithHeaders() {// 有标题行的数据val headers = listOf("产品", "一月", "二月", "三月", "四月", "五月", "六月")tableView.setHeaderData(headers)val products = listOf(listOf("智能手机", "1250", "1380", "1520", "1670", "1820", "1980"),listOf("笔记本电脑", "780", "820", "890", "920", "950", "980"),listOf("平板电脑", "620", "680", "710", "750", "790", "820"))tableView.setRowData(products)}private fun setupWithoutHeaders() {// 无标题行的数据tableView.setHeaderData(null) // 不设置标题行val data = listOf(listOf("张三", "90", "85", "95", "88", "92"),listOf("李四", "88", "92", "90", "85", "90"),listOf("王五", "78", "80", "85", "90", "86"),listOf("赵六", "92", "90", "88", "92", "94"),listOf("钱七", "76", "85", "80", "78", "82"))tableView.setRowData(data)}private fun setupModeSwitchButton() {val switchButton: Button = findViewById(R.id.switchModeButton)switchButton.setOnClickListener {val newMode = if (tableView.getScrollMode() == FlexibleTableView.ScrollMode.FIXED_FIRST_COLUMN) {FlexibleTableView.ScrollMode.FULL_SCROLL} else {FlexibleTableView.ScrollMode.FIXED_FIRST_COLUMN}tableView.setScrollMode(newMode)switchButton.text = if (newMode == FlexibleTableView.ScrollMode.FIXED_FIRST_COLUMN) {"切换到全滑动模式"} else {"切换到固定第一列模式"}}}
}

完整实现代码

FlexibleTableView

class FlexibleTableView @JvmOverloads constructor(context: Context,attrs: AttributeSet? = null,defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {enum class ScrollMode {FIXED_FIRST_COLUMN, // 固定第一列模式FULL_SCROLL        // 全部滑动模式}private val mContext: Context = contextprivate lateinit var rvHeader: RecyclerViewprivate lateinit var rvFirstColumn: RecyclerViewprivate lateinit var rvItems: RecyclerViewprivate lateinit var tvFirstHeader: TableCellprivate lateinit var headerAdapter: TableAdapterprivate lateinit var firstColumnAdapter: TableAdapterprivate lateinit var itemAdapter: TableAdapterprivate var headerList: List<String> = ArrayList()private val firstColumnList: MutableList<String> = ArrayList()private val itemList: MutableList<String> = ArrayList()// 样式配置@ColorInt private var headerTextColor = Color.WHITE@ColorInt private var headerBackgroundColor = Color.parseColor("#3F51B5")@ColorInt private var firstColumnTextColor = Color.DKGRAY@ColorInt private var firstColumnBackgroundColor = Color.parseColor("#E8EAF6")@ColorInt private var contentTextColor = Color.BLACK@ColorInt private var contentBackgroundColor = Color.parseColor("#F5F5F5")@ColorInt private var gridLineColor = Color.parseColor("#9E9E9E")// 宽度配置private var firstColumnMinWidth = dpToPx(120) // 第一列最小宽度private var otherColumnMinWidth = dpToPx(100) // 其他列最小宽度private var equalColumnWidth = true // 是否等宽显示// 滚动模式private var scrollMode = ScrollMode.FIXED_FIRST_COLUMN// 滚动位置缓存private var scrollX = 0private var scrollY = 0// 标题行可见性private var headerVisible = trueinit {initView()}private fun initView() {orientation = HORIZONTALremoveAllViews()when (scrollMode) {ScrollMode.FIXED_FIRST_COLUMN -> initFixedFirstColumnMode()ScrollMode.FULL_SCROLL -> initFullScrollMode()}}private fun initFixedFirstColumnMode() {// 固定第一列模式addView(createFixedColumnHeader())addView(createScrollableContentArea())setupAdapters()setupScrollSync()}private fun initFullScrollMode() {// 全滑动模式addView(createFullScrollContainer())setupAdapters()setupScrollSync()}private fun setupAdapters() {headerAdapter = TableAdapter(mContext)headerAdapter.isHeader(true)firstColumnAdapter = TableAdapter(mContext)itemAdapter = TableAdapter(mContext)if (::rvHeader.isInitialized) rvHeader.adapter = headerAdapterif (::rvFirstColumn.isInitialized) rvFirstColumn.adapter = firstColumnAdapterif (::rvItems.isInitialized) rvItems.adapter = itemAdapterif (::tvFirstHeader.isInitialized) {tvFirstHeader.setHeader(true)}}private fun setupScrollSync() {if (!::rvItems.isInitialized || !::rvFirstColumn.isInitialized) returnrvFirstColumn.addOnScrollListener(object : RecyclerView.OnScrollListener() {override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {super.onScrolled(recyclerView, dx, dy)if (recyclerView.scrollState != RecyclerView.SCROLL_STATE_IDLE) {rvItems.scrollBy(dx, dy)scrollY += dy}}})rvItems.addOnScrollListener(object : RecyclerView.OnScrollListener() {override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {super.onScrolled(recyclerView, dx, dy)if (recyclerView.scrollState != RecyclerView.SCROLL_STATE_IDLE) {rvFirstColumn.scrollBy(dx, dy)scrollY += dyscrollX += dx}}})}private fun createFixedColumnHeader(): LinearLayout {tvFirstHeader = TableCell(mContext, firstColumnMinWidth)tvFirstHeader.setGridLineColor(gridLineColor)tvFirstHeader.setTextColor(headerTextColor)tvFirstHeader.setHeaderBackgroundColor(headerBackgroundColor)tvFirstHeader.visibility = if (headerVisible) View.VISIBLE else View.GONEval lyHeader = LinearLayout(mContext)lyHeader.orientation = LinearLayout.VERTICALlyHeader.layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT)lyHeader.addView(tvFirstHeader)rvFirstColumn = RecyclerView(mContext)rvFirstColumn.layoutManager = LinearLayoutManager(mContext)lyHeader.addView(rvFirstColumn)return lyHeader}private fun createScrollableContentArea(): HorizontalScrollView {val layout = LinearLayout(mContext)layout.orientation = LinearLayout.VERTICALlayout.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)// 标题行容器rvHeader = RecyclerView(mContext)rvHeader.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)rvHeader.visibility = if (headerVisible) View.VISIBLE else View.GONEval headerManager = LinearLayoutManager(mContext)headerManager.orientation = LinearLayoutManager.HORIZONTALrvHeader.layoutManager = headerManagerlayout.addView(rvHeader)// 内容行容器rvItems = RecyclerView(mContext)rvItems.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)layout.addView(rvItems)val scrollView = HorizontalScrollView(mContext)scrollView.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)scrollView.addView(layout)scrollView.isFillViewport = truescrollView.overScrollMode = View.OVER_SCROLL_NEVERscrollView.isHorizontalScrollBarEnabled = falsereturn scrollView}private fun createFullScrollContainer(): HorizontalScrollView {val container = LinearLayout(mContext)container.orientation = LinearLayout.VERTICALcontainer.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)// 标题行(可滑动)rvHeader = RecyclerView(mContext)rvHeader.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)rvHeader.visibility = if (headerVisible) View.VISIBLE else View.GONEval headerManager = LinearLayoutManager(mContext)headerManager.orientation = LinearLayoutManager.HORIZONTALrvHeader.layoutManager = headerManagercontainer.addView(rvHeader)// 内容区域(包括第一列和其余列)val contentContainer = LinearLayout(mContext)contentContainer.orientation = LinearLayout.HORIZONTALcontentContainer.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)// 第一列(在完整滑动模式下也包含在可滚动区域)val firstColumnContainer = LinearLayout(mContext)firstColumnContainer.orientation = LinearLayout.VERTICALfirstColumnContainer.layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT)tvFirstHeader = TableCell(mContext, firstColumnMinWidth)tvFirstHeader.setGridLineColor(gridLineColor)tvFirstHeader.setTextColor(headerTextColor)tvFirstHeader.setHeaderBackgroundColor(headerBackgroundColor)tvFirstHeader.visibility = if (headerVisible) View.VISIBLE else View.GONEfirstColumnContainer.addView(tvFirstHeader)rvFirstColumn = RecyclerView(mContext)rvFirstColumn.layoutManager = LinearLayoutManager(mContext)firstColumnContainer.addView(rvFirstColumn)contentContainer.addView(firstColumnContainer)// 其余列rvItems = RecyclerView(mContext)rvItems.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)contentContainer.addView(rvItems)container.addView(contentContainer)val scrollView = HorizontalScrollView(mContext)scrollView.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)scrollView.addView(container)scrollView.isFillViewport = truescrollView.overScrollMode = View.OVER_SCROLL_NEVERscrollView.isHorizontalScrollBarEnabled = falsescrollView.scrollTo(scrollX, 0)return scrollView}fun setHeaderData(headerData: List<String>?) {if (headerData == null || headerData.isEmpty()) {// 没有标题行headerVisible = falseheaderList = emptyList()if (::tvFirstHeader.isInitialized) {tvFirstHeader.visibility = View.GONE}if (::rvHeader.isInitialized) {rvHeader.visibility = View.GONE}headerAdapter.setItemList(emptyList())return}// 有标题行headerVisible = trueheaderList = ArrayList(headerData)val headers = ArrayList(headerData)if (::tvFirstHeader.isInitialized) {tvFirstHeader.text = headers[0]tvFirstHeader.visibility = View.VISIBLE}headers.removeAt(0)headerAdapter.setItemList(headers)if (::rvHeader.isInitialized) {rvHeader.visibility = View.VISIBLE}if (::rvItems.isInitialized) {rvItems.layoutManager = GridLayoutManager(mContext, headerList.size - 1)}}fun setRowData(rowDataList: List<List<String>>) {if (rowDataList.isEmpty()) {// 清空数据firstColumnList.clear()itemList.clear()firstColumnAdapter.setItemList(emptyList())itemAdapter.setItemList(emptyList())return}// 确定列数:取第一行数据除去第一列后的列数val columnCount = rowDataList[0].size - 1// 设置GridLayoutManager的列数if (::rvItems.isInitialized) {rvItems.layoutManager = GridLayoutManager(mContext, columnCount)}// 处理数据firstColumnList.clear()itemList.clear()addRowData(rowDataList)}fun addRowData(rowDataList: List<List<String>>) {val list = ArrayList(rowDataList)for (rowData in list) {val row = ArrayList(rowData)if (row.isNotEmpty()) {firstColumnList.add(row[0])row.removeAt(0)itemList.addAll(row)}}firstColumnAdapter.setItemList(firstColumnList)itemAdapter.setItemList(itemList)if (::rvFirstColumn.isInitialized && ::rvItems.isInitialized) {rvFirstColumn.scrollTo(0, scrollY)rvItems.scrollTo(scrollX, scrollY)}calculateColumnWidths()}private fun calculateColumnWidths() {// 计算第一列宽度val firstColData = firstColumnList.toMutableList()if (headerVisible) {firstColData.add(tvFirstHeader.text.toString())}val firstColWidth = calculateColumnWidth(firstColData, firstColumnMinWidth)// 计算其他列宽度val otherColWidth = if (equalColumnWidth) {val maxOtherWidth = if (headerVisible) {maxOf(calculateColumnWidth(headerList, otherColumnMinWidth),calculateColumnWidth(itemList, otherColumnMinWidth))} else {calculateColumnWidth(itemList, otherColumnMinWidth)}maxOf(maxOtherWidth, otherColumnMinWidth)} else {if (headerVisible) {maxOf(calculateColumnWidth(headerList, otherColumnMinWidth),calculateColumnWidth(itemList, otherColumnMinWidth))} else {calculateColumnWidth(itemList, otherColumnMinWidth)}}// 设置宽度if (::tvFirstHeader.isInitialized) {tvFirstHeader.width = firstColWidth}firstColumnAdapter.setItemWidth(firstColWidth)headerAdapter.setItemWidth(otherColWidth)itemAdapter.setItemWidth(otherColWidth)}private fun calculateColumnWidth(data: List<String>, minWidth: Int): Int {if (data.isEmpty()) return minWidthvar maxWidth = minWidthval paint = Paint()paint.textSize = spToPx(14)for (text in data) {val textWidth = paint.measureText(text).toInt()val cellWidth = textWidth + dpToPx(20) // 加上内边距if (cellWidth > maxWidth) {maxWidth = cellWidth}}return maxWidth}fun getItemCount(): Int = firstColumnList.size * (if (headerVisible) headerList.size - 1 else 0)// 样式设置方法fun setHeaderTextColor(@ColorInt color: Int) {headerTextColor = colorif (::tvFirstHeader.isInitialized) {tvFirstHeader.setTextColor(color)}headerAdapter.setTextColor(color)}fun setHeaderBackgroundColor(@ColorInt color: Int) {headerBackgroundColor = colorif (::tvFirstHeader.isInitialized) {tvFirstHeader.setHeaderBackgroundColor(color)}headerAdapter.setBackgroundColor(color)}fun setFirstColumnTextColor(@ColorInt color: Int) {firstColumnTextColor = colorfirstColumnAdapter.setTextColor(color)}fun setFirstColumnBackgroundColor(@ColorInt color: Int) {firstColumnBackgroundColor = colorfirstColumnAdapter.setBackgroundColor(color)}fun setContentTextColor(@ColorInt color: Int) {contentTextColor = coloritemAdapter.setTextColor(color)}fun setContentBackgroundColor(@ColorInt color: Int) {contentBackgroundColor = coloritemAdapter.setBackgroundColor(color)}fun setGridLineColor(@ColorInt color: Int) {gridLineColor = colorif (::tvFirstHeader.isInitialized) {tvFirstHeader.setGridLineColor(color)}headerAdapter.setGridLineColor(color)firstColumnAdapter.setGridLineColor(color)itemAdapter.setGridLineColor(color)}// 宽度设置方法fun setEqualColumnWidth(enabled: Boolean) {equalColumnWidth = enabledcalculateColumnWidths()}fun setFirstColumnMinWidth(minWidthDp: Int) {firstColumnMinWidth = dpToPx(minWidthDp)calculateColumnWidths()}fun setOtherColumnMinWidth(minWidthDp: Int) {otherColumnMinWidth = dpToPx(minWidthDp)calculateColumnWidths()}// 滚动模式设置fun setScrollMode(mode: ScrollMode) {if (scrollMode != mode) {// 保存当前滚动位置scrollX = 0scrollY = 0scrollMode = modeinitView()// 重新应用数据if (headerList.isNotEmpty()) {setHeaderData(headerList)}if (firstColumnList.isNotEmpty()) {setRowData(firstColumnList.map { listOf(it) })}}}fun getScrollMode(): ScrollMode = scrollMode// 单位转换工具private fun dpToPx(dp: Int): Int {return (dp * context.resources.displayMetrics.density).toInt()}private fun spToPx(sp: Int): Float {return sp * context.resources.displayMetrics.scaledDensity}
}
TableAdapter
@SuppressLint("NotifyDataSetChanged")
class TableAdapter(private val mContext: Context) : RecyclerView.Adapter<TableAdapter.MyViewHolder>() {private var mItemList: List<String> = ArrayList()private var itemWidth = 0private var isHeader = falseprivate var textColor = Color.BLACKprivate var backgroundColor = Color.WHITEprivate var gridLineColor = Color.GRAYfun setItemWidth(width: Int) {itemWidth = widthnotifyDataSetChanged()}fun setTextColor(color: Int) {textColor = colornotifyDataSetChanged()}fun setBackgroundColor(color: Int) {backgroundColor = colornotifyDataSetChanged()}fun setGridLineColor(color: Int) {gridLineColor = colornotifyDataSetChanged()}fun setItemList(itemList: List<String>) {mItemList = itemListnotifyDataSetChanged()}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {val cell = TableCell(mContext, itemWidth)cell.setHeader(isHeader)cell.setGridLineColor(gridLineColor)return MyViewHolder(cell)}override fun onBindViewHolder(holder: MyViewHolder, position: Int) {val item = mItemList[position]val tv = holder.itemView as TableCelltv.text = itemtv.setTextColor(textColor)tv.setCellBackgroundColor(backgroundColor)// 如果是表头行,应用特殊样式if (isHeader) {tv.setTextColor(textColor)tv.setHeaderBackgroundColor(backgroundColor)}}override fun getItemCount(): Int = mItemList.sizefun isHeader(isHeader: Boolean) {this.isHeader = isHeader}inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
}
TableCell
class TableCell @JvmOverloads constructor(context: Context,private var width: Int = ViewGroup.LayoutParams.WRAP_CONTENT
) : AppCompatTextView(context) {private var isHeader = falseprivate var gridLineColor = Color.GRAYprivate var headerBackgroundColor = Color.parseColor("#3F51B5")private var cellBackgroundColor = Color.WHITEinit {initView()}private fun initView() {setBackgroundColor(cellBackgroundColor)val params = LinearLayout.LayoutParams(width - 2, ViewGroup.LayoutParams.WRAP_CONTENT)params.setMargins(0, 0, 2, 2)layoutParams = paramstextSize = 14fgravity = Gravity.CENTERsetPadding(dpToPx(10), dpToPx(10), dpToPx(10), dpToPx(10))}fun setHeader(isHeader: Boolean) {this.isHeader = isHeadersetBackgroundColor(if (isHeader) headerBackgroundColor else cellBackgroundColor)setPadding(dpToPx(10), dpToPx(if (isHeader) 15 else 10), dpToPx(10), dpToPx(if (isHeader) 15 else 10))}fun setHeaderBackgroundColor(color: Int) {headerBackgroundColor = colorif (isHeader) {setBackgroundColor(color)}}fun setCellBackgroundColor(color: Int) {cellBackgroundColor = colorif (!isHeader) {setBackgroundColor(color)}}fun setGridLineColor(color: Int) {gridLineColor = colorinvalidate()}override fun setWidth(pixels: Int) {width = pixelsrefreshWidth()}private fun refreshWidth() {val params = layoutParams as LinearLayout.LayoutParamsparams.width = width - 2params.setMargins(0, 0, 2, 2)layoutParams = params}override fun onDraw(canvas: Canvas) {// 绘制网格线val paint = Paint()paint.color = gridLineColorpaint.strokeWidth = 1f// 绘制右边框canvas.drawLine(width.toFloat() - 2, 0f, width.toFloat() - 2, height.toFloat(), paint)// 绘制下边框canvas.drawLine(0f, height.toFloat() - 2, width.toFloat(), height.toFloat() - 2, paint)// 表头特殊样式if (isHeader) {paint.style = Paint.Style.FILL_AND_STROKEpaint.strokeWidth = 1.5f}super.onDraw(canvas)}private fun dpToPx(dp: Int): Int {return (dp * context.resources.displayMetrics.density).toInt()}
}
在布局文件中添加表格视图
 <com.yourpackage.FlexibleTableViewandroid:id="@+id/scrollTableView"android:layout_width="match_parent"android:layout_height="wrap_content" />

相关文章:

Android 轻松实现 增强版灵活的 滑动式表格视图

表格视图组件&#xff0c;支持&#xff1a; 1. 无标题模式&#xff1a;只有数据行也可以正常滑动 2. 两种滑动模式&#xff1a;固定第一列 或 全部滑动 3. 全面的样式自定义能力 4. 智能列宽计算 1. 无标题模式支持 设置无标题&#xff1a;调用 setHeaderData(null) 或 …...

寄存器模型生成:从手工到自动化

写代码这件事&#xff0c;总是充满了矛盾。你想要完美控制每一个细节&#xff0c;但又希望能够批量生产。寄存器模型的生成&#xff0c;恰恰体现了这种矛盾。 手工编写的局限性 我们完全可以手工编写一个寄存器模型。代码写出来&#xff0c;功能也能实现&#xff0c;看起来一切…...

国标GB28181视频平台EasyGBS视频实时监控系统打造换热站全景可视化管理方案

一、方案背景​ 在城市供热体系中&#xff0c;换热站作为连接热源与用户的核心枢纽&#xff0c;其运行稳定性直接影响供热质量。面对供热规模扩大与需求升级&#xff0c;传统人工巡检模式暴露出效率低、响应慢、监测不足等问题。基于GB28181协议的EasyGBS视频实时监控系统&…...

docker生命周期

进入容器里面 docker pull ubuntu # 获取ubtuntu镜像 docker run ubtuntu # -i 交互式命令操作&#xff0c;-t 开启一个终端 bash 进入容器后&#xff0c;执行的命令 docker run -it ubtuntu bash...

鸿蒙缺少WMIC missing WMIC

在DecEco Studio中选择模拟器的时候会弹出“可能会导致设备管理功能失效。请检查并安装 WMIC”。 在启动鸿蒙模拟器时报&#xff1a;missing WMIC missing WMIC lt may cause the device management function to fail. Please check and install WMIC. 解决方案&#xff1a…...

25.6.5学习总结

归并排序&#xff08;Merge Sort&#xff09; 1. 概述 归并排序是一种基于分治思想的排序算法。它通过递归的方式&#xff0c;将待排序的数组不断分割成两半&#xff0c;直到每个子数组只剩一个元素&#xff08;自然排序&#xff09;&#xff1b;然后&#xff0c;将这些子数组…...

Spring Boot 使用 SLF4J 实现控制台输出与分类日志文件管理

概述 在日常的 Java 项目开发中&#xff0c;日志是最重要的调试与排查手段之一。为了便于开发时实时查看&#xff0c;同时在生产中追踪问题&#xff0c;我们通常希望实现以下日志管理目标&#xff1a; ✅ 控制台实时输出日志&#xff0c;方便开发调试✅ 日志根据级别分类保存…...

linux_centos7.x的ifconfig命令显示内容详解

这是一段在Linux系统中执行 ifconfig 命令后得到的网络接口信息输出。ifconfig 命令用于显示或配置网络接口的参数。以下是对输出中各个网络接口信息的详细解释&#xff1a; 1. ens33 接口 ​​状态标志​​&#xff1a;flags4163<UP,BROADCAST,RUNNING,MULTICAST> 表示…...

CentOS 7 如何pip3安装pyaudio?

CentOS 7 如何pip3安装pyaudio&#xff1f; # 先将yum软件源改为阿里云镜像源 http://mirrors.aliyun.com/centos-vault/7.9.2009/ bash <(curl -sSL https://linuxmirrors.cn/main.sh) # 基于一键换源脚本&#xff0c;全部回车即可# pip3安装模块是从源码构建&#xff08;…...

6.5本日总结

一、英语 复习默写list8list21&#xff0c;订正翻译07年第二篇阅读 二、数学 学习线代第一讲 三、408 学习计组2.2&#xff0c;写计组习题 四、总结 这篇阅读全对&#xff0c;整体题目不算难&#xff0c;但是对文意的翻译差点&#xff0c;后续要多练习句子翻译 五、明日…...

【个人笔记】数据库原理(西电)

第一章 ER图和关系分解见课本p69 ER图是常用的 概念模型 方形&#xff1a;实体圆形&#xff1a;属性菱形&#xff1a;关系 常用的逻辑模型 说白了&#xff1a;增删改查 几种数据模型的基本概念 层次模型&#xff1a;树状结构【只能处理一对多的关系&#xff0c;只有沿着从根…...

嵌入式学习之系统编程(十)网络编程之TCP传输控制协议

目录 一、网络模型 1、服务器/客户端模型 2、C/S与B/S区别 3、P2P模型 二、TCP&#xff08;传输控制协议&#xff09; &#xff08;一&#xff09;TCP概述 &#xff08;二&#xff09;TCP的特征&#xff08;面问高频问题&#xff09; 1、有链接 三次握手&#xff1a;建…...

【react+antd+vite】优雅的引入svg和阿里巴巴图标

1.安装相关包 由于是vite项目&#xff0c;要安装插件来帮助svg文件引入进来&#xff0c;否则会失败 npm下载包 npm i vite-plugin-svgr vite.config.ts文件内&#xff1a; import svgr from "vite-plugin-svgr"; //... export default defineConfig({plugins: …...

3D动画在微信小程序的实现方法

微信小程序支持通过多种方式实现3D动画效果&#xff0c;主要包括使用CSS3、WebGL及第三方库。以下为具体方法&#xff1a; 一. 使用CSS3 Transform实现基础3D动画详解 CSS3的transform属性提供了强大的2D/3D变换功能&#xff0c;通过简单的代码就能实现复杂的视觉效果。在小程…...

计算机网络 | 1.2 计算机网络体系结构与参考模型

计算机网络体系结构与参考模型 目录 计算机网络体系结构与参考模型 【思维导图】 1、计算机的分层结构 1、为什么要分层&#xff1f; 2、什么是计算机网络体系结构 2、计算机网络协议、接口和服务 1&#xff09;协议&#xff1a; 2&#xff09;接口&#xff1a; 3…...

网心云 OEC/OECT 笔记(1) 拆机刷入Armbian固件

目录 网心云 OEC/OECT 笔记(1) 拆机刷入Armbian固件网心云 OEC/OECT 笔记(2) 运行RKNN程序 外观 内部 PCB正面 PCB背面 PCB背面 RK3566 1Gbps PHY 配置 OEC 和 OECT(OEC-turbo) 都是基于瑞芯微 RK3566/RK3568 的网络盒子, 没有HDMI输入输出. 硬件上 OEC 和 OECT…...

【Web应用】若依框架:基础篇17二次开发-项目名称修改-新建业务模块

文章目录 ⭐前言⭐一、课程讲解⭐二、自己手动实操⭐总结 标题详情作者JosieBook头衔CSDN博客专家资格、阿里云社区专家博主、软件设计工程师博客内容开源、框架、软件工程、全栈&#xff08;,NET/Java/Python/C&#xff09;、数据库、操作系统、大数据、人工智能、工控、网络、…...

C获取unix操作系统的信息

在 C 语言中获取 Linux 操作系统的架构类型(如 x86_64)、系统位数(32/64位)、内核信息等,可以通过多种方式实现。下面是几种常见的方法: ✅ 方法一:使用 uname 获取系统信息 #include <stdio.h> #include <sys/utsname.h>int main(...

MQTT入门实战宝典:从零起步掌握物联网核心通信协议

MQTT入门实战宝典&#xff1a;从零起步掌握物联网核心通信协议 前言 物联网时代&#xff0c;万物互联已成为现实&#xff0c;而MQTT协议作为这个时代的"数据总线"&#xff0c;正默默支撑着从智能家居到工业物联的各类应用场景。本文将带你揭开MQTT的神秘面纱&#…...

05【Linux经典命令】Linux 用户管理全面指南:从基础到高级操作

目录 前言 1 Linux用户管理基础概念 1.1 Linux用户类型 1.2 用户相关配置文件 1.3 UID与GID 2 用户创建与管理 2.1 创建用户 2.2 设置用户密码 3 用户权限管理 3.1 授予sudo权限 3.2 以其他用户身份执行命令 4 用户信息查询 4.1 查看用户基本信息 4.2 查看用户所…...

POP3、IMAP、SMTP:三大邮件协议核心差异与应用场景解析

## 一、协议概述与核心功能 电子邮件系统的运行依赖三大核心协议&#xff1a;**POP3**&#xff08;Post Office Protocol 3&#xff09;、**IMAP**&#xff08;Internet Message Access Protocol&#xff09;和**SMTP**&#xff08;Simple Mail Transfer Protocol&#xff09;…...

HarmonyOS5 仓颉入门:和 ArkTs 互操作

现在一般的场景是在已有 ArkTs 库中使用仓颉&#xff0c;所以可以将仓颉代码封装为 ArkTs 库&#xff0c;提供给外部使用。 原理就是互操作宏解析被注解修饰的仓颉代码&#xff0c;会自动生成 ArkTs 声明文件和互操作层代码。 使用步骤&#xff1a; 1.在 cj 文件中&#xff…...

【Git 合并冲突解决记录:从 “refusing to merge unrelated histories“ 到批量冲突处理】

Git 合并冲突解决记录&#xff1a;从 “refusing to merge unrelated histories” 到批量冲突处理 前言 作为开发者&#xff0c;我们经常会遇到各种 Git 问题&#xff0c;其中最让人头疼的莫过于 fatal: refusing to merge unrelated histories 这个错误。最近在项目开发中遇…...

使用vite-plugin-html在 HTML 文件中动态注入数据,如元数据、环境变量、标题

vite-plugin-html 是一个用于 Vite 构建工具的插件&#xff0c;它可以帮助你在构建过程中动态注入一些 HTML 内容&#xff0c;比如标题、元数据、环境变量等。通过使用这个插件&#xff0c;你可以根据项目的配置和环境变量自动生成带有动态内容的 HTML 文件&#xff0c;适用于 …...

Kinova机械臂在Atlas手术导航系统中的核心作用

Kinova机械臂凭借其高精度运动控制和智能交互功能&#xff0c;成为Atlas手术导航系统的重要组成部分。该系统通过实时跟踪患者位置和精确规划手术路径&#xff0c;提高了医疗过程的精准性与效率。灵活的设计使外科医生能够更轻松地操作复杂的手术工具&#xff0c;从而提升患者安…...

C++——智能指针 auto_ptr

一、RAII思想的引入 #include <iostream> using namespace std;#if 0 // C中动态申请的资源需要用户自己手动释放 // 如果操作不当&#xff0c;容易造成内存泄漏 // 能否做到让资源自动被释放&#xff1a;RAII // RAII : 将资源交给对象管理&#xff0c;对象被销毁时自动…...

.Net Framework 4/C# System.IO 命名空间(文件的输入输出)

一、Path 类 Path 类是一个静态类,只能通过类名访问它的静态成员。 获得文件的名字,可以用 GetFileName,返回的是具有扩展名的指定路径字符串的文件名,也可以用 GetFileNameWithoutExtension,返回的是不具有扩展名的指定路径字符串的文件名。 获得文件夹的名字,可以用 G…...

图像分类进阶:从基础到专业 (superior哥AI系列第10期)

图像分类进阶&#xff1a;从基础到专业 &#x1f680; 前言 &#x1f44b; 哈喽&#xff0c;各位深度学习的探索者们&#xff01;我是你们的老朋友superior哥 &#x1f60e; 经过前面九篇文章的学习&#xff0c;相信大家对深度学习的基础概念、神经网络架构、以及训练部署都…...

性能优化之SSR、SSG

一、SSR和SSG介绍 SSR&#xff08;Server-Side Rendering&#xff0c;服务端渲染&#xff09;和 SSG&#xff08;Static Site Generation&#xff0c;静态站点生成&#xff09;是现代前端框架&#xff08;如 Next.js、Nuxt.js、Gatsby&#xff09;的核心渲染策略&#xff0c;用…...

【C语言】字符与字符串

在 C 语言中&#xff0c;字符&#xff08;Character&#xff09; 和 字符串&#xff08;String&#xff09; 是两个不同但相关的概念。下面详细介绍它们的定义、存储方式和使用方法&#xff1a; 一、字符&#xff08;Character&#xff09; 1. 定义与存储 基本类型&#xff…...