Android 学习之多状态布局的一种实现方案

2023-11-03

开发应用的过程中,首页的控件越来越多,布局文件的代码已经到了爆表的程度,而且不同状态下首页各个控件的 Visibility 不同,每次新增状态都是一件头疼的事情,时常遗漏控件导致出错,和 YYY 大佬交流讨论后他给出了一种巧妙的方案,特此学习记录一下!

一、多状态布局

此处的多状态布局是指一个约束布局中,有很多的子布局和控件(Demo 中仅使用控件,嵌套子布局效果也是一样的),这些布局和控件根据首页状态的不同,各自的显示隐藏状态也不同,形成了不同的布局呈现。
布局展示

二、实现思路

(一)方案简述

  1. 需求是根据首页的状态不一样,触发不同的控件的隐藏显示状态的改变。
  2. 可以考虑使用 0 和 1 来表示某个控件的隐藏显示,那么一个控件就可以使用一个 bit 来控制,0 表示隐藏 1 表示显示,多个控件的状态组合在一起成为了一串 0/1 二进制码。
  3. 考虑到首页控件的数量 Demo 使用 Int 类型(Int 类型在 kotlin 中是 32 位,可以表示 32 个控件的可见性状态),如果控件数量过多则可以考虑使用 Long 类型。
  4. 不同的 Int 值表示不同的首页状态,状态改变时更新当前状态的 Int 值,首页布局随之发生改变。

(二)具体设计

  1. 按照顺序指定 Int 的每一位代表的首页控件,先考虑某一个控件单独显示,其他控件均隐藏的情况
位数 控件 二进制码 十进制值
低位第一位 tv_author_name 0000 0000 0000 0000 0000 0000 0000 0001 1
低位第二位 tv_author_introduction 0000 0000 0000 0000 0000 0000 0000 0010 2
低位第三位 tv_tool_box 0000 0000 0000 0000 0000 0000 0000 0100 4
低位第四位 tv_folder 0000 0000 0000 0000 0000 0000 0000 1000 8
低位第五位 iv_zoom_in 0000 0000 0000 0000 0000 0000 0001 0000 16
低位第六位 iv_zoom_out 0000 0000 0000 0000 0000 0000 0010 0000 32
低位第七位 iv_close 0000 0000 0000 0000 0000 0000 0100 0000 64
低位第八位 iv_android 0000 0000 0000 0000 0000 0000 1000 0000 128
低位第九位 tv_tab_first 0000 0000 0000 0000 0000 0001 0000 0000 256
低位第十位 tv_tab_second 0000 0000 0000 0000 0000 0010 0000 0000 512
低位第十一位 tv_tab_third 0000 0000 0000 0000 0000 0100 0000 0000 1024

观察发现,其实就是第一位的 1 不断的向左移动,这就让人想起了位运算中的左移运算[1],比起直接使用十进制数来赋值表示要准确明了许多

private const val INDEX = 1
const val INDEX_VIEW_AUTHOR_NAME :Int = INDEX shl 0
const val INDEX_VIEW_AUTHOR_INTRODUCTION :Int = INDEX shl 1
const val INDEX_VIEW_TOOL_BOX :Int = INDEX shl 2
const val INDEX_VIEW_FOLDER :Int = INDEX shl 3
const val INDEX_VIEW_ZOOM_IN :Int = INDEX shl 4
const val INDEX_VIEW_ZOOM_OUT :Int = INDEX shl 5
const val INDEX_VIEW_CLOSE :Int = INDEX shl 6
const val INDEX_VIEW_ANDROID :Int = INDEX shl 7
const val INDEX_VIEW_TAB_FIRST :Int = INDEX shl 8
const val INDEX_VIEW_TAB_SECOND :Int = INDEX shl 9
const val INDEX_VIEW_TAB_THIRD :Int = INDEX shl 10
  1. 首页各个控件单独的可见状态我们已经表示完毕,那首页的不同状态该如何表示?
    假设,我们现在要求首页进入全屏状态,但是希望能够保留作者姓名和作者简介,那么我们的页面状态是 0000 0000 0000 0000 0000 0000 0000 0011
    如果要求点击作者姓名进入简洁模式,即显示作者姓名、作者简介、Tab 1、Tab 2 和 Tab 3,隐藏关闭按钮,文件夹按钮,工具箱按钮,放大缩小按钮和中心安卓图标,那么我们的页面状态是 0000 0000 0000 0000 0000 0111 0000 0011
    观察发现,其实就是需要展示的控件彼此之间做一下位运算中的或运算[2]
