Android低代码开发 - 直接创建一个下拉刷新列表界面

news2024/11/16 21:35:31

看了我Android低代码开发 - 让IDE帮你写代码这篇文章的小伙伴,大概都对Dora全家桶开发框架有基本的认识了吧。本篇文章将会讲解如何使用dora-studio-plugin快捷创建一个下拉刷新列表界面。

效果演示

请添加图片描述

这样直接通过图形界面的方式就创建好了下拉刷新上拉加载+空态界面+列表的基础代码,接下来开发起来就方便了。

依赖库

截屏2024-06-13 10.39.15.png

DoraTitleBar:建议用最新版本1.37

DoraEmptyLayout:必须用1.12版本

SwipeLayout和PullableRecyclerView:用1.0版本就好

IntelliJ IDEA插件1.4版本更新内容

生成布局文件的模板
/*
 * Copyright (C) 2022 The Dora Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.dorachat.templates.recipes.app_package.res.layout

fun swipeLayoutActivityXml(
        packageName: String,
  activityClass: String
) = """
<?xml version="1.0" encoding="utf-8"?>
<layout 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"
    tools:context="${packageName}.${activityClass}">

    <data>
    
    </data>
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <dora.widget.DoraTitleBar
            android:id="@+id/titleBar"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            app:dview_title="@string/app_name"
            android:background="@color/colorPrimary"/>

        <dora.widget.DoraEmptyLayout
            android:id="@+id/emptyLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <dora.widget.pull.SwipeLayout
                android:id="@+id/swipeLayout"
                android:layout_width="match_parent"
                android:layout_height="match_parent">
                <include layout="@layout/layout_swipe_layout_header" />
                <dora.widget.pull.PullableRecyclerView
                    android:id="@+id/recyclerView"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:background="@color/colorPanelBg" />
                <include layout="@layout/layout_swipe_layout_footer" />
            </dora.widget.pull.SwipeLayout>
        </dora.widget.DoraEmptyLayout>
    </LinearLayout>
</layout>
"""
生成Java和Kotlin代码的模板
/*
 * Copyright (C) 2022 The Dora Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.dorachat.templates.recipes.app_package.src

fun swipeLayoutActivityKt(
        applicationPackage: String,
  packageName: String,
        activityClass: String,
  bindingName: String,
  layoutName: String
) = """
package ${packageName}

import android.os.Bundle

import dora.BaseActivity
import dora.widget.pull.SwipeLayout

import ${applicationPackage}.R
import ${applicationPackage}.databinding.${bindingName}

class ${activityClass} : BaseActivity<${bindingName}>() {

   override fun getLayoutId(): Int {
          return R.layout.${layoutName}
   }

   override fun initData(savedInstanceState: Bundle?, binding: ${bindingName}) {
          TODO("Not yet implemented")
            // For Example:
            // binding.swipeLayout.setOnSwipeListener(object : SwipeLayout.OnSwipeListener {
            //
            //     override fun onRefresh(swipeLayout: SwipeLayout) {
            //     }
            //
            //     override fun onLoadMore(swipeLayout: SwipeLayout) {
            //     }
            // })
   }
}
"""

fun swipeLayoutActivity(
        applicationPackage: String,
        packageName: String,
        activityClass: String,
        bindingName: String,
        layoutName: String
) = """
package ${packageName};

import android.os.Bundle;
import androidx.annotation.Nullable;

import dora.BaseActivity;
import dora.widget.pull.SwipeLayout;

import ${applicationPackage}.R;
import ${applicationPackage}.databinding.${bindingName};

public class ${activityClass} extends BaseActivity<${bindingName}> {

   @Override
    protected int getLayoutId() {
        return R.layout.${layoutName};
    }

   @Override
    public void initData(@Nullable Bundle savedInstanceState, ${bindingName} binding) {
        // TODO: Not yet implemented
        // For Example:
        // binding.swipeLayout.setOnSwipeListener(new SwipeLayout.OnSwipeListener() {
        //
        //     @Override
        //     public void onRefresh(SwipeLayout swipeLayout) {
        //     }
        //     
        //     @Override
        //     public void onLoadMore(SwipeLayout swipeLayout) {
        //     }
        // });
   }
}
"""
DoraTemplateRecipe.kt新增生成代码的方法

fun RecipeExecutor.swipeLayoutActivityRecipe(
    moduleData: ModuleTemplateData,
    activityClass: String,
    activityTitle: String,
    layoutName: String,
    packageName: String
) {
    val (projectData, srcOut, resOut) = moduleData

    generateManifest(
        moduleData = moduleData,
        activityClass = activityClass,
        packageName = packageName,
        isLauncher = false,
        hasNoActionBar = false,
        generateActivityTitle = false
    )

    if (projectData.language == Language.Kotlin) {
        save(
            swipeLayoutActivityKt(projectData.applicationPackage ?: packageName, packageName, activityClass,
                buildBindingName(layoutName), layoutName), srcOut.resolve("${activityClass}.${projectData.language.extension}"))
    }
    if (projectData.language == Language.Java) {
        save(swipeLayoutActivity(projectData.applicationPackage ?: packageName, packageName, activityClass,
            buildBindingName(layoutName), layoutName), srcOut.resolve("${activityClass}.${projectData.language.extension}"))
    }
    save(swipeLayoutActivityXml(packageName, activityClass), resOut.resolve("layout/${layoutName}.xml"))

    open(resOut.resolve("layout/${layoutName}.xml"))

}
新增一个向导界面模板
/*
 * Copyright (C) 2022 The Dora Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.dorachat.templates.recipes

import com.android.tools.idea.wizard.template.*
import com.android.tools.idea.wizard.template.impl.activities.common.MIN_API
import java.io.File

object SwipeLayoutActivityTemplate : Template {
    override val category: Category
        get() = Category.Activity
    override val constraints: Collection<TemplateConstraint>
        get() = emptyList()     // AndroidX, kotlin
    override val description: String
        get() = "创建一个dora.BaseActivity,来自https://github.com/dora4/dora"
    override val documentationUrl: String?
        get() = null
    override val formFactor: FormFactor
        get() = FormFactor.Mobile
    override val minSdk: Int
        get() = MIN_API
    override val name: String
        get() = "SwipeLayout DataBinding Activity"
    override val recipe: Recipe
        get() = {
            swipeLayoutActivityRecipe(
                    it as ModuleTemplateData,
                    activityClassInputParameter.value,
                    activityTitleInputParameter.value,
                    layoutNameInputParameter.value,
                    packageName.value
            )
        }
    override val uiContexts: Collection<WizardUiContext>
        get() = listOf(WizardUiContext.ActivityGallery, WizardUiContext.MenuEntry, WizardUiContext.NewProject, WizardUiContext.NewModule)
    override val useGenericInstrumentedTests: Boolean
        get() = false
    override val useGenericLocalTests: Boolean
        get() = false

    override val widgets: Collection<Widget<*>>
        get() = listOf(
                TextFieldWidget(activityTitleInputParameter),
                TextFieldWidget(activityClassInputParameter),
                TextFieldWidget(layoutNameInputParameter),
                PackageNameWidget(packageName),
                LanguageWidget()
        )

    override fun thumb(): Thumb {
        return Thumb { findResource(this.javaClass, File("template_activity.png")) }
    }

    val activityClassInputParameter = stringParameter {
        name = "Activity Name"
        default = "MainActivity"
        help = "The name of the activity class to create"
        constraints = listOf(Constraint.CLASS, Constraint.UNIQUE, Constraint.NONEMPTY)
        suggest = { layoutToActivity(layoutNameInputParameter.value) }
    }

    var layoutNameInputParameter: StringParameter = stringParameter {
        name = "Layout Name"
        default = "activity_main"
        help = "The name of the layout to create for the activity"
        constraints = listOf(Constraint.LAYOUT, Constraint.UNIQUE, Constraint.NONEMPTY)
        suggest = { activityToLayout(activityClassInputParameter.value) }
    }

    val activityTitleInputParameter = stringParameter {
        name = "Title"
        default = "Main"
        help = "The name of the activity. For launcher activities, the application title"
        visible = { false }
        constraints = listOf(Constraint.NONEMPTY)
        suggest = { buildClassNameWithoutSuffix(activityClassInputParameter.value, "Activity") }
    }
    val packageName = defaultPackageNameParameter
}
将向导界面模板添加到向导模板提供者
/*
 * Copyright (C) 2022 The Dora Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.dorachat.templates.recipes

import com.android.tools.idea.wizard.template.WizardTemplateProvider

class DoraTemplateWizardProvider: WizardTemplateProvider() {
    override fun getTemplates() = listOf(
            DataBindingActivityTemplate,
            DataBindingFragmentTemplate,
            MenuPanelActivityTemplate,
            SwipeLayoutActivityTemplate,
            MVVMActivityTemplate,
            MVVMFragmentTemplate)
}
更新版本日志
patchPluginXml {
    version.set("${project.version}")
    sinceBuild.set("213")
    untilBuild.set("223.*")
    changeNotes.set("""
    <h3>1.4</h3>
  新增对SwipeLayout的支持<br/>
    <h3>1.3</h3>
  新增对MenuPanel的支持<br/>
    <h3>1.2</h3>
  新增对BaseVMActivity和BaseVMFragment的支持<br/>
    <h3>1.1</h3>
  initData()方法中增加databinding参数<br/>
    <h3>1.0</h3>
  初始版本,能够创建Java和Kotlin版本的MVVM Activiy和MVVM Fragment<br/>
  """)
}

代码讲解

DoraEmptyLayout为什么可以识别SwipeLayout里面的RecyclerView?
open fun showContent() {
    runMain {
        if (contentView is RecyclerView) {
            if ((contentView as RecyclerView).adapter == null ||
                    (contentView as RecyclerView).adapter!!.itemCount == 0) {
                showEmpty()
                return@runMain
            }
        }
        // 1.12开始支持遍历容器,确保一个EmptyLayout里面只能放一个RecyclerView
        if (contentView is ViewGroup) {
            for (i in 0 until childCount) {
                val view = getChildAt(i)
                if (view is RecyclerView) {
                    if (view.adapter == null ||
                        view.adapter!!.itemCount == 0) {
                        showEmpty()
                        return@runMain
                    }
                }
            }
        }
        val view = showStateView(STATE_CONTENT)
        this.content?.invoke(view)
    }
}

我们可以看到DoraEmptyLayout类从1.11升级到1.12版本的过程中,新增了以下代码来支持SwipeLayout。

if (contentView is ViewGroup) {
    for (i in 0 until childCount) {
        val view = getChildAt(i)
        if (view is RecyclerView) {
            if (view.adapter == null ||
                        view.adapter!!.itemCount == 0) {
                showEmpty()
                return@runMain
            }
        }
    }
}        

这样就不难理解了,如果遇到了刷新布局,如SwipeLayout,就再解析一层找RecyclerView。当然,一个DoraEmptyLayout里面只能放一个RecyclerView。

SwipeLayout是何方神圣?
  • 支持暗色模式
  • 支持英语、阿拉伯语、德语、西班牙语、法语、意大利语、日语、韩语、葡萄牙语、俄语、泰语、越南语、简体中文和繁体中文等世界10几个主流语种
  • 支持自定义可拉动的内容布局
  • 支持插件创建
  • 与DoraEmptyLayout空态布局完美兼容(需要使用v1.12以上版本)
  • 界面丝滑
通过PullableRecyclerView源码来看怎么自定义可拉动的内容布局
package dora.widget.pull

import android.content.Context
import android.util.AttributeSet
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import dora.widget.swipelayout.R

class PullableRecyclerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null,
                                                     defStyle: Int = 0)
                                                : RecyclerView(context, attrs, defStyle), Pullable {

    private var canPullDown = true
    private var canPullUp = true

    init {
        val a = context.obtainStyledAttributes(attrs, R.styleable.PullableRecyclerView, defStyle, 0)
        canPullDown = a.getBoolean(R.styleable.PullableRecyclerView_dview_canPullDown, canPullDown)
        canPullUp = a.getBoolean(R.styleable.PullableRecyclerView_dview_canPullUp, canPullUp)
        a.recycle()
    }

    fun setCanPullDown(canPullDown: Boolean) {
        this.canPullDown = canPullDown
    }

    fun setCanPullUp(canPullUp: Boolean) {
        this.canPullUp = canPullUp
    }

    override fun canPullDown(): Boolean {
        return if (canPullDown) {
            val layoutManager = layoutManager as LinearLayoutManager?
            val adapter = adapter
            if (adapter != null) {
                return if (adapter.itemCount == 0) {
                    false
                } else layoutManager!!.findFirstVisibleItemPosition() == 0
                    && getChildAt(0).top >= 0
            }
            false
        } else {
            false
        }
    }

    override fun canPullUp(): Boolean {
        if (canPullUp) {
            val layoutManager = layoutManager as LinearLayoutManager
            if (adapter != null && adapter?.itemCount!! == 0) {
                return false
            } else if (layoutManager.findLastVisibleItemPosition() == ((adapter as Adapter).itemCount - 1)) {
                // 滑到底部了
                if (getChildAt(layoutManager.findLastVisibleItemPosition() - layoutManager.findFirstVisibleItemPosition()) != null
                        && getChildAt(
                        layoutManager.findLastVisibleItemPosition()
                                - layoutManager.findFirstVisibleItemPosition()).bottom <= measuredHeight
                ) {
                    return true
                }
            }
            return false
        } else {
            return false
        }
    }
}

它实现了一个顶层接口Pullable,通过canPullDown()canPullUp()两个方法来在运行时动态判断可不可以下拉刷新和上拉加载。

private var canPullDown = true
private var canPullUp = true

里面提供了两个属性,表示是否有下拉和上拉能力,如果设置为false,则无论条件达成与否都不能进行刷新和加载。

<dora.widget.pull.PullableRecyclerView
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorPanelBg"
    app:dview_canPullDown="true"
    app:dview_canPullUp="false"/>

可以通过属性设置这两个变量,比如不需要上拉加载就把上拉的设置为false,app:dview_canPullUp=“false”。

设置完成刷新和加载的监听
binding.swipeLayout.setOnSwipeListener(new SwipeLayout.OnSwipeListener() {

    @Override
    public void onRefresh(SwipeLayout swipeLayout) {
    }

    @Override
    public void onLoadMore(SwipeLayout swipeLayout) {
    }
});

通过调用swipeLayout的refreshFinish(state)loadMoreFinish(state)来结束刷新和加载状态。

const val SUCCEED = 0
const val FAIL = 1

有成功和失败两种状态可以设置。所以,你在onRefresh()或onLoadMore()的最后一行调用刷新状态即可。

源码链接

下拉刷新:https://github.com/dora4/dview-swipe-layout

空态布局:https://github.com/dora4/dview-empty-layout

代码生成插件:https://github.com/dora4/dora-studio-plugin

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

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

相关文章

3. 打造个性化可爱怪物表情包:详细步骤教学

表情符号已经成为当今互联网对话中不可或缺的元素&#xff0c;一句话加上一个笑脸符号&#xff0c;语气就大不同。表情符号与我们一道稳步发展&#xff0c;成为鲜活和丰富情感的必要交流工具。通过表情符号&#xff0c;几个像素就能以有趣、清晰、能引起情感共鸣的方式表达我们…

d3dcompiler_43.dll是什么文件?怎么高效率的解决d3dcompiler_43.dll丢失问题

d3dcompiler_43.dll是什么文件&#xff1f;当你知道d3dcompiler_43.dll这个文件名字的时候&#xff0c;相信你是遇到了d3dcompiler_43.dll丢失的问题了&#xff01;所以才会这样问&#xff0c;其实这就是一个普通的dll文件&#xff0c;对于电脑系统有着至关重要的作用&#xff…

100v 高耐压ldo 高压三端稳压芯片

100v 高耐压ldo 高压三端稳压芯片

【Android 11】AOSP Settings添加屏幕旋转按钮

前言 这里是客户要求添加按钮以实现屏幕旋转。屏幕旋转使用adb的命令很容易实现&#xff1a; #屏幕翻转 adb shell settings put system user_rotation 1 #屏幕正常模式 adb shell settings put system user_rotation 0这里的值可以是0&#xff0c;1&#xff0c;2&#xff0c…

pyinstaller打包exe多种失败原因解决方法

pyinstaller打包exe多种失败原因解决方法 目录 pyinstaller打包exe多种失败原因解决方法1、pyinstaller安装有问题1.1 安装pyinstaller1.2 采用anconda的环境启动 2、pyqt5与pyside6冲突2.1 打包生成.spec文件2.2 编辑spec文件 3、打包成功后打不开exe&#xff0c;exe闪退3.1 s…

网格重构技术在AI绘画中的革新作用

引言&#xff1a; 随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;艺术创作也迎来了前所未有的变革。AI绘画不仅改变了艺术家的创作方式&#xff0c;还为非专业人士开启了艺术创作的大门。在众多AI技术中&#xff0c;网格重构技术因其独特的作用和效果成为A…

【CH32V305FBP6】USBD HS 中断分析

文章目录 前言中断分析 USBHS_IRQHandler传输完成&#xff1a;USBHS_UIF_TRANSFERTOKEN_IN&#xff1a;发送完成TOKEN_OUT&#xff1a;接收完成 描述符&#xff1a;USBHS_UIF_SETUP_ACT总线复位&#xff1a;USBHS_UIF_BUS_RST总线挂起&#xff1a;USBHS_UIF_SUSPEND 前言 所有…

图文解析ASN.1中BER编码:结构类型、编码方法、编码实例

本文将详细介绍ASN.1中的BER编码规则&#xff0c;包括其编码机制、数据类型表示、以及如何将复杂的数据结构转换为二进制数据。通过本文的阅读&#xff0c;读者将对ASN.1中的BER编码有一个全面的理解。 目录 一.引言 二.BER编码基本结构 ▐ 1. 类型域&#xff08;Type&#…

光伏气象站:智能驱动,助力光伏产业绿色发展

TH-FGF9在全球能源结构转型和环境保护的大背景下&#xff0c;分布式光伏发电以其清洁、可再生的特性&#xff0c;逐渐成为了能源领域的新宠。然而&#xff0c;光伏发电的效率受气象条件影响较大&#xff0c;如光照强度、温度、风速等因素都会对光伏电站的发电效率产生直接影响。…

flask实战之模板实现公共导航

基础实现 目标 在Flask中&#xff0c;使用模板继承和块&#xff08;blocks&#xff09;可以方便地提取公共导航菜单&#xff0c;使得您可以在多个页面上重用相同的导航结构。以下是一个基本示例&#xff0c;展示如何创建一个包含公共导航菜单的模板&#xff1a; 创建基础模板…

Python-Socket网络编程简单示例

# TCP 服务端程序 server.py # 导入socket 库 from socket import *# 主机地址为空字符串&#xff0c;表示绑定本机所有网络接口ip地址 # 等待客户端来连接 IP # 端口号 PORT 50000 # 定义一次从socket缓冲区最多读入512个字节数据 BUFLEN 512# 实例化一个socket对象 # 参…

实测完快手的AI视频「可灵」后,我觉得这才是第一个中国版Sora

6月6号&#xff0c;是快手的13周年生日。 在这一天&#xff0c;所有AI圈的人都想不到&#xff0c;快手在13周年之际&#xff0c;没有任何预兆、没有任何宣传&#xff0c;直接发布了他们的AI视频大模型。 可灵。 给我也干了个措手不及。 我当时正在看360的发布会&#xff0c;…

SD5510 单节锂离子电池充电器和恒定5V升压控制器芯片IC

一般描述 SD5510为一款移动电源专用的单节锂离子电池充电器和恒定5V升压控制器&#xff0c;充电部分集高 精度电压和充电电流调节器、预充、充电状态指示和充电截止等功能于一体&#xff0c;可以输出最大1A充电电流。而升压电路采用CMOS工艺制造的空载电流极低的VFM开关…

SpringBoot不用写Controller、不用写Service、不用建表,直接起飞是什么感觉

Spring Data REST 提供了一种简单的方式来暴露 JPA 实体为 RESTful 服务&#xff0c;这使得构建基于 REST 的数据服务变得非常快速和高效。下面是一个使用 Spring Data REST 构建通用架构的基本示例&#xff1a; 首先&#xff0c;我们需要创建一个实体类&#xff08;例如&…

Centos离线安装Python3

目录 1.准备工作 2.解压python压缩包 3.编译 4.安装、更改环境变量 5.建立pip连接 使用的是Centos7服务器&#xff0c;Py版本是py3.9.0 1.准备工作 首先确保服务器中存在相关的编译器&#xff0c;例如GCC&#xff1b;这里不做过多叙述&#xff0c;需要者前往&#xff1a…

【全开源】旅行吧旅游门票预订系统源码(FastAdmin+ThinkPHP+Uniapp)

&#x1f30d;旅游门票预订系统&#xff1a;畅游世界&#xff0c;一键预订 一款基于FastAdminThinkPHPUniapp开发的旅游门票预订系统&#xff0c;支持景点门票、导游产品便捷预订、美食打卡、景点分享、旅游笔记分享等综合系统&#xff0c;提供前后台无加密源码&#xff0c;支…

milvus的GPU索引

前言 milvus支持多种GPU索引类型&#xff0c;它能加速查询的性能和效率&#xff0c;特别是在高吞吐量&#xff0c;低延迟和高召回率的场景。本文我们将介绍milvus支持的各种GPU索引类型以及它们适用的场景、性能特点。 下图展示了milvus的几种索引的查询性能对比&#xff0c;…

40. 【Java教程】数据库编程

本小节我们将学习如何使用 Java 语言结合数据库进行编程。注意&#xff0c;学习本小节需要你有一定的 SQL 基础&#xff0c;了解 MySQL 数据库的 基础 CRUD 操作。 本小节我们将选择开源免费的 MySQL 5.7 作为数据库&#xff0c;可以去官网下载并安装 MySQL。 通过本小节的学…

nosql数据库的特点

NoSQL简介 NoSQL是一种不同于关系数据库的数据库管理系统设计方式,是对非关系型数据库的统称,它所采用的数据模型并非传统关系数据库的关系模型,而是类似键/值、列族、文档等非关系模型。NoSQL数据库没有固定的表结构,通常也不存在连接操作,也没有严格遵守ACID约束。因此…

无法在地址[localhost]和端口[8005]上创建服务器关闭套接字(基本端口[8005]和偏移量[0])

今天小伙伴问我一个问题&#xff0c;说是新服务器启动应用&#xff0c;报了一个错误&#xff0c;如下&#xff1a; 一开始我怀疑是端口被占用 经过排查端口没有被占用&#xff0c;然后我怀疑localhost解析有问题 经过 ping localhost 后&#xff0c;得到以下结果到这里很明…