一篇文章搞定《CoordinatorLayout完成电商首页》

news2025/2/12 13:56:23

一篇文章搞定《CoordinatorLayout完成电商首页》

  • 前言
  • NestedScroll
    • NestedScrollingParent
    • NestedScrollingChild
    • NestedScrollingChildHelper 和 NestedScrollingParentHelper
  • CoordinatorLayout
    • CoordinatorLayout知识点讲解
    • 实现后续功能
  • 通过自定义Behavior实现Fling效果
    • 认识Behavior
    • 自定义Behavior
  • 总结

前言

Android中为我们提供了一些解决嵌套滑动的方式方法,本篇文章利用这些方式方法来实现和处理一下嵌套滑动,作为实战的实例。
先铺垫一下NestedScrollingParent和NestedScrollingChild,后面利用CoordinatorLayout完成电商首页。

NestedScroll

上篇文章也有说到,其实在一些已经把嵌套滑动解决的控件中。
比如RecyclerView

public class RecyclerView extends ViewGroup implements ScrollingView,
        NestedScrollingChild2, NestedScrollingChild3 {

NestedScrollView

public class NestedScrollView extends FrameLayout implements NestedScrollingParent3,
        NestedScrollingChild3, ScrollingView {

CoordinatorLayout

public class CoordinatorLayout extends ViewGroup implements NestedScrollingParent2,
        NestedScrollingParent3 {

可以看到在一些滑动的组件中都继承了NestedScrollingChild或者NestedScrollingParent来对嵌套滑动进行了处理。所以大家在使用这些组件进行嵌套时,会发现没什么嵌套滑动的问题。
那我们来看看NestedScrollingChild、NestedScrollingParent都有什么因为我们下面要使用CoordinatorLayout

NestedScrollingParent

什么时候去继承:可滑动的ViewGroup作为父容器的时候,那么就需要实现这个接口。
实现这个接口主要是想去重写他的几个方法,来帮助我们解决滑动冲突,所以这块不用太纠结。把这几个方法了解了就行:

public interface NestedScrollingParent {
    /**
     * React to a descendant view initiating a nestable scroll operation, claiming the
     * nested scroll operation if appropriate.

     * 当子View调用startNestedScroll方法的时候,父容器会在这个方法中获取回调
     */
    boolean onStartNestedScroll(@NonNull View child, @NonNull View target, @ScrollAxis int axes);

    /**
     * React to the successful claiming of a nested scroll operation.
     * 在onStartNestedScroll调用之后,就紧接着调用这个方法
     */
    void onNestedScrollAccepted(@NonNull View child, @NonNull View target, @ScrollAxis int axes);

    /**
     * React to a nested scroll operation ending.
     * 当子View调用 stopNestedScroll方法的时候回调
     */
    void onStopNestedScroll(@NonNull View target);

    /**
     * React to a nested scroll in progress.
     *
     */
    void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed,
    int dxUnconsumed, int dyUnconsumed);

    /**
     * React to a nested scroll in progress before the target view consumes a portion of the scroll.
     * 在子View调用dispatchNestedPreScroll之后,这个方法拿到了回调
     *
     */
    void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed);

    /**
     * Request a fling from a nested scroll.
     *
     */
    boolean onNestedFling(@NonNull View target, float velocityX, float velocityY, boolean consumed);

    /**
     * React to a nested fling before the target view consumes it.
     *
     */
    boolean onNestedPreFling(@NonNull View target, float velocityX, float velocityY);

    /**
     * Return the current axes of nested scrolling for this NestedScrollingParent.
     * 返回当前滑动的方向
     */
    @ScrollAxis
    int getNestedScrollAxes();
}

NestedScrollingChild

什么时候去继承:作为可滑动的子View,那么就需要实现NestedScrollingChild接口。

public interface NestedScrollingChild {
    /**
     * Enable or disable nested scrolling for this view.
     *
     * 启动或者禁用嵌套滑动,如果返回ture,那么说明当前布局存在嵌套滑动的场景,反之没有
     * 使用场景:NestedScrollingParent嵌套NestedScrollingChild
     * 在此接口中的方法,都是交给NestedScrollingChildHelper代理类实现
     */
    void setNestedScrollingEnabled(boolean enabled);

    /**
     * Returns true if nested scrolling is enabled for this view.
     * 其实就是返回setNestedScrollingEnabled中设置的值
     */
    boolean isNestedScrollingEnabled();

    /**
     * Begin a nestable scroll operation along the given axes.
     * 表示view开始滚动了,一般是在ACTION_DOWN中调用,如果返回true则表示父布局支持嵌套滚动。
     * 一般也是直接代理给NestedScrollingChildHelper的同名方法即可。这个时候正常情况会触发Parent的onStartNestedScroll()方法
     */
    boolean startNestedScroll(@ScrollAxis int axes);

    /**
     * Stop a nested scroll in progress.
     * 停止嵌套滚动,一般在UP或者CANCEL事件中执行,告诉父容器已经停止了嵌套滑动
     */
    void stopNestedScroll();

    /**
     * Returns true if this view has a nested scrolling parent.
     * 判断当前View是否存在嵌套滑动的Parent
     */
    boolean hasNestedScrollingParent();

    /**
     * 当前View消费滑动事件之后,滚动一段距离之后,把剩余的距离回调给父容器,父容器知道当前剩余距离
     * dxConsumed:x轴滚动的距离
     * dyConsumed:y轴滚动的距离
     * dxUnconsumed:x轴未消费的距离
     * dyUnconsumed:y轴未消费的距离
     * 这个方法是嵌套滑动的时候调用才有用,返回值 true分发成功;false 分发失败
     */
    boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
    int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow);

    /**
     * Dispatch one step of a nested scroll in progress before this view consumes any portion of it.
     * 在子View消费滑动距离之前,将滑动距离传递给父容器,相当于把消费权交给parent
     * dx:当前水平方向滑动的距离
     * dy:当前垂直方向滑动的距离
     * consumed:输出参数,会将Parent消费掉的距离封装进该参数consumed[0]代表水平方向,consumed[1]代表垂直方向
     * @return true:代表Parent消费了滚动距离
     */
    boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed,
    @Nullable int[] offsetInWindow);

    /**
     * Dispatch one step of a nested scroll in progress.
     * 处理惯性事件,与dispatchNestedScroll类似,也是在消费事件之后,将消费和未消费的距离都传递给父容器
     */
    boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed);

    /**
     * Dispatch a fling to a nested scrolling parent before it is processed by this view.
     * 与dispatchNestedPreScroll类似,在消费之前首先会传递给父容器,把优先处理权交给父容器
     */
    boolean dispatchNestedPreFling(float velocityX, float velocityY);
}