// 全屏模式
const val INDEX_FULL_SCREEN = INDEX_VIEW_AUTHOR_NAME or INDEX_VIEW_AUTHOR_INTRODUCTION
// 简洁模式
const val INDEX_CONCISE_MODE =
    INDEX_VIEW_AUTHOR_NAME or
    INDEX_VIEW_AUTHOR_INTRODUCTION or
    INDEX_VIEW_TAB_FIRST or
    INDEX_VIEW_TAB_SECOND or
    INDEX_VIEW_TAB_THIRD
  1. 现在可以通过控件显示或者隐藏来决定当前布局的状态,那么反过来当拿到布局状态,如何确定这个状态下,各个控件的可见性情况?
    答案是使用位运算中的与运算[3],将需要确定的控件的单独显示状态对应的 Int 值与表示当前首页状态的 Int 值做与运算,如果和这个控件单独显示的状态值相同表示这个控件是显示的,不同则表示它是隐藏的,
    比如说要确定全屏模式下,作者姓名是否展示,可以这样做:
    INDEX_FULL_SCREEN and INDEX_VIEW_AUTHOR_NAME
    0000 0000 0000 0000 0000 0000 0000 0011 and 0000 0000 0000 0000 0000 0000 0000 0001
    结果是 0000 0000 0000 0000 0000 0000 0000 0001 表示作者姓名是显示的

  2. 现在可以表示不同状态下的首页布局的情况了,那么还需要考虑的就是不同状态的切换了
    1.)两种状态差异过大,直接切换,这种情况就可以直接根据不同状态的值进行控件的显示与隐藏操作
    2.)比当前状态多或者少展示一个控件
    这种情况下当然可以根据不同状态的值进行显示与隐藏操作,但是状态粒度太小对于我们来说后期维护会非常吃力,布局的状态会成指数增加,
    所以当两种状态变化不大,或者是某个控件在多种状态下都有可能显示或者隐藏,我们采取另外的策略,即在当前状态下补充进去或者筛减出来
    如何补充呢?根据上面第二点布局的状态表示,我们可以知道当前布局状态就是使用或运算将仅显示单个控件的状态组合在一起,那么补充进来一个控件就是在现有的基础上与目标控件进行或运算
    例如:在简洁模式的基础上,显示关闭按钮:
    INDEX_CONCISE_MODE or INDEX_VIEW_CLOSE
    0000 0000 0000 0000 0000 0111 0000 0011 or 0000 0000 0000 0000 0000 0000 0100 0000
    =》0000 0000 0000 0000 0000 0111 0100 0011
    如何筛减呢? 本着相同为 0 不同为 1 的原则,想要排除一个显示控件,需要将当前状态和目标控件的单独显示状态做位运算中的异或运算[4]
    例如:在简洁模式且显示关闭按钮的基础上,隐藏关闭按钮:
    (INDEX_CONCISE_MODE or INDEX_VIEW_CLOSE) xor INDEX_VIEW_CLOSE
    0000 0000 0000 0000 0000 0111 0100 0011 or 0000 0000 0000 0000 0000 0000 0100 0000
    =》0000 0000 0000 0000 0000 0111 0000 0011

  3. 到目前为止,情况基本上都考虑完善了,接下来就是实现上需要注意的地方:
    1.)我们可以使用 Map 来收集控件对象的实例,Key 就是单个控件展示的状态值,Value 就是控件对象实例
    2.)要预先写好几种状态的值,如初始状态,全屏模式,简洁模式
    3.)使用一个类来统一管理首页的状态和展示

三、Demo 代码

GitHub 代码 https://github.com/NicholasHzf/LayerVisibility

