Android的三种动画详解(帧动画,View动画,属性动画)

news2024/11/19 6:42:39

Android的三种动画详解(帧动画、View动画、属性动画)_android动画效果大全-CSDN博客

1、帧动画

缺点是:占用内存较高,播放的是一帧一帧的图片,很少使用

顺序播放预先定义的图片,类似于播放视频。

步骤:

1).在drawable文件夹下创建一个animation_picture.xml文件,Root element选择为animation-list.

具体为:右键点击drawable文件夹->New→Drawable Resource File

2)配置自己需要播放的图片

<?xml version="1.0" encoding="utf-8"?>

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"

    android:oneshot="false">

    <item android:drawable="@drawable/food1" android:duration="500"/>

    <item android:drawable="@drawable/food2" android:duration="500"/>

    <item android:drawable="@drawable/food3" android:duration="500"/>

    <item android:drawable="@drawable/laojunshan1" android:duration="500"/>

    <item android:drawable="@drawable/laojunshan2" android:duration="500"/>

</animation-list>

上述xml中,有些属性我们要了解到:

  • 1、android:oneshot=“false”: 表示是否重复播放动画,还是只播放一次;
  • 2、每个item都有Drawable和duration属性,Drawable表示我们要播放的图片;duration表示这张图播放的时间;
3).将animation_picture.xml设置为imageview的播放资源

package com.example.animationtest;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.drawable.AnimationDrawable;

import android.os.Bundle;

import android.util.Log;

import android.view.View;

import android.widget.Button;

import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {

    private ImageView mImageView;

    private AnimationDrawable animationDrawable = null;

    private boolean flag = true;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        Button button = (Button) findViewById(R.id.animationButton);

        mImageView = (ImageView)findViewById(R.id.image_view);

        mImageView.setBackgroundResource(R.drawable.animation_picture);//设置资源文件

        animationDrawable = (AnimationDrawable) mImageView.getBackground();

        button.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                if (flag)

                {

                    animationDrawable.start();//开启动画

                    flag = false;

                }else{

                    animationDrawable.stop();

                    flag = true;

                }

            }

        });

    }

}

实际上,从名字也可以看出,AnimationDrawable是一个Drawable的子类,所以我们定义的xml文件也是放在res/rawable目录下的.

2、View动画

View动画是补间动画,设定起始和终止位置,中间会自动补齐,有平移、缩放、旋转、透明四种选择。对应的类为TranslateAnimation、ScaleAnimation、RotateAnimation、AlphaAnimation。

view动画也称为补间动画,因为我们只需要拿到一个view,设定它开始和结束的位置,中间的view会自动由系统补齐,而不需要帧动画每一幅图都是提前准备好的。

View动画是Android一开始就提供的比较原始的动画,主要支持四种效果:平移、缩放、旋转、透明度变化(渐变) 四种基本效果,我们可以再这四种基础效果的基础上,选择其中的几种进行组合。

优点:效率高,使用方便。

缺点:交互性差,当动画结束后会回到初始位置,对于交互性要求较高的使用属性动画。

        (1) 作用对象局限于View

        (2) 动画效果单一,仅能实现位移、旋转、缩放、透明度四种属性的改变

        (3) 没有改变View真实属性

可以使用xml配置资源文件实现,也可以用代码实现,这里用代码实现。其中包含按钮控制动画和默认显示组合动画。

Bar.java

package com.example.viewanimationtest;

import androidx.annotation.NonNull;

import androidx.appcompat.app.AppCompatActivity;

import android.annotation.SuppressLint;

import android.os.Bundle;

import android.os.Handler;

import android.os.Looper;

import android.os.Message;

import android.util.Log;

import android.view.View;

import android.view.animation.AlphaAnimation;

import android.view.animation.Animation;

import android.view.animation.AnimationSet;

import android.view.animation.RotateAnimation;

import android.view.animation.ScaleAnimation;

import android.view.animation.TranslateAnimation;

import android.widget.Button;

import android.widget.ImageView;

import android.widget.Toast;