通过这两个接口,我们大概就能够明白,其实嵌套滑动机制完全是子View在做主导,通过子View能够决定Parent是否能够优先消费事件(dispatchNestedPreScroll)。

NestedScrollingChildHelper 和 NestedScrollingParentHelper

NestedScrollingChildHelper 和 NestedScrollingParentHelper 类的作用:主要是帮助内部View和外部View实现交互逻辑。
举个例子就知道了:
在实现了NestedScrollingChild就需要声明NestedScrollingChildHelper进行

public class NestedChildView extends View implements NestedScrollingChild {
    ......
    private NestedScrollingChildHelper mScrollingChildHelper;
    
    private void init() {
        mScrollingChildHelper = new NestedScrollingChildHelper(this);
    }
    .....
    
    /**
     * 设置是否允许嵌套滑动
     */
     @Overridepublic void setNestedScrollingEnabled(boolean enabled) {
         //通过mScrollingChildHelper进行通信
         mScrollingChildHelper.setNestedScrollingEnabled(enabled);
     }
     
     /**
     * 是否允许嵌套滑动
     */
     @Overridepublic boolean isNestedScrollingEnabled() {
         return mScrollingChildHelper.isNestedScrollingEnabled();
     }
     .....

知道干啥的了吧,不用整那么细,把上面的方法都解读一下。了解有什么作用,用的时候再去细看就行。都是事件分发那一套。

CoordinatorLayout

CoordinatorLayout翻译为协调者布局,是用来协调其子View们之间动作的一个容器,他是一个超级强大的FrameLayout,结合AppBarLayout、 CollapsingToolbarLayout等可产生各种炫酷的效果。
下面用一个例子来讲解
以下面的图片为例:
在这里插入图片描述
我们按照这样的架构实现下面的交互功能:
1、上面为一个headerView模块我们以一个图片模块代替
2、中间为一个TabLayout,有多个Table结合下面的ViewPage
3、下面为ViewPage中的Fragment,其中用RecyclerView展示我们的商品
4、TabLayout可以吸顶
5、当headerView没有完全隐藏的情况下在RecyclerView上滑动时,优先滑动headerView直至隐藏后RecyclerView继续滑动。
6、在headView上向下fling,会将剩余的惯性传递到RecyclerView。
下面是小编实现的一个效果:

在这里插入图片描述
接下来看看都是怎么实现的一步一步进行。
首先实现前三步:(这是比较常规的代码了)
1、上面为一个headerView模块我们以一个图片模块代替
2、中间为一个TabLayout,有多个Table结合下面的ViewPage
3、下面为ViewPage中的Fragment,其中用RecyclerView展示我们的商品

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <ImageView
        android:id="@+id/head_view"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:scaleType="fitXY"
        android:src="@mipmap/image" />

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:background="@color/pick"/>

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/view_page"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/black"/>
</LinearLayout>

首先实现这三个模块(内部代码自己写吧后面我会把代码地址给出来)
在这里插入图片描述
大概就是这个样子。
之后大家运行一下发现,几乎跟我们上面实现的效果不搭边。 那我们就继续实现:
4、TabLayout可以吸顶
5、当headerView没有完全隐藏的情况下在RecyclerView上滑动时,优先滑动headerView直至隐藏后RecyclerView继续滑动。
6、在headView上向下fling,会将剩余的惯性传递到RecyclerView。
这时候就要用到CoordinatorLayout布局了。

CoordinatorLayout知识点讲解

1、CoordinatorLayout是什么布局?
他是协调者布局,本身是个FrameLayout,是继承与NestedScrollingParent2和NestedScrollingParent3来解决滑动冲突。

CoordinatorLayout is a super-powered FrameLayout. //API中的解释

public class CoordinatorLayout extends ViewGroup implements NestedScrollingParent2,
        NestedScrollingParent3 {

2、协调员是谁?
协调员就是我们的父布局CoordinatorLayout
3、协调谁?
CoordinatorLayout的子女们
4、通过谁来协调?
通过Behavior来萧条
5、协调什么内容?
代理儿女的事件流程、相应兄弟的变化、代理儿女的绘制

实现后续功能

4、TabLayout可以吸顶
5、当headerView没有完全隐藏的情况下在RecyclerView上滑动时,优先滑动headerView直至隐藏后RecyclerView继续滑动。
其中这两项功能通过我们的现有API代码就可以实现。这里我先将代码给大家,再进一步讲解。

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/appbar_layout"
        app:layout_behavior="com.google.android.material.appbar.AppBarLayout$Behavior"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:fitsSystemWindows="true"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <ImageView
                android:id="@+id/head_view"
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:scaleType="fitXY"
                android:src="@mipmap/image" />
        </com.google.android.material.appbar.CollapsingToolbarLayout>

        <com.google.android.material.tabs.TabLayout
            android:id="@+id/tab_layout"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:background="@color/pick"/>
    </com.google.android.material.appbar.AppBarLayout>

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/view_page"
        app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

我们只需要改变我们的XML就可以实现这个效果。不需要改动任何之前实现的代码。
那这里是怎么处理的呢?
这里是通过:
a、AppBarLayout实现吸顶、并配合CollapsingToolbarLayout实现head的ImageView进行折叠的效果。这里不清楚的单独去查一下AppBarLayout和CollapsingToolbarLayout的作用,毕竟是一个布局这里就不细说了。来实现我们的吸顶
b、通过AppBarLayout S c r o l l i n g V i e w B e h a v i o r 将滑动先传递给 A p p B a r L a y o u t ScrollingViewBehavior将滑动先传递给AppBarLayout ScrollingViewBehavior将滑动先传递给AppBarLayoutBehavior来实现。当headerView没有完全隐藏的情况下在RecyclerView上滑动时,优先滑动headerView直至隐藏后RecyclerView继续滑动。

这样我的4、和5也完成了

6、在headView上向下Fling,会将剩余的惯性传递到RecyclerView
这个需要我们稍微的麻烦一些,通过自定义的Behavior来实现效果。在下面哦,先铺垫一下Behavior的知识。

通过自定义Behavior实现Fling效果

认识Behavior

1、首先Behavior是什么?
Behavior是一个可以控制子View间互动的类
2、Behavior能做什么?
可以用来实现类似于嵌套的ScrollView顶部悬浮的效果,或者实现联动效果,例如当一个View移动时,其他的View随之进行相应的调整。它可以通过实现Behavior类或者继承已有的Behavior类来自定义Behavior,实现在不同的交互场景下子View的动态协调。
3、有几种常见的Behavior

Behavior功能作用
AppBarLayout.ScrollingViewBehavior用于将可滚动视图与AppBarLayout关联,实现当可滚动视图滚动时,AppBarLayout随之滚动,实现顶部标题栏随着内容的滚动而发生变化。
BottomSheetBehavior用于在屏幕的底部展示一个可滑动的特定区域,一般用来显示应用程序的底部菜单或者对话框。
SwipeDismissBehavior用于给View添加滑动关闭的功能,类似于Android系统自带的Notification 消息滑动删除功能。
HeaderScrollingViewBehavior用于将一个可滑动控件(如RecyclerView、NestedScrollView)中的headerView与一个具有特殊行为的顶部控件(如Toolbar)关联,实现当滚动视图滚动时,顶部控件也会同步进行动画效果。
FloatingActionButton.Behavior用于让FloatingActionButton随着滚动事件而出现或者消失。
总体而言,在Android开发中,使用Behavior时要根据实际情况进行选择,不同Behavior之间具有不同的效果,选择合适的Behavior能够大大提升用户体验。

自定义Behavior

想实现Fling的效果还是需要我们来自定义的
先看思路:
第一步:要知道他是Fling这里用ViewConfiguration.get(context).scaledTouchSlop获取并利用ACTION_MOVE判断
第二步:利用onInterceptTouchEvent中ACTION_MOVE判断Fling之后拦截到本层ViewGroup处理事件
第三步:怎么处理事件? 就是将HeaderView的AppBarLayout.Behavior事件交给ViewPage的AppBarLayout.ScrollingViewBehavior事件进行处理。
第四步:(可以说应该是第一步)获取HeaderView的AppBarLayout.Behavior事件
下面代码的中心思想就是围绕着上面的步骤开展的。

class CustomBehavior : AppBarLayout.Behavior {
    constructor() : super()
    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
        if (mTouchSlop < 0) {
            mTouchSlop = ViewConfiguration.get(context).scaledTouchSlop
        }
    }

    private var mTouchSlop: Int = -1
    private lateinit var mScrollingViewBehaviorView: View
    private var mIsBeginDragged = false
    private var mNeedDispatcher = true
    private var mLastMotionY = 0
    private var mActivityPointId = -1
    private var mCurrentEvent: MotionEvent? = null

    //确认滑动去拦截
    override fun onInterceptTouchEvent(
        parent: CoordinatorLayout,
        child: AppBarLayout,
        ev: MotionEvent
    ): Boolean {
        Log.e("onInterceptTouchEvent", "onInterceptTouchEvent: " + ev.action)
        when (ev.action) {
            MotionEvent.ACTION_DOWN -> {
                mIsBeginDragged = false
                mNeedDispatcher = true
                val x = ev.x
                val y = ev.y.toInt()
                if (parent.isPointInChildBounds(child, x.toInt(), y)) {
                    mLastMotionY = y
                    mActivityPointId = ev.getPointerId(0)
                    mCurrentEvent?.recycle()
                    mCurrentEvent = MotionEvent.obtain(ev)
                }
            }

            MotionEvent.ACTION_MOVE -> {
                val activityId = mActivityPointId
                if (activityId != 0 && ev.findPointerIndex(activityId) != -1) {
                    val moveY = ev.y
                    val diffY = abs(moveY - mLastMotionY)
                    if (diffY > mTouchSlop) { //说明是滑动动作
                        mIsBeginDragged = true
                    }
                }
            }

            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                mIsBeginDragged = false
                mNeedDispatcher = true
                mActivityPointId = -1
            }
        }
        if (!mIsBeginDragged) {
            return super.(parent, child, ev)
        }
        return true
    }