(一)布局文件

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tv_change_state"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="状态切换"
        android:textColor="@color/black"
        android:textSize="18sp"
        android:background="#8E8F8D"
        android:padding="4dp"
        app:layout_constraintHorizontal_chainStyle="spread_inside"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toStartOf="@id/tv_add_close"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_add_close"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="添加「关闭」按钮"
        android:textColor="@color/black"
        android:textSize="18sp"
        android:background="#8E8F8D"
        android:padding="4dp"
        app:layout_constraintStart_toEndOf="@id/tv_change_state"
        app:layout_constraintEnd_toStartOf="@id/tv_reduce_close"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_reduce_close"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="移除「关闭」按钮"
        android:textColor="@color/black"
        android:textSize="18sp"
        android:background="#8E8F8D"
        android:padding="4dp"
        app:layout_constraintStart_toEndOf="@id/tv_add_close"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_author_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Nicholas.Hzf"
        android:textColor="@color/black"
        android:textSize="18sp"
        android:background="#0088ff"
        android:padding="14dp"
        android:layout_marginTop="10dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tv_change_state" />

    <TextView
        android:id="@+id/tv_author_introduction"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="每天进步一点点"
        android:textColor="#d1d1d1"
        android:textSize="14sp"
        android:paddingVertical="7dp"
        android:paddingHorizontal="14dp"
        android:background="#0088ff"
        android:layout_marginTop="10dp"
        app:layout_constraintStart_toStartOf="@id/tv_author_name"
        app:layout_constraintTop_toBottomOf="@id/tv_author_name" />

    <TextView
        android:id="@+id/tv_tool_box"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="工具箱"
        android:textColor="@color/white"
        android:textStyle="bold"
        android:textSize="16sp"
        android:padding="14dp"
        android:background="@color/black"
        android:layout_marginTop="10dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tv_reduce_close" />

    <TextView
        android:id="@+id/tv_folder"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="文件夹"
        android:textColor="@color/white"
        android:textStyle="bold"
        android:textSize="16sp"
        android:padding="14dp"
        android:background="@color/black"
        android:layout_marginEnd="7dp"
        android:layout_marginTop="10dp"
        app:layout_constraintTop_toBottomOf="@id/tv_reduce_close"
        app:layout_constraintEnd_toStartOf="@id/tv_tool_box"/>

    <ImageView
        android:id="@+id/iv_zoom_in"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:src="@drawable/ic_baseline_add_24"
        android:background="@color/black"
        android:layout_marginBottom="10dp"
        app:layout_constraintVertical_chainStyle="packed"
        app:layout_constraintBottom_toTopOf="@id/iv_zoom_out"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/iv_zoom_out"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:src="@drawable/ic_baseline_reduce_24"
        android:background="@color/black"
        app:layout_constraintTop_toBottomOf="@id/iv_zoom_in"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent" />

    <ImageView
        android:id="@+id/iv_close"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_marginStart="16dp"
        android:background="@color/black"
        android:src="@drawable/ic_baseline_close_24"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.8" />

    <ImageView
        android:id="@+id/iv_android"
        android:layout_width="84dp"
        android:layout_height="84dp"
        android:src="@drawable/ic_baseline_android_24"
        android:background="#0088ff"
        android:padding="14dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_tab_first"
        android:layout_width="0dp"
        android:layout_height="56dp"
        android:text="TAB1"
        android:textColor="@color/black"
        android:textSize="18sp"
        android:background="#8BC34A"
        android:gravity="center"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toStartOf="@id/tv_tab_second"/>

    <TextView
        android:id="@+id/tv_tab_second"
        android:layout_width="0dp"
        android:layout_height="56dp"
        android:text="TAB2"
        android:textColor="@color/black"
        android:textSize="18sp"
        android:background="#8BC34A"
        android:gravity="center"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toEndOf="@id/tv_tab_first"
        app:layout_constraintEnd_toStartOf="@id/tv_tab_third"/>

    <TextView
        android:id="@+id/tv_tab_third"
        android:layout_width="0dp"
        android:layout_height="56dp"
        android:text="TAB3"
        android:textColor="@color/black"
        android:textSize="18sp"
        android:background="#8BC34A"
        android:gravity="center"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/tv_tab_second" />

</androidx.constraintlayout.widget.ConstraintLayout>

(二)首页状态管理器

package com.hzf.layerproject

