Android 原生 TabLayout 使用全解析

news2024/12/25 9:07:26

前言

为什么会有这篇文章呢,是因为之前关于TabLayout的使用陆陆续续也写了好几篇了,感觉比较分散,且不成体系,写这篇文章的目的就是希望能把各种效果的实现一次性讲齐,所以也有了标题的「看这篇就够了」。

TabLayout作为导航组件来说,使用场景非常的多,也意味着要满足各种各样的需求。

在效果实现上,有同学会选择自定义View来做,定制性高,但易用性、稳定性、维护性不敢保证,使用官方组件能避免这些不确定性,一是开源,有很多大佬共建,会不停的迭代;二是经过大型app验证,比如google play;有了这两点,基本可以放心大胆的使用官方组件了。

那可能有的同学又会说,道理我都懂,可是不满足需求啊,只能自定义了。是的,早期的api确实不够丰富,在某些需求的实现上显得捉襟见肘,但是google也在不断的迭代,目前为止,常见的样式都能满足。

效果图

简介

TabLayout:一个横向可滑动的菜单导航ui组件。

Tab:TabLayout中的item,可以通过newTab()创建。

TabView:Tab的实例,是一个包含ImageView和TextView的线性布局。

TabItem:一种特殊的“视图”,在TabLayout中可以显式声明Tab。

官方文档

https://developer.android.google.cn/reference/com/google/android/material/tabs/TabLayout?hl=en

功能拆解

Material Design 组件最新正式版依赖:

implementation 'com.google.android.material:material:1.5.0'

1.基础实现

1.1 xml动态写法

 <com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_layout1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/white"
        app:tabIndicatorColor="@color/colorPrimary"
        app:tabMaxWidth="200dp"
        app:tabMinWidth="100dp"
        app:tabMode="fixed"
        app:tabSelectedTextColor="@color/colorPrimary"
        app:tabTextColor="@color/gray" />

只写一个Layout,item可以配合ViewPager来生成。

1.2 xml静态写法

 <com.google.android.material.tabs.TabLayout
         android:layout_height="wrap_content"
         android:layout_width="match_parent">

     <com.google.android.material.tabs.TabItem
             android:text="@string/tab_text"/>

     <com.google.android.material.tabs.TabItem
             android:icon="@drawable/ic_android"/>

 </com.google.android.material.tabs.TabLayout>

属于固定写法,比如我们非常确定item有几个,可以通过TabItem显式声明。

1.3 kotlin/java代码写法

val tab = mBinding.tabLayout7.newTab()
tab.text = it.key
//...
mBinding.tabLayout7.addTab(tab)

这种情况适合Tab的数据是动态的,比如接口数据回来之后,再创建Tab并添加到TabLayout中。

2.添加图标

mBinding.tabLayout2.getTabAt (index )?.setIcon (R.mipmap.ic_launcher )

获取Tab然后设置icon。

Tab内部其实是一个TextView和ImageView,添加图标就是给ImageView设置icon。

3.字体大小、加粗

通过app:tabTextAppearance给TabLayout设置文本样式

<com.google.android.material.tabs.TabLayout
    ...
    app:tabTextAppearance="@style/MyTabLayout"
    />

style:

<style name="MyTabLayout">
    <item name="android:textSize">20sp</item>
    <item name="android:textStyle">bold</item>
    <item name="android:textAllCaps">false</item>
</style>

比如这里设置了字体大小和加粗。

默认字体大小14sp:

<dimen name="design_tab_text_size">14sp</dimen>

4.去掉Tab长按提示文字

长按Tab时会有一个提示文字,类似Toast一样。

/**
 * 隐藏长按显示文本
 */
private fun hideToolTipText(tab: TabLayout.Tab) {
    // 取消长按事件
    tab.view.isLongClickable = false
    // api 26 以上 设置空text
    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
        tab.view.tooltipText = ""
    }
}

可以取消长按事件,在api26以上也可以设置提示文本为空。

5.去掉下划线indicator

app:tabIndicatorHeight="0dp"

设置高度为0即可。

注意,单纯设置tabIndicatorColor为透明,其实不准确,默认还是有2dp的,根本瞒不过射鸡师的眼睛。

6.下划线的样式

通过app:tabIndicator可以设置自定义的样式,比如通过shape设置圆角和宽度。