    //去处理将AppBarLayout.Behavior事件交给AppBarLayout.ScrollingViewBehavior
    override fun onTouchEvent(
        parent: CoordinatorLayout,
        child: AppBarLayout,
        ev: MotionEvent
    ): Boolean {
        Log.e("onInterceptTouchEvent", "onTouchEvent: " + ev.action)
        var mIsTouchEvent = false
        when (ev.action) {
            MotionEvent.ACTION_DOWN -> {
                mIsTouchEvent = false
            }

            MotionEvent.ACTION_MOVE -> {
                val moveY = ev.y
                val diffY = abs(moveY - mLastMotionY)
                if (diffY > mTouchSlop) { //说明是滑动动作
                    mIsBeginDragged = true
                }
                if (mIsBeginDragged) {
                    //位移到下方View
                    val offset = child.height - child.bottom
                    if (mNeedDispatcher) {
                        mNeedDispatcher = false
                        mCurrentEvent?.offsetLocation(0F, offset.toFloat())
                        //传递下去 进行移交事件
                        mScrollingViewBehaviorView.dispatchTouchEvent(mCurrentEvent)
                    } else {
                        ev.offsetLocation(0F, offset.toFloat())
                        mScrollingViewBehaviorView.dispatchTouchEvent(ev)
                        mIsTouchEvent = true
                    }
                }
            }

            MotionEvent.ACTION_UP -> {
                if (mIsBeginDragged) {
                    ev.offsetLocation(0F, (child.height - child.bottom).toFloat())
                    mScrollingViewBehaviorView.dispatchTouchEvent(ev)
                    mIsTouchEvent = true
                }
            }
        }
        if (!mIsTouchEvent) {
            return super.onTouchEvent(parent, child, ev)
        }
        return true
    }

    override fun onLayoutChild(
        parent: CoordinatorLayout,
        abl: AppBarLayout,
        layoutDirection: Int
    ): Boolean {
        val onLayoutChild = super.onLayoutChild(parent, abl, layoutDirection)
        val childCount: Int = parent.childCount
        for (i in 0 until childCount) {
            val childView: View = parent.getChildAt(i)
            val behavior: CoordinatorLayout.Behavior<AppBarLayout> =
                (childView.layoutParams as CoordinatorLayout.LayoutParams).behavior as CoordinatorLayout.Behavior<AppBarLayout>
            if (behavior is AppBarLayout.ScrollingViewBehavior) {
                mScrollingViewBehaviorView = childView
            }
        }
        return onLayoutChild
    }
}

总结

虽然Android API提供给我们很多的方式方法去解决滑动嵌套的问题。但是万变不离其宗,还是要清楚的知道事件分发的流程和具体的原因。
不然就算你去使用了API处理了滑动嵌套的问题。一旦出了问题,依旧会一头雾水。

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

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

相关文章

操作系统课程设计 模拟FAT文件系统的设计与实现

一、 目的与要求 1.研究FAT文件系统的物理布局。 2.掌握FAT文件系统中目录的结构与目录项定义。 3.掌握文件操作如建立目录&#xff0c;建立文件&#xff0c;删除文件&#xff0c;复制文件时&#xff0c;对FAT和目录的操作步骤。 4.合理设计文件系统布局与数据结构&#xff08…

Day_49归并排序

目录 一. 归并排序的思想 1.归并排序的过程 2. 两种实现方式&#xff1a; 2.1 非递归实现方式 2.2递归实现方式 二. 归并排序的代码实现 1. 数组的辅助空间及初始化 2. 核心代码 2.1每个小组的基本设置 2.2小组内部的排序 三. 代码展示 四. 运行结果 五. 总结 一. 归并排序…

Android逆向环境搭建

逆向工具 手机root Android逆向必要条件是你需要有个Root的手机&#xff0c;最好是真机。没有root的手机&#xff0c;逆向工作很难继续下去&#xff0c;手机的话&#xff0c;能解锁OEM的都可以。手机Root的话一般有以下几步&#xff1a; 解锁OEM解锁BootLoader安装Magisk制作…

Win11的两个实用技巧系列之夜间模式怎么定时、如何卸载重装应用商店?

Win11如何卸载重装应用商店? Win11删除重装Microsoft Store的命令 Win11如何卸载重装应用商店&#xff1f;Win11中的应用商店想要卸了重装&#xff0c;该怎么操作呢&#xff1f;下面我们就来看看Win11删除重装Microsoft Store的命令 Microsoft Store也就是微软的应用商店&…

Redis的主从复制和哨兵机制详解

目录 一、CAP 原理二、Redis主从同步1、主从同步是什么能干嘛&#xff1f;2、Redis是如何实现数据同步的&#xff1f;2.1.增量同步2.2.快照同步2.3.无盘复制2.4.通过Wait 指令保证强一致性 3、搭建Redis 1主2从3.1.安装Redis3.2.创建1主2从配置文件3.3.启动Redis3.4.验证主从同…

算法分析和大O简介

在本文中&#xff0c;我们将讨论如何分析算法以及为什么这样做很重要&#xff01; 为什么要分析算法&#xff1f; 在我们开始之前&#xff0c;让我们澄清一下什么是算法。在本文中&#xff0c;算法只是解决问题的过程或公式。有些问题非常有名&#xff0c;以至于算法都有名字…

JMeter参数化4种实现方式

目录 前言&#xff1a; 1 参数化释义 2 参数化实现 CSV实例 注意事项 前言&#xff1a; 在使用JMeter进行测试时&#xff0c;参数化允许您模拟不同的用户、不同的数据、不同的操作等&#xff0c;从而增加了测试的灵活性和复用性 1 参数化释义 什么是参数化&#xff1f;…

软件系统开发包括哪些步骤?

在传统的理解中&#xff0c;企业内数字化应用的开发和迭代应该是 IT 部门的工作&#xff0c;但事实并非如此。一方面&#xff0c;激烈的市场竞争和反复出现的疫情给数字化提出了新的要求&#xff1b;另一方面&#xff0c;五花八门的零代码、低代码工具正如雨后春笋一般出现&…

NB-lot和LoRa真正的差别在哪里?

就像要把大象装冰箱一样&#xff0c;物联网&#xff0c;万物互联也是要分步骤的。 一、感知层(信息获取层)&#xff0c;即利用各种传感器等设备随时随地获取物体的信息; 二、网络层(信息传输层)&#xff0c;通过各种电信网络与互联网的融合&#xff0c;将物体的信息实时准确地…

[AI语音克隆] 5秒内克隆您的声音并生成任意语音内容

前言 随着人工智能技术的不断发展&#xff0c;语音克隆技术也得到了越来越多的关注和研究。目前&#xff0c;AI语音克隆技术已经可以实现让机器模拟出一个人的声音&#xff0c;甚至可以让机器模拟出一个人的语言习惯和表情。 然而&#xff0c;AI语音克隆技术仍然面临着许多难…

BB84协议:量子秘钥分发

文章目录 为什么需要量子密钥分发通讯工具基本通讯 BB84协议为什么这样做能防止通讯被窃听?在传统信道上的窃听在量子信道上的窃听 内容来源&#xff1a; 为什么需要量子密钥分发 BB84协议是一种“密钥分发协议”&#xff0c;所以从功能上来讲&#xff0c;它要实现的目标与之…

618大促 | 解析平台、商家和消费者必须面对的三大风险

目录 618年中大促的三大风险 商家乐此不疲的“刷单炒信” 消费者无计可施的“薅羊毛” 平台恨之入骨的“恶意爬虫” 618三类欺诈风险的特点 618电商平台的业务安全防控之道 618大促再次开启&#xff0c;各平台及商家的促销大战如火如荼。 2023年618&#xff0c;京东推出…

【状态估计】用于非标量系统估计的最优卡尔曼滤波(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

leetcode数据结构题解(Java实现,第一天和第二天)

文章目录 第一天217. 存在重复元素53.最大子数组和 第二天1. 两数之和88. 合并两个有序数组 第一天 217. 存在重复元素 题解思路&#xff1a;首先题目需要的是判断数组中是否存在相同的数字&#xff0c;存在返回true,不存在就返回false。 那么显然可以这样做&#xff0c;先进行…

内网IM即时通讯软件WorkPlus,快速连接工作的沟通利器

在当今信息高度发达的时代&#xff0c;企业内部沟通的重要性不言而喻。随着企业团队的扩大和分布式办公的兴起&#xff0c;即时通讯软件成为了一种必不可少的工具。通过即时通讯软件&#xff0c;团队成员可以实时交流和协作&#xff0c;快速解决问题&#xff0c;加快决策过程。…

Echarts的地图实现拖拽缩放同步功能(解决多层geo缩放、拖动卡顿问题)

项目场景&#xff1a; 大屏项目显示云南省3D的地图&#xff0c;可拖拽缩放、地图打点、点击图标弹框等等功能 问题描述 多图层拖拽时会上下层会分离&#xff0c;延迟卡顿 原因分析&#xff1a; 1、拖拽时不同图层的中心坐标没有保持一致&#xff0c; 2、卡顿是数据更新动画时…

玩业余无线电时,突然听到有人呼救,该怎么办?

不管是在玩业余无线电还是在其他活动当中&#xff0c;突然遇到有人求救&#xff0c;都是一件非常重要的事情&#xff0c;因为救援行动的效率快慢和是否及时&#xff0c;都有可能影响到别人的生命安全。 下面是河南宝蓝小编给出的一些思考和建议&#xff0c;希望对大家会有一些帮…

软件测试-测试用例

目录 测试用例的四个要素 测试用例设计方法 基于需求进行测试用例的设计 等价类 边界值 判定表 正交表 通过allpirs画正交表 场景设计法 错误猜测法 面试题 如何模拟弱网 接口如何测试 zip命令测试 测试用例万能公式 水杯测试用例 微信发朋友圈 测试用例的四个…

Java实现的五子棋游戏 ~java.awtjava.swing

文章目录 Java实现的五子棋游戏1.实现效果2.实现源码2.1运行主函数main.java2.2 棋盘布局Chessboard.java3.Algorithm算法 点击下载链接&#xff1a;Java实现的五子棋游戏源码下载 Java实现的五子棋游戏 作业要求&#xff1a; &#xff08;1&#xff09;课题代号&#xff1a; …

老Mac电脑安装macOS Ventura实战

前提说明&#xff1a;此实战适用于老旧Mac电脑&#xff08;2015年之前的&#xff0c;无法在系统设置中升级macOS Ventura系统的电脑&#xff09;安装macOS Ventura系统&#xff01; 软件&#xff1a;OpenCore-Patcher-GUI.app.zip 工具&#xff1a;16G U盘、或者移动硬盘&a…