import android.view.View
import androidx.core.view.isVisible
import kotlin.random.Random

/**
 * @ClassName: IndexStateManager
 * @Description: 首页状态管理类
 * @Author: Nicholas.hzf
 * @Date: 2022/8/13 18:01 Created
 */
object IndexStateManager {

    private val mViewMap by lazy {
        HashMap<Int, View>()
    }

    private const val VIEW_SIZE = 11
    private const val INDEX = 1
    const val INDEX_VIEW_AUTHOR_NAME :Int = INDEX shl 0
    const val INDEX_VIEW_AUTHOR_INTRODUCTION :Int = INDEX shl 1
    const val INDEX_VIEW_TOOL_BOX :Int = INDEX shl 2
    const val INDEX_VIEW_FOLDER :Int = INDEX shl 3
    const val INDEX_VIEW_ZOOM_IN :Int = INDEX shl 4
    const val INDEX_VIEW_ZOOM_OUT :Int = INDEX shl 5
    const val INDEX_VIEW_CLOSE :Int = INDEX shl 6
    const val INDEX_VIEW_ANDROID :Int = INDEX shl 7
    const val INDEX_VIEW_TAB_FIRST :Int = INDEX shl 8
    const val INDEX_VIEW_TAB_SECOND :Int = INDEX shl 9
    const val INDEX_VIEW_TAB_THIRD :Int = INDEX shl 10

    const val PRIMARY_STATE =
        INDEX_VIEW_AUTHOR_NAME or
        INDEX_VIEW_AUTHOR_INTRODUCTION or
        INDEX_VIEW_TOOL_BOX or
        INDEX_VIEW_FOLDER or
        INDEX_VIEW_ZOOM_IN or
        INDEX_VIEW_ZOOM_OUT or
        INDEX_VIEW_ANDROID or
        INDEX_VIEW_TAB_FIRST or
        INDEX_VIEW_TAB_SECOND or
        INDEX_VIEW_TAB_THIRD

    const val INDEX_FULL_SCREEN =
        INDEX_VIEW_AUTHOR_NAME or INDEX_VIEW_AUTHOR_INTRODUCTION

    const val INDEX_CONCISE_MODE =
        INDEX_VIEW_AUTHOR_NAME or
                INDEX_VIEW_AUTHOR_INTRODUCTION or
                INDEX_VIEW_TAB_FIRST or
                INDEX_VIEW_TAB_SECOND or
                INDEX_VIEW_TAB_THIRD

    private var CURRENT_STATE = PRIMARY_STATE

    fun initViewMap(viewList: List<View>){
        if (viewList.size != VIEW_SIZE){
            throw Exception("View 数量错误")
        }
        mViewMap.clear()
        CURRENT_STATE = PRIMARY_STATE

        mViewMap[INDEX_VIEW_AUTHOR_NAME] = viewList[0]
        mViewMap[INDEX_VIEW_AUTHOR_INTRODUCTION] = viewList[1]
        mViewMap[INDEX_VIEW_TOOL_BOX] = viewList[2]
        mViewMap[INDEX_VIEW_FOLDER] = viewList[3]
        mViewMap[INDEX_VIEW_ZOOM_IN] = viewList[4]
        mViewMap[INDEX_VIEW_ZOOM_OUT] = viewList[5]
        mViewMap[INDEX_VIEW_CLOSE] = viewList[6]
        mViewMap[INDEX_VIEW_ANDROID] = viewList[7]
        mViewMap[INDEX_VIEW_TAB_FIRST] = viewList[8]
        mViewMap[INDEX_VIEW_TAB_SECOND] = viewList[9]
        mViewMap[INDEX_VIEW_TAB_THIRD] = viewList[10]

        updateViews()
    }

    fun updateViews(){
        mViewMap.keys.forEach { key ->
            mViewMap[key]?.isVisible = (key and CURRENT_STATE) == key
        }
    }

    fun destroyViews(){
        mViewMap.clear()
        CURRENT_STATE = 0
    }

    fun showView(view: Int){
        CURRENT_STATE = CURRENT_STATE or view
        updateViews()
    }

    fun hideView(view: Int){
        CURRENT_STATE = CURRENT_STATE xor view
        updateViews()
    }