<com.google.android.material.tabs.TabLayout
    ...
    app:tabIndicator="@drawable/shape_tab_indicator"
    app:tabIndicatorColor="@color/colorPrimary"
    />

注意:Indicator的颜色在shape中设置是无效的,需要通过app:tabIndicatorColor设置才可以。

shape:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:width="15dp"
        android:height="5dp"
        android:gravity="center">
        <shape>
            <corners android:radius="5dp" />
            <!--color无效,源码用tabIndicatorColor-->
            <solid android:color="@color/colorPrimary" />
        </shape>
    </item>
</layer-list>

7.下划线的宽度

默认情况下,tabIndicator的宽度是填充整个Tab的,比如上图中的第一个,我们可以简单的设置不填充,与文本对齐,即第二个效果。

app:tabIndicatorFullWidth="false"

也可以像上一节那样,通过shape自定义tabIndicator的宽度。

8.Tab分割线

  /** A {@link LinearLayout} containing {@link Tab} instances for use with {@link TabLayout}. */
  public final class TabView extends LinearLayout {

  }

通过源码可以看到内部实现TabView继承至LinearLayout,我们知道LinearLayout是可以给子view设置分割线的,那我们就可以通过遍历来添加分割线。

//设置 分割线
for (index in 0..mBinding.tabLayout4.tabCount) {
    val linearLayout = mBinding.tabLayout4.getChildAt(index) as? LinearLayout
    linearLayout?.let {
        it.showDividers = LinearLayout.SHOW_DIVIDER_MIDDLE
        it.dividerDrawable = ContextCompat.getDrawable(this, R.drawable.shape_tab_divider)
        it.dividerPadding = 30
    }
}

shape_tab_divider:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@color/colorPrimary" />
    <size android:width="1dp" android:height="10dp" />
</shape>

9.TabLayout样式

上图中的效果其实是TabLayout样式+tabIndicator样式形成的一个「整体」的效果。

TabLayout是两边半圆的一个长条,这个我们通过编写shape设置给其背景即可实现。

shape_tab_bg:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <corners android:radius="999dp" />
    <solid android:color="@color/colorPrimary" />
</shape>

这个效果的关键在于tabIndicator的高度与TabLayout的高度相同,所以二者高度设置一致即可。

shape_full_tab_indicator:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:gravity="center" android:top="0.5dp" android:bottom="0.5dp">
        <shape>
            <!-- 上下边距合计1dp 高度减少1dp -->
            <size android:height="41dp" />
            <corners android:radius="999dp" />
            <solid android:color="@color/white" />
        </shape>
    </item>
</layer-list>

TabLayout:

<com.google.android.material.tabs.TabLayout
    android:id="@+id/tab_layout6"
    android:layout_width="wrap_content"
    android:layout_height="42dp"
    android:layout_gravity="center"
    android:layout_marginTop="10dp"
    android:background="@drawable/shape_tab_bg"
    app:tabIndicator="@drawable/shape_full_tab_indicator"
    app:tabIndicatorColor="@color/white"
    app:tabIndicatorFullWidth="true"
    app:tabIndicatorHeight="42dp"
    app:tabMinWidth="96dp"
    app:tabMode="fixed"
    app:tabSelectedTextColor="@color/colorPrimary"
    app:tabTextColor="@color/black" />

10.Tab添加小红点

添加小红点的功能还是比较常见的,好在TabLayout也提供了这种能力,其实添加起来也非常简单,难在未知。

可以设置带数字的红点,也可以设置没有数字单纯的一个点。

通过getOrCreateBadge可以对红点进行简单的配置:

// 数字
mBinding.tabLayout5.getTabAt(defaultIndex)?.let { tab ->
    tab.orCreateBadge.apply {
        backgroundColor = Color.RED
        maxCharacterCount = 3
        number = 99999
        badgeTextColor = Color.WHITE
    }
}

// 红点
mBinding.tabLayout5.getTabAt(1)?.let { tab ->
    tab.orCreateBadge.backgroundColor = ContextCompat.getColor(this, R.color.orange)
}

getOrCreateBadge实际上是获取或创建BadgeDrawable。

通过源码发现,BadgeDrawable除了TabLayout引用之外,还有NavigationBarItemView、NavigationBarMenuView、NavigationBarView,意味着它们也同样具备着小红点这种能力。其实别的view也是可以具备的。