/*

*Since the child thread cannot directly modify the main UI,

*it is implemented using the Handler mechanism.

* */

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private ImageView mImageView;

    public static final int TRANSLATE_ANI = 1;

    public static final int ROTATE_ANI = 2;

    public static final int SCALE_ANI = 3;

    public static final int ALPHA_ANI = 4;

    private Handler mHandler = new Handler(Looper.getMainLooper()){

        @Override

        public void handleMessage(@NonNull Message msg) {

            switch(msg.what)

            {

                case TRANSLATE_ANI:

                    mImageView.clearAnimation();//When setting a new animation, first clear the previous animation.

                    Animation translateAnimation = new TranslateAnimation(0,500,

                            0,500);//The animation moves from(0,0)to (500,500).

                    translateAnimation.setDuration(2000);

                    mImageView.setAnimation(translateAnimation);

                    break;

                case ROTATE_ANI:

                    mImageView.clearAnimation();

                    Animation rotateAnimation = new RotateAnimation(0,360,

                            0,0);//The animation rotates from 0° to 360° around(0,0).

                    rotateAnimation.setDuration(2000);

                    mImageView.setAnimation(rotateAnimation);

                    break;

                case SCALE_ANI:

                    mImageView.clearAnimation();

                    Animation scaleAnimation = new ScaleAnimation(0,1,0,1);

                    scaleAnimation.setDuration(2000);

                    mImageView.setAnimation(scaleAnimation);

                    break;

                case ALPHA_ANI:

                    mImageView.clearAnimation();

                    Animation alphaAnimation = new AlphaAnimation(0,1);

                    alphaAnimation.setDuration(2000);

                    mImageView.setAnimation(alphaAnimation);

                    break;

                default:

                    Log.d("1111","default");

                    break;

            }

        }

    };

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        mImageView =(ImageView)findViewById(R.id.image_view);

        Button translateButton = (Button) findViewById(R.id.translate_button);

        Button scaleButton = (Button) findViewById(R.id.scale_button);

        Button rotateButton = (Button) findViewById(R.id.rotate_button);

        Button alphaButton = (Button) findViewById(R.id.alpha_button);

        translateButton.setOnClickListener(this);

        scaleButton.setOnClickListener(this);

        rotateButton.setOnClickListener(this);

        alphaButton.setOnClickListener(this);

        mImageView.clearAnimation();//If some animations exist, clear them first.

        //set mix animation

        AnimationSet animationSet = new AnimationSet(true);

        //set default alpha animation

        Animation alphaAnimation = new AlphaAnimation(0,1);//The animation is from fully transparent to  fully opaque.

        alphaAnimation.setDuration(2000);//The animation lasts 2 seconds.

        //set default scale animation

        Animation scaleAnimation = new ScaleAnimation(0,1,0,1);//The animation is from 0% to 100%.

        scaleAnimation.setDuration(2000);

        //add sub-animations to combined animation

        animationSet.addAnimation(alphaAnimation);

        animationSet.addAnimation(scaleAnimation);

        //show the combined animation

        mImageView.setAnimation(animationSet);

    }

    @SuppressLint("NonConstantResourceId")

    @Override

    public void onClick(View v) {

        switch (v.getId())

        {

            case R.id.translate_button:

                new Thread(new Runnable() {

                    @Override

                    public void run() {

                        Message msg = new Message();

                        msg.what = TRANSLATE_ANI;

                        mHandler.sendMessage(msg);

                    }

                }).start();

                break;

            case R.id.rotate_button:

                new Thread(new Runnable() {

                    @Override

                    public void run() {

                        Message msg = new Message();

                        msg.what = ROTATE_ANI;

                        mHandler.sendMessage(msg);

                    }

                }).start();

                break;

            case R.id.scale_button:

                new Thread(new Runnable() {

                    @Override

                    public void run() {

                        Message msg = new Message();

                        msg.what = SCALE_ANI;

                        mHandler.sendMessage(msg);

                    }

                }).start();

                break;

            case R.id.alpha_button:

                new Thread(new Runnable() {

                    @Override

                    public void run() {

                        Message msg = new Message();

                        msg.what = ALPHA_ANI;

                        mHandler.sendMessage(msg);

                    }

                }).start();

                break;

            default:

                break;

        }

    }

}

具体效果

​编辑view.mp4

3.属性动画

跟补间动画类似。具体内容可以参考文章:

Android的三种动画详解(帧动画、View动画、属性动画)

在Android3.0以后引入了这种动画模式,用来弥补传统的补间动画和帧动画的不足。属性动画的核心类如下图所示:

其中Animator是属性动画的基类,提供了一些通用的方法。AnimatorSet相当于一组属性动画的容器,用来同时执行多个属性动画。app开发中常用的属性动画为ValueAnimator和ObjectAnimator,本文将基于Android O的版本详细介绍ValueAnimator在系统中的实现方式。

优点:交互性强,动画结束时的位置就是最终位置。详细使用可参考:Android进阶之光  书籍

代码实现

Bar.java

package com.example.propertyanimation;

import androidx.appcompat.app.AppCompatActivity;

import android.animation.Animator;

import android.animation.AnimatorSet;

import android.animation.ObjectAnimator;

import android.os.Bundle;

import android.view.animation.AnimationSet;

import android.widget.ImageView;

import java.util.Set;

public class MainActivity extends AppCompatActivity {

    private ImageView mImageview;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        mImageview = (ImageView) findViewById(R.id.image_view);

        //use AnimatorSet to show the combined animation

        AnimatorSet animatorSet = new AnimatorSet();

        //Use the ofFloat function to construct an ObjectAnimator object and set the alpha parameter.

        ObjectAnimator animator1 = ObjectAnimator.ofFloat(mImageview,

                "alpha",0f,1.0f);

        //Set the translationY parameter to move to a certain position along the Y axis

        ObjectAnimator animator2 = ObjectAnimator.ofFloat(mImageview,

                "translationY"200);

        //Set the translationX parameter to move to a certain position along the X axis

        ObjectAnimator animator3 = ObjectAnimator.ofFloat(mImageview,

                "translationX"200);

        //Set the rotation parameter to rotate a certain angle

        ObjectAnimator animator4 = ObjectAnimator.ofFloat(mImageview,

                "rotation"180);

        //Magnify 0.5 times

        ObjectAnimator animator5 = ObjectAnimator.ofFloat(mImageview,

                "scaleX"0.5f);

        //The animation lasts 2 seconds.

        animatorSet.setDuration(2000);

        //set the combined animation

        animatorSet.playTogether(

                animator1,

                animator2,

                animator3,

                animator4,

                animator5

        );

        //start the animation

        animatorSet.start();

    }

}

1.使用方法

private void testValueAnimator() {

    ValueAnimator valueAnimator = ValueAnimator.ofInt(0100);

    valueAnimator.setDuration(300);

    valueAnimator.addListener(new Animator.AnimatorListener() {

        @Override

        public void onAnimationStart(Animator animation) {

            Log.d(TAG, "onAnimationStart");

        }

  

        @Override

        public void onAnimationEnd(Animator animation) {

            Log.d(TAG, "onAnimationEnd");

        }

  

        @Override

        public void onAnimationCancel(Animator animation) {

  

        }

  

        @Override

        public void onAnimationRepeat(Animator animation) {

  

        }

    });

  

    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

        @Override

        public void onAnimationUpdate(ValueAnimator animation) {

            Log.d(TAG, "fraction: " + animation.getAnimatedFraction() + " value: "

                    + animation.getAnimatedValue());

        }

    });

  

    Log.d(TAG, "testValueAnimator");

    valueAnimator.setStartDelay(100);

    valueAnimator.start();

}

        其主要分为以下几个过程:

        (1) 创建ValueAnimator对象 => 通过ofXXX的静态方法获取

        (2) 初始化参数 => 根据一系列setXXX/addXXX方法添加一些初始化参数和回调函数

        (3) 开始动画 => 调用start方法

        在使用属性动画时,客户端通过ValueAnimator.AnimatorUpdateListener接口的回调函数onAnimationUpdate来监听每一帧的变化。属性动画顾名思义,在动画过程中每一帧都会对预设的属性进行改变,客户端通过每一帧该属性的变化自定义一些操作。其预设的属性就是在获取ValueAnimator对象时,通过ofXXX方法设置的。上例中的ValueAnimator.ofInt(0, 100)则表示:在该属性动画执行的过程中,一个属性名为空字符串(默认属性名,后面会说到属性动画的属性名何时为默认,何时为开发者自定义),属性类型为整型的属性在0 ~ 100区间内递增的变化。在每一帧的回调中,开发者可以通过ValueAnimator#getAnimatedValue方法获取默认属性值在该帧的值为多少。上例中的另一个方法:ValueAnimator#getAnimatedFraction则表示该帧的动画进度(0 ~ 1浮点数)为多少。

2.动画初始化过程

        (1) ofXXX方法:设置动画属性类型及变化范围

  初始化提供的方法有:ofInt,ofFloat,ofArgb(描述颜色的ARGB值的变化),ofPropertyValuesHolder(自定义的一系列PropertyValuesHolder,使用该方法时,会在动画每一帧分别处理添加进来的PropertyValuesHolder的变化),ofObject(使用该方法时需要自定义类型估值器)。ofInt,ofFloat,ofObject,ofArgb的实质都是先包装出一个对应的PropertyValuesHolder对象,然后当动画驱动时,根据这个对象计算数当前动画的进度(fraction)以及对应类型值在这一帧的取值(animated value)。ofPropertyValuesHolder方法相当于开发者自己创建若干PropertyValuesHolder对象,在动画驱动时批处理这些对象。

        如果使用ofPropertyValuesHolder,需要开发者自己创建PropertyValuesHolder对象,需要传入对应改变的属性的名字。如果使用其它初始化方法,则属性名字默认为空字符串("")。客户端通过ValueAnimator#getAnimatedValue方法获取每一帧的属性值为多少:如果调用无参的该方法,则返回的是默认的属性值在当前帧的值,因为默认的初始化方法确定了其属性类型,并且只有一个。如果调用的是带String类型参数的该方法,则返回的是对应名字的属性值在当前帧的值,其名字是在创建PropertyValuesHolder对象时设置的。另外,开发者也可以通过ValueAnimator#setValues方法主动添加PropertyValuesHolder对象进来。

        综上,无论通过哪种ofXXX方法进行初始化,最终都会创建一个或多个PropertyValuesHolder对象,这个类是用来管理属性动画中开发者定义的“属性”的变化,每当动画进行时,ValueAnimator对象会计算所有持有的PropertyValuesHolder对象在此帧的属性值为多少,开发者可以通过getAnimatedValue获取。属性动画的核心也在于此——动画驱动过程中,开发者可以自定义任意类型的对象的变化,因此属性动画也打破了补间动画的局限性。

        (2) addUpdateListener:添加一个AnimatorUpdateListener的监听者
        这个监听者的作用在于:在动画驱动过程中,每一帧都会通过该回调通知给客户端进程,客户端可以在该回调中处理每一帧要做的事情。

        (3) addListener:添加一个AnimatorListener的监听者
        这个监听者用来监听动画的开始/结束/取消/重复四个行为的发生。

        (4) setDuration:添加动画执行时长

        (5) setCurrentFraction/setCurrentPlayTime
        设置动画开始的动画进度/开始的时间点(0 ~ Duration范围内)

3.动画启动过程:ValueAnimator#start()

        动画启动过程的关键流程:(step表示上图中的步骤序号)

        step4:向Choreographer中注册一个动画回调,用来驱动整个动画流程。

        step6 ~ step7:初始化PropertyValuesHolder对象中的类型估值器,默认只支持Int/Float两种类型的类型估值器,其他类型需要自定义。

        step8:通知客户端注册的监听者动画开始:AnimatorListener#onAnimationStart被回调。

        step9 ~ step10:设置动画开始时间以及开始时动画进度,如果客户端没有主动通过setCurrentFraction/setCurrentPlayTime设置,则默认会调用setCurrentPlayTime(0),表示动画从头开始。

        step13 ~ step15:入口为animateValue,每一帧的动画都会调用到此方法,该方法主要完成以下几件事:

        (1) 根据时间进度以及动画插值器计算出当前动画进度(fraction)

        (2) 根据当前动画进度以及类型估值器计算出当前PropertyValuesHolder在此帧中的取值是多少(animated value)

        (3) 回调AnimatorUpdateListener#onAnimationUpdate方法

        启动时会调用该方法,认为start方法触发的行为为第一帧(Choreographer驱动的为第二帧)

4.动画驱动过程:Choreographer#doFrame

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

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

相关文章

数据结构 第3章:栈与队列

文章目录 1. 栈1.1 栈的基本概念1.2 栈的基本操作1.3 栈的顺序存储实现1.4 栈的链式存储实现 2. 队列2.1 队列的基本概念2.2 队列的基本操作2.3. 队列的顺序存储实现2.4 队列的链式存储实现2.5 双端队列 3. 栈与队列的应用3.1 栈在括号匹配中的应用3.2 栈在表达式求值中的应用3…

k8s的pod和svc相互访问时网络链路解析

k8s的pod和svc相互访问时网络链路解析 1. k8s环境中pod相互访问1.1. k8s中pod相互访问的整体流程1.2. k8s的相同机器的不同pod相互访问1.3. k8s的不同机器的不同pod相互访问 2. k8s访问svc2.1 nat操作2.2 流量进入到后端pod 3. 疑问和思考3.1 访问pod相互访问为什么不用做nat?…

【Linux】从零开始认识进程 — 前篇

我从来不相信什么懒洋洋的自由。我向往的自由是通过勤奋和努力实现的更广阔的人生。。——山本耀司 从零开始认识进程 1 认识冯诺依曼体系2 操作系统3 进程3.1 什么是进程&#xff1f;&#xff1f;&#xff1f;3.2 进程管理PCB 3.3 Linux中的进程深入理解 3.4 进程创建总结 送给…

【vue baidu-map】实现百度地图展示基地,鼠标悬浮标注点展示详细信息

实现效果如下&#xff1a; 自用代码记录 <template><div class"map" style"position: relative;"><baidu-mapid"bjmap":scroll-wheel-zoom"true":auto-resize"true"ready"handler"><bm-mar…

JVMJava虚拟机

JVM的内存区域 程序计数器&#xff1a; 字节码解释器通过改变程序计数器来依次读取指令&#xff0c;从而实现代码的流程控制&#xff0c;如&#xff1a;顺序执行、选择、循环、异常处理。 在多线程的情况下&#xff0c;程序计数器用于记录当前线程执行的位置&#xff0c;从而当…

python—gui-计算图像像素两点间距离

代码&#xff1a; import tkinter as tk from tkinter import ttkdef create_gui():# 创建Tkinter窗口root tk.Tk()# 设置窗口标题root.title("显示图片")# 图片文件路径image_path path_to_your_image.jpg# 加载图片img load_image(image_path)# 创建标签&#…

产品推荐 - ALINX XILINX FPGA开发板 Artix-7 XC7A100T-2FGG484I

01开发板介绍 此款开发板采用核心板扩展板的模式&#xff0c;方便用户对核心板的二次开发利用。FPGA使用的是Xilinx公司的ARTIX-7系列的芯片&#xff0c;型号为XC7A100T-2FGG484I。在核心板使用了2片MICRON公司的MT41J256M16HA-125 DDR3芯片&#xff0c;组合成32bit的数据总线…

联想拯救者刃7000K2024游戏电脑主机仅售6999元

这款联想拯救者刀锋7000K 2024游戏电脑主机在京东促销中售价仅为6999元&#xff0c;相比原价7499元有相当大的折扣。 这是一款功能强大的游戏电脑&#xff0c;配备了全新的 15-14400(F) 处理器和 RTX™ 4060 显卡&#xff0c;以及 16GB DDR5 内存和 1TB 固态硬盘。 外观方面&a…

STL_vector简化模拟—详解深层次深拷贝问题

文章目录 迭代器框架和成员变量基础成员函数容量相关的成员函数关于深拷贝中的深拷贝问题operator[ ]重载和内容修改函数类模板内的嵌套类型全部代码 根据原码看出vector的成员并不像string类的一个指针加一个size和一个capacity。 而是三个指针&#xff0c;_start , _finish ,…

IntelliJ IDEA 面试题及答案整理,最新面试题

IntelliJ IDEA中的插件系统如何工作&#xff1f; IntelliJ IDEA的插件系统工作原理如下&#xff1a; 1、插件架构&#xff1a; IntelliJ IDEA通过插件架构扩展其功能&#xff0c;插件可以添加新的功能或修改现有功能。 2、安装和管理&#xff1a; 通过IDEA内置的插件市场下载…

【海贼王的数据航海】排序——直接选择排序|堆排序

目录 1 -> 选择排序 1.1 -> 基本思想 1.2 -> 直接选择排序 1.2.1 -> 代码实现 1.3 -> 堆排序 1.3.1 -> 代码实现 1 -> 选择排序 1.1 -> 基本思想 每一次从待排序的数据元素中选出最小(或最大)的一个元素&#xff0c;存放在序列的起始位置&…

springCloudeAlibaba的使用

父pom文件&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.o…

【Docker篇】数据卷相关操作

文章目录 &#x1f388;前言&#x1f354;数据卷&#x1f6f8;操作命令⭐创建一个数据卷&#xff0c;并查看数据卷在宿主机的目录位置 &#x1f339;挂载数据卷 &#x1f388;前言 在前面文章的nginx案例中&#xff0c;修改nginx的html页面时&#xff0c;需要进入nginx内部。并…

k8s-高可用etcd集群 26

reset掉k8s2&#xff0c;k8s3&#xff0c;k8s4节点 清理完网络插件后重启 快速创建一个k8s集群 修改初始化文件 添加master节点 备份 查看etcd配置 启动docker 将etcd二进制命令从容器拷贝到本机 备份 查看快照状态 删除集群资源 恢复 停掉所有的核心组件 从快照恢复 重启所有…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:StepperItem)

用作Stepper组件的页面子组件。 说明&#xff1a; 该组件从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 支持单个子组件。 接口 StepperItem() 属性 参数名参数类型参数描述prevLabelstring设置左侧文本按钮内…

python 统计中国观鸟记录中心官网已观测的鸟类种类

python 统计中国观鸟记录中心官网已观测的鸟类种类 中国观鸟记录中心网站&#xff1a;https://www.birdreport.cn/ 先下载官网 Excel 文件 文件放置目录如下&#xff1a; home dataset xxx.xlsxxxx.xlsxxxx.xlsx Excelgrep.py &#xff08;进行文件内容提取的程序&#xff…

day-21 前 K 个高频元素

思路&#xff1a;用ans[]存储频次最高的k个元素&#xff0c;用anslen[]存储对应的索引&#xff0c;将nums进行排序依次统计每个元素出现次数&#xff0c;再判断是否需要对ans[]和anslen[]进行替换&#xff0c;最后ans即为答案 注意点&#xff1a;遍历结束后&#xff0c;还需要…

数据库引论:2.SQL简介

SQL(Structured Query Language,结构化查询语言) 2.1 SQL查询语言概览 SQL语言包含 数据定义语言(Data-Definition Language,DDL)。SQL DDL提供定义关系模式、删除关系以及修改关系模式的命令。数据操纵语言(Data-Manipulation Language,DML)。SQL DML提供从数据库中查询信息…

【算法】AC自动机的优化:增量更新与删除

一、概述 AC自动机&#xff08;Aho-Corasick Automation&#xff09;是著名的多模匹配算法&#xff0c;源于贝尔实验室&#xff0c;并且在实际应用中得到广泛的引用&#xff0c;且具有以下特点&#xff1a; 只需要扫描一次文本&#xff0c;即可获取所有匹配该文本的模式串复杂…

小红书根据关键词取商品列表 API 返回值说明

小红书根据关键词取商品列表的API返回值通常包含与搜索请求相关的商品列表信息。这些信息包括匹配到的商品列表、商品详情、排序方式等。以下是一个简化的示例&#xff0c;展示了小红书根据关键词取商品列表API可能返回的JSON格式数据&#xff1a;获取调用详情链接 item_searc…