    fun changeState(state: Int){
        CURRENT_STATE = state
        updateViews()
    }

    fun changeStateRandom(){
        val random = Random.nextInt(3)
        CURRENT_STATE = when(random){
            0 -> PRIMARY_STATE
            1 -> INDEX_FULL_SCREEN
            2 -> INDEX_CONCISE_MODE
            else -> PRIMARY_STATE
        }
        updateViews()
    }

    fun getCurrentState() = CURRENT_STATE

}

(三)首页代码

package com.hzf.layerproject

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.hzf.layerproject.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        IndexStateManager.initViewMap(mutableListOf(
            binding.tvAuthorName,binding.tvAuthorIntroduction,
            binding.tvToolBox,binding.tvFolder,
            binding.ivZoomIn,binding.ivZoomOut,
            binding.ivClose,binding.ivAndroid,
            binding.tvTabFirst,binding.tvTabSecond,binding.tvTabThird
        ))

        binding.tvChangeState.setOnClickListener {
            IndexStateManager.changeStateRandom()
        }

        binding.tvAddClose.setOnClickListener {
            IndexStateManager.showView(IndexStateManager.INDEX_VIEW_CLOSE)
        }

        binding.tvReduceClose.setOnClickListener {
            IndexStateManager.hideView(IndexStateManager.INDEX_VIEW_CLOSE)
        }
    }
}

【1】左移运算:将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)
例如:INDEX shl 10
0000 0000 0000 0000 0000 0000 0000 0001 左移 10 位,得到
0000 0000 0000 0000 0000 01000 0000 0000
【2】或运算:相同位进行比较,有1则对应位的结果为1,否则为0
例如:INDEX_CONCISE_MODE or INDEX_VIEW_CLOSE
0000 0000 0000 0000 0000 0111 0000 0011 or 0000 0000 0000 0000 0000 0000 0100 0000,得到
0000 0000 0000 0000 0000 0111 0100 0011
【3】与运算:相同位进行比较,两位同时为 1,结果才为 1,否则为 0
0000 0000 0000 0000 0000 0000 0000 0011 and 0000 0000 0000 0000 0000 0000 0000 0001,得到
0000 0000 0000 0000 0000 0000 0000 0001
【4】异或运算:相同位进行比较,相同为 0 不同为 1
例如:(INDEX_CONCISE_MODE or INDEX_VIEW_CLOSE) xor INDEX_VIEW_CLOSE
0000 0000 0000 0000 0000 0111 0100 0011 or 0000 0000 0000 0000 0000 0000 0100 0000,得到
0000 0000 0000 0000 0000 0111 0000 0011

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android 学习之多状态布局的一种实现方案 的相关文章