关于小红点这里就不展开了,非常推荐查看我之前写的这篇:【涨姿势】你没用过的BadgeDrawable

https://blog.csdn.net/yechaoa/article/details/122272822?spm=1001.2014.3001.5501

11.获取隐藏的Tab

上一节中我们实现了小红点效果,那如果一屏显示不够的情况下,如何提示未展示的信息呢,比如上面我们如何把未显示的tab且有数字的Tab提示出来呢?常见的解决方案都是在尾部加一个红点提示。

那么问题来了,如何判断某一个Tab是否可见呢,翻看了源码,可惜并没有提供相应的api,那只能我们自己实现了。

我们前面添加小红点是根据Tab添加的,Tab内部实现也是一个view,那view就可以判断其是否可见。

private fun isShowDot(): Boolean {
    var showIndex = 0
    var tipCount = 0
    companyMap.keys.forEachIndexed { index, _ ->
        mBinding.tabLayout7.getTabAt(index)?.let { tab ->
            val tabView = tab.view as LinearLayout
            val rect = Rect()
            val visible = tabView.getLocalVisibleRect(rect)
            // 可见范围小于80%也在计算范围之内,剩下20%宽度足够红点透出(可自定义)
            if (visible && rect.right > tab.view.width * 0.8) {
                showIndex = index
            } else {
                //if (index > showIndex) // 任意一个有count的tab隐藏就会显示,比如第一个在滑动过程中会隐藏,也在计算范围之内
                if (index > lastShowIndex) { // 只检测右侧隐藏且有count的tab 才在计算范围之内
                    tab.badge?.let { tipCount += it.number }
                }
            }

        }
    }
    lastShowIndex = showIndex
    return tipCount > 0
}

上面的方法中就是判断是否需要显示右侧提示的小红点。

计算规则:Tab不可见,且Tab上的红点数字大于0的即在计算范围之内。

这里有一个优化的点,比如上图中的“腾讯”Tab,它是可见的,但是红点不可见,那么问题就来了,如果我们没有提示到,是很容易产生客诉的,所以这里在计算的时候也加了一个条件,就是可见范围小于80%也在计算范围之内,剩下20%的宽度是足够Tab上的红点透出的(也可自定义)。

同时在TabLayout滑动的过程中也应该加上判断显示的逻辑:

// mBinding.tabLayout7.setOnScrollChangeListener() // min api 23 (6.0)
// 适配 5.0  滑动过程中判断右侧小红点是否需要显示
mBinding.tabLayout7.viewTreeObserver.addOnScrollChangedListener {
    mBinding.vArrowDot.visibility = if (isShowDot()) View.VISIBLE else View.INVISIBLE
}

还有初始化时的判断逻辑:

override fun onResume() {
    super.onResume()
    // 初始化判断右侧小红点是否需要显示
    mBinding.tabLayout7.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            mBinding.vArrowDot.visibility = if (isShowDot()) View.VISIBLE else View.INVISIBLE
            mBinding.tabLayout7.viewTreeObserver.removeOnGlobalLayoutListener(this)
        }
    })
}

12.Tab宽度自适应

细心的同学会发现,这个TabLayout的item左右间距都是一样的,不管标题是两个字还是四个字的,左右间距都是相等的,而实际上的效果是两个字的Tab要比四个字的Tab左右间距要大一些的,那这个效果是怎么实现的呢?

实际上是我们设置了tabMinWidth:

app :tabMinWidth="50dp"

源码中默认的是:

private int getTabMinWidth() {
    if (requestedTabMinWidth != INVALID_WIDTH) {
      // If we have been given a min width, use it
      return requestedTabMinWidth;
    }
    // Else, we'll use the default value
    return (mode == MODE_SCROLLABLE || mode == MODE_AUTO) ? scrollableTabMinWidth : 0;
}

requestedTabMinWidth是根据xml设置获取的。

假如xml没设置tabMinWidth的情况下,且tabMode是scrollable的情况下,会返回默认配置,否则为0,即tabMode为fixed的情况。

系统默认配置scrollableTabMinWidth:

<dimen name="design_tab_scrollable_min_width"> 72dp</dimen>