随机推荐

  • Mongodb笔记六:排序与限制输出

    一 排序 db collectionname find sort key1 1 key 1 这里的1代表升序 1代表降序 如 对所有人按年龄升序排序 降序排序 二 索引 索引是特殊的数据结构 索引存储在一个易于遍历读取的数据集合中 索引是对
  • FFmpeg中RTSP客户端拉流测试代码

    之前在https blog csdn net fengbingchun article details 91355410中给出了通过LIVE555实现拉流的测试代码 这里通过FFmpeg来实现 代码量远小于LIVE555 实现模块在liba
  • 蓝桥杯每日一题——手算题·空间

    本题为填空题 只需要算出结果后 在代码中使用输出语句将所填结果输出即可 小蓝准备用 256MB 的内存空间开一个数组 数组的每个元素都是 3232 位 二进制整数 如果不考虑程序占用的空间和维护内存需要的辅助空间 请问 56MB 的空间可以
  • [阶段二] 4. MySQL的基本操作

    mysql的基本操作 数据插入 INSERT 语句可以向数据表写入数据 可以是一条记录 也可以是多条记录 INSERT INTO 数据表名称 字段1 字段2 VALUES 值1 值2 插入一条记录 INSERT INTO 数据表名称 字段1
  • 分析工具 nvprof简介

    nvprof 是一个可用于Linux Windows和OS X的命令行探查器 使用 nvprof myApp 运行我的应用程序 我可以快速看到它所使用的所有内核和内存副本的摘要 摘要将对同一内核的所有调用组合在一起 显示每个内核的总时间和总
  • 十六进制转二进制

    public static String hexToBinary String hex if hex null hex length 2 0 return null String bString String tmp for int i 0
  • Visual Studio(VS) 编程推荐字体和主题设置

    首先是字体 工具 gt 选项 gt 环境 gt 字体和颜色 具体图如下 选择Consolas的原因 Consolas算是最常见的编码字体了 在很多的编译软件都是这个字体 而且在这个字体下的中英文标点和半角圆角符号也能有比较明显的区别 至于字
  • Java 集合 - Map 接口

    文章目录 1 概述 2 常用 API 3 遍历 Map 集合 4 HashMap 和 Hashtable 5 LinkedHashMap 6 TreeMap 7 Properties 8 Set 集合与 Map 集合的关系 9 总结 1 概
  • C++11/14之模板全特化,偏特化

    目录 模板全特化 偏特化 类模板特化 类模板全特化 a 常规全特化 b 特化成员函数而不是模板 类模板偏特化 局部特化 a 模板参数数量 b 模板参数范围 int const int 比int小 函数模板特化 函数模板全特化 函数模板偏特化
  • LayerNorm的理解

    LayerNorm计算公式 y x E x
  • C语言实现多级反馈队列调度算法

    include
  • java架构师进阶之路

    Java架构师 应该算是一些Java程序员们的一个职业目标了吧 很多码农码了五六年的代码也没能成为架构师 那成为Java架构师要掌握哪些技术呢 总体来说呢 有两方面 一个是基础技术 另一个就是组织能力和提出解决方案能力了 如果你是想成为Ja
  • Netty入门-Channel

    目录 Channel详解 Channel的特点 Channel接口方法 ChannelOutboundInvoker接口 AttributeMap接口 ChannelHandler接口 ChannelInboundHandler接口 Cha
  • 请取件

    Part1前言 最常见的鼠标平移算法是平行于水平面 地面 的 无论相机视角如何 平移时 相机的世界Z值始终不变 因为绝大多数场景都是在观察地面上的物体 而人类的行走总是平行于地面的 但是本文要介绍的另一种小众的平移算法则平行于视锥体的截面
  • JPA使用审计功能新增时, 不自动更新@LastModifiedDate和@LastModifiedBy字段

    JPA使用审计功能新增时 不自动更新 LastModifiedDate和 LastModifiedBy字段 疑问 查询源码 解决方案 疑问 JPA使用审计功能 网上有一大堆demo 但是使用时 会发现创建的时候会自动填写 LastModif
  • Mac安装homebrew报错curl: (7) Failed to connect to raw.githubusercontent.com port 443: Operation

    homebrew安装时 一般直接在终端直接输入命令 usr bin ruby e curl fsSL https raw githubusercontent com Homebrew install master install 但是这个方
  • Numpy/Pytorch之数据类型与强制类型转换

    目录 1 数据类型简介 Numpy Pytorch 2 Python的type 函数 3 Numpy Pytorch的dtype属性 4 Numpy中的类型转换 先聊聊我为什么会用到这个函数 不看跳过 astype 函数 输出 4 Pyto
  • 【线性表的原地逆置】

    目录 前言 一 顺序表 数组 一 双指针 二 单链表 一 模拟顺序表的双指针 交换的节点的值域 二 头插法 改变节点的指针域 三 递归实现 将整体链表反向 整体代码 总结 前言 打怪升级第一天 大家好 今天我们来了解一下数组和单链表的原地逆
  • (mybatis驼峰命名导致映射错误)

    今天在复习mybatis时遇到这样的一个问题 我数据库表的字段和我定义的实体类名不一致 中间有下划线 如下图 实体类 数据库字段 结果会导致部分查询数据是null 于是我首先想到了自己定义一个resultmap映射 给数据库字段取一个别名
  • Android 学习之多状态布局的一种实现方案

    开发应用的过程中 首页的控件越来越多 布局文件的代码已经到了爆表的程度 而且不同状态下首页各个控件的 Visibility 不同 每次新增状态都是一件头疼的事情 时常遗漏控件导致出错 和 YYY 大佬交流讨论后他给出了一种巧妙的方案 特此学