在两个字和四个字的标题都存在的情况下,两个字用这个默认宽度就会有多余的间距,所以会出现间距不均等的情况,通过设置覆盖默认即可解决。

13.自定义Item View

前面讲到Tab内部实现是一个View,那我们就可以通过官方提供api(setCustomView)来自定义这个view。

setCustomView的两种方式:

1. public Tab setCustomView(@Nullable View view)

2. public Tab setCustomView(@LayoutRes int resId)

我们先编写一个自定义的布局文件,布局文件比较简单,一个LottieAnimationView和TextView。

再通过Tab添加进去即可。

val animMap = mapOf("party" to R.raw.anim_confetti, "pizza" to R.raw.anim_pizza, "apple" to R.raw.anim_apple)

animMap.keys.forEach { s ->
    val tab = mBinding.tabLayout8.newTab()
    val view = LayoutInflater.from(this).inflate(R.layout.item_tab, null)
    val imageView = view.findViewById<LottieAnimationView>(R.id.lav_tab_img)
    val textView = view.findViewById<TextView>(R.id.tv_tab_text)
    imageView.setAnimation(animMap[s]!!)
    imageView.setColorFilter(Color.BLUE)
    textView.text = s
    tab.customView = view
    mBinding.tabLayout8.addTab(tab)
}

14.使用Lottie

Lottie是一个可以在多平台展示动画的库,相信很多同学都已经用过了,就不详细展开了,感兴趣的可以查看Lottie官方文档。

Lottie依赖:

implementation "com.airbnb.android:lottie:5.0.1"

上一节中我们实现了自定义TabLayout的Item View,在这个自定义的布局中,我们用LottieAnimationView来承载动画的展示。

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/item_tab"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:orientation="vertical">

    <com.airbnb.lottie.LottieAnimationView
        android:id="@+id/lav_tab_img"
        android:layout_width="30dp"
        android:layout_height="30dp"
        app:lottie_colorFilter="@color/black"
        app:lottie_rawRes="@raw/anim_confetti" />

    <TextView
        android:id="@+id/tv_tab_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/app_name"
        android:textColor="@color/black"
        android:textSize="14sp" />

</androidx.appcompat.widget.LinearLayoutCompat>

添加的方式也在上一节中讲过了,我们只需要控制好选中、未选中的状态即可。

mBinding.tabLayout8.addOnTabSelectedListener(object : OnTabSelectedListener {
    override fun onTabSelected(tab: TabLayout.Tab?) {
        tab?.setSelected()
        tab?.let { mBinding.viewPager.currentItem = it.position }
    }

    override fun onTabUnselected(tab: TabLayout.Tab?) {
        tab?.setUnselected()
    }

    override fun onTabReselected(tab: TabLayout.Tab?) {

    }
})

这里通过两个扩展方法分别处理不同的状态。

1. 选中状态,播放动画并设置icon颜色。

/**
     * 选中状态
     */
    fun TabLayout.Tab.setSelected() {
        this.customView?.let {
            val textView = it.findViewById<TextView>(R.id.tv_tab_text)
            val selectedColor = ContextCompat.getColor(this@TabLayoutActivity, R.color.colorPrimary)
            textView.setTextColor(selectedColor)

            val imageView = it.findViewById<LottieAnimationView>(R.id.lav_tab_img)
            if (!imageView.isAnimating) {
                imageView.playAnimation()
            }
            setLottieColor(imageView, true)
        }
    }

2. 未选中状态,停止动画并还原初始状态,然后设置icon颜色。

/**
 * 未选中状态
 */
fun TabLayout.Tab.setUnselected() {
    this.customView?.let {
        val textView = it.findViewById<TextView>(R.id.tv_tab_text)
        val unselectedColor = ContextCompat.getColor(this@TabLayoutActivity, R.color.black)
        textView.setTextColor(unselectedColor)

        val imageView = it.findViewById<LottieAnimationView>(R.id.lav_tab_img)
        if (imageView.isAnimating) {
            imageView.cancelAnimation()
            imageView.progress = 0f // 还原初始状态
        }
        setLottieColor(imageView, false)
    }
}

关于修改lottie icon的颜色,目前网上的答案参差不齐,还是源码来的直接。

源码:

if (ta.hasValue(R.styleable.LottieAnimationView_lottie_colorFilter)) {
  int colorRes = ta.getResourceId(R.styleable.LottieAnimationView_lottie_colorFilter, -1);
  ColorStateList csl = AppCompatResources.getColorStateList(getContext(), colorRes);
  SimpleColorFilter filter = new SimpleColorFilter(csl.getDefaultColor());
  KeyPath keyPath = new KeyPath("**");
  LottieValueCallback<ColorFilter> callback = new LottieValueCallback<>(filter);
  addValueCallback(keyPath, LottieProperty.COLOR_FILTER, callback);
}

所以直接借鉴即可:

/**
 * set lottie icon color
 */
private fun setLottieColor(imageView: LottieAnimationView?, isSelected: Boolean) {
    imageView?.let {
        val color = if (isSelected) R.color.colorPrimary else R.color.black
        val csl = AppCompatResources.getColorStateList(this@TabLayoutActivity, color)
        val filter = SimpleColorFilter(csl.defaultColor)
        val keyPath = KeyPath("**")
        val callback = LottieValueCallback<ColorFilter>(filter)
        it.addValueCallback(keyPath, LottieProperty.COLOR_FILTER, callback)
    }
}

动画文件的下载网站推荐:lordicon

https://lordicon.com/

15.关联ViewPager

15.1 编写FragmentPagerAdapter

private inner class SimpleFragmentPagerAdapter constructor(fm: FragmentManager) :
    FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {

    private val tabTitles = arrayOf("Android", "Kotlin", "Flutter")
    private val fragment = arrayOf(Fragment1(), Fragment2(), Fragment3())

    override fun getItem(position: Int): Fragment {
        return fragment[position]
    }

    override fun getCount(): Int {
        return fragment.size
    }

    override fun getPageTitle(position: Int): CharSequence {
        return tabTitles[position]
    }
}

15.2 给ViewPager设置Adapter

mBinding.viewPager.adapter = SimpleFragmentPagerAdapter(supportFragmentManager)

15.3 给TabLayout关联ViewPager

mBinding.tabLayout1.setupWithViewPager (mBinding.viewPager )

以上即可把TabLayout和ViewPager关联起来,TabLayout的Tab也会由FragmentPagerAdapter中的标题自动生成。

15.4 setupWithViewPager源码分析

究竟是怎么关联起来的呢?

下面是setupWithViewPager中的部分源码:

if (viewPager != null) {
    this.viewPager = viewPager;
    if (this.pageChangeListener == null) {
        // 步骤1
        this.pageChangeListener = new TabLayout.TabLayoutOnPageChangeListener(this);
    }

    this.pageChangeListener.reset();
    viewPager.addOnPageChangeListener(this.pageChangeListener);
    // 步骤2
    this.currentVpSelectedListener = new TabLayout.ViewPagerOnTabSelectedListener(viewPager);
    // 步骤3
    this.addOnTabSelectedListener(this.currentVpSelectedListener);
    PagerAdapter adapter = viewPager.getAdapter();
    if (adapter != null) {
        this.setPagerAdapter(adapter, autoRefresh);
    }

    if (this.adapterChangeListener == null) {
        this.adapterChangeListener = new TabLayout.AdapterChangeListener();
    }

    this.adapterChangeListener.setAutoRefresh(autoRefresh);
    // 步骤4
    viewPager.addOnAdapterChangeListener(this.adapterChangeListener);
    this.setScrollPosition(viewPager.getCurrentItem(), 0.0F, true);
}

1. 先是创建了TabLayout.TabLayoutOnPageChangeListener,并设置给了viewPager.addOnPageChangeListener。

2. 然后又创建了TabLayout.ViewPagerOnTabSelectedListener(viewPager),并传入当前viewPager,然后设置给了addOnTabSelectedListener。

3. 所以,经过这种你来我往的操作之后,设置TabLayout的选中下标和设置ViewPager的选中下标,其实效果是一毛一样的,因为联动起来了…

另外,FragmentPagerAdapter已经废弃了,官方推荐使用viewpager2 和 FragmentStateAdapter 代替。

Deprecated Switch to androidx.viewpager2.widget.ViewPager2 and use androidx.viewpager2.adapter.FragmentStateAdapter instead.

16.常用API整理

16.1 TabLayout

16.2 TabLayout.Tab

16.3 BadgeDrawable

Github:

https://github.com/yechaoa/MaterialDesign

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

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

相关文章

【自然语言处理】Topic Coherence You Need to Know(主题连贯度详解)

Topic Coherence You Need to Know皮皮&#xff0c;京哥皮皮&#xff0c;京哥皮皮&#xff0c;京哥CommunicationUniversityofChinaCommunication\ University\ of\ ChinaCommunication University of China 在大多数关于主题建模的文章中&#xff0c;常用主题连贯度&#xff…

JSP实现数据传递与保存(一)

学习目标&#xff1a; 理解JSP内置对象的概念 掌握request和response的使用 掌握转发和重定向的区别 掌握out对象的使用 学习内容&#xff1a; 1.HTML页面转成JSP页面 HTML页面转成JSP页面一般有两种方式 方式1&#xff1a;直接修改HTML页面 1&#xff09;直接在HTM…

QT+OpenGL模板测试和混合

QTOpenGL模板测试和混合 本篇完整工程见gitee:QtOpenGL 对应点的tag&#xff0c;由turbolove提供技术支持&#xff0c;您可以关注博主或者私信博主 模板测试 当片段着色器处理完一个片段之后&#xff0c;模板测试会开始执行。和深度测试一样&#xff0c;它可能会丢弃片段&am…

Win11的两个实用技巧系列之Win11怎么找回Win7照片查看器

Win11怎么找回Win7照片查看器? Win11旧版照片查看器的切换方法Win11怎么找回Win7照片查看器&#xff1f;用习惯了win7的照片查看器&#xff0c;想要在win11中使用&#xff0c;该怎么启用旧版照片查看器呢&#xff1f;下面我们就来看看Win11旧版照片查看器的切换方法Win11系统启…

c++之二叉树【进阶版】

前言 在c语言阶段的数据结构系列中已经学习过二叉树&#xff0c;但是这篇文章是二叉树的进阶版&#xff0c;因为首先就会讲到一种树形结构“二叉搜索树”&#xff0c;学习二叉搜索树的目标是为了更好的理解map和set的特性。二叉搜索树的特性就是左子树键值小于根&#xff0c;右…

【JVM】运行时数据区与对象的创建流程

4、运行时数据区 4.1、运行时数据区介绍 运行时数据区也就是JVM在运⾏时产生的数据存放的区域&#xff0c;这块区域就是JVM的内存区域&#xff0c;也称为JVM的内存模型——JMM 堆空间&#xff08;线程共享&#xff09;&#xff1a;存放new出来的对象 元空间&#xff08;线程共…

3,预初始化(一)(大象无形9.2)

正如书中所说&#xff0c;预初始化流程由FEngineLoop::PreInit()所实现 主要处理流程 1&#xff0c;设置路径&#xff1a;当前程序路径&#xff0c;当前工作目录路径&#xff0c;游戏的工程路径 2,设置标准输出&#xff1a;设置GLog系统输出的设备&#xff0c;是输出到命令行…

web自动化测试-执行 JavaScript 脚本

JavaScript 是一种脚本语言&#xff0c;有的场景需要使用 js 脚本注入辅助我们完成 Selenium 无法做到的事情。 当 webdriver 遇到无法完成的操作时&#xff0c;可以使用 JavaScript 来完成&#xff0c;webdriver 提供了 execute_script() 方法来调用 js 代码。 执行 js 有两种…

Leetcode.2385 感染二叉树需要的总时间

题目链接 Leetcode.2385 感染二叉树需要的总时间 Rating &#xff1a; 1711 题目描述 给你一棵二叉树的根节点 root&#xff0c;二叉树中节点的值 互不相同 。另给你一个整数 start。在第 0分钟&#xff0c;感染 将会从值为 start的节点开始爆发。 每分钟&#xff0c;如果节点…

一文3000字从0到1实现基于requests框架接口自动化测试项目实战(建议收藏)

requests库是一个常用的用于http请求的模块&#xff0c;它使用python语言编写&#xff0c;在当下python系列的接口自动化中应用广泛&#xff0c;本文将带领大家深入学习这个库 Python环境的安装就不在这里赘述了&#xff0c;我们直接开干。 01、requests的安装 windows下执行…

大数据常见应用场景及架构改进

大数据常见应用场景及架构改进大数据典型的离线处理场景1.大数据数据仓库及它的架构改进2.海量数据规模下的搜索与检索3.新兴的图计算领域4.海量数据挖掘潜在价值大数据实时处理场景大数据典型的离线处理场景 1.大数据数据仓库及它的架构改进 对于离线场景&#xff0c;最典型…

磷脂-聚乙二醇-丙烯酸酯;DSPE-PEG-AC试剂说明;DSPE-PEG-Acrylate科研用

中文名称&#xff1a;磷脂-聚乙二醇-丙烯酸酯 丙烯酸酯-聚乙二醇-磷脂 简称&#xff1a;DSPE-PEG-AC&#xff1b;DSPE-PEG-Acrylate 溶剂&#xff1a;溶于部分常规有机溶剂 PEG分子量:1000&#xff1b;2000&#xff1b;3400&#xff1b;5000等等 注意事项&#xff1a;避免…

JavaSE02-JVM、JRE、JDK

文章目录一、JVM、JRE、JDK区别二、JDK的安装和配置1.JDK安装2.测试验证3.环境变量配置3.1 配置JAVA_HOME系统变量3.2 配置Path环境变量再最前面加上&#xff1a; %JAVA_HOME%\bin一、JVM、JRE、JDK区别 JVM&#xff08;Java Virtual Machine&#xff09;&#xff0c;Java虚拟…

jar包和AAR包

以前在使用 Eclipse 开发 Android 时&#xff0c;如果想代码打包&#xff0c;只有 jar 包一个方法&#xff0c;但是 jar包 只能把 Java 文件代码打包进去&#xff0c;如果要使用一个有布局和资源的库的话&#xff0c;除了将 jar 放入 libs 外,还要引入相关的资源和配置文件&…

详解一个TCP连接的建立与销毁

目录 &#x1f332; 图解TCP三次握手建立连接 TCP数据报结构 TCP连接的建立&#xff08;三次握手&#xff09; 最后的说明 &#x1f332; 详细分析TCP数据的传输过程 &#x1f332; 图解TCP四次握手断开连接 &#x1f332; 图解TCP三次握手建立连接 TCP&#xff08;Tran…

【模拟集成电路】宽摆幅压控振荡器(VCO)设计

鉴频鉴相器设计&#xff08;Phase Frequency Detector&#xff0c;PFD&#xff09;前言一、VCO工作原理二、VCO电路设计VCO原理图三、压控振荡器&#xff08;VCO&#xff09;测试VCO测试电路图瞬态测试&#xff08;1&#xff09;瞬态输出&#xff08;2&#xff09;局部放大图&a…

【Java】Spring Boot项目的创建和使用

文章目录SpringBoot的创建和使用1. 什么是Spring Boot&#xff1f;为什么要学Spring Boot&#xff1f;2. Spring Boot项目的优点3. Spring Boot 项目的创建3.1 使用idea创建3.2 接下来创建Spring Boot项目4. 项目目录介绍和运行4.1 运行项目4.2 输出内容5. 总结SpringBoot的创建…

nyist最终淘汰赛第一场

我出的题喜欢吗 我要水题解所以每一篇题解都分一个博客 A 题解链接: Atcoder abc257 E_霾まる的博客-CSDN博客 构造贪心题 在本次淘汰赛中较难 B 题解链接: atcoder abc217 D_霾まる的博客-CSDN博客 STL二分题, 当然你可以数组二分, 相对麻烦一点 在本次淘汰赛中较简单…

学习 Python 之 Pygame 开发魂斗罗(二)

学习 Python 之 Pygame 开发魂斗罗&#xff08;二&#xff09;魂斗罗的需求开始编写魂斗罗1. 搭建主类框架2. 设置游戏运行遍历和创建窗口3. 获取窗口中的事件4. 创建角色5. 完成角色更新函数魂斗罗的需求 魂斗罗游戏中包含很多个物体&#xff0c;现在要对这些物体进行总结 类…

【RabbitMQ笔记02】消息队列RabbitMQ七种模式之最简单的模式

这篇文章&#xff0c;主要介绍RabbitMQ消息队列中七种模式里面最简单的使用模式。 目录 一、消息队列的使用 1.1、消息队列七种模式 1.2、最简单的模式使用 &#xff08;1&#xff09;引入依赖 &#xff08;2&#xff09;编写生产者 &#xff08;3&#xff09;编写消费者…