MapApp 地图应用

news2024/12/24 10:27:29

1. 简述

  1.1 重点

    1)更好地理解 MVVM 架构

    2)更轻松地使用 SwiftUI 框架、对齐、动画和转换

  1.2 资源下载地址:

Swiftful-Thinking:icon-default.png?t=N7T8https://www.swiftful-thinking.com/downloads

  1.3 项目结构图:

  1.4 图片、颜色资源文件图:

  1.5 启动图片配置图:

2. Model 层

  2.1 创建模拟位置文件 Location.swift

import Foundation
import MapKit

struct Location: Identifiable, Equatable{
    let name: String
    let cityName: String
    let coordinates: CLLocationCoordinate2D
    let description: String
    let imageNames: [String]
    let link: String
    
    // UUID().uuidString,生产的每个ID都不一样,为了保证有相同可识别的相同模型,使用名称加城市名称
    var id: String {
        name + cityName
    }
    
    // Equatable 判断 id 是否一样
    static func == (lhs: Location, rhs: Location) -> Bool {
       return lhs.id == rhs.id
    }
}

3. 数据服务层

  3.1 创建模拟位置数据信息服务 LocationsDataSerVice.swift

import Foundation
import MapKit

class LocationsDataService {
    
    static let locations: [Location] = [
        Location(
            name: "Colosseum",
            cityName: "Rome",
            coordinates: CLLocationCoordinate2D(latitude: 41.8902, longitude: 12.4922),
            description: "The Colosseum is an oval amphitheatre in the centre of the city of Rome, Italy, just east of the Roman Forum. It is the largest ancient amphitheatre ever built, and is still the largest standing amphitheatre in the world today, despite its age.",
            imageNames: [
                "rome-colosseum-1",
                "rome-colosseum-2",
                "rome-colosseum-3",
            ],
            link: "https://en.wikipedia.org/wiki/Colosseum"),
        Location(
            name: "Pantheon",
            cityName: "Rome",
            coordinates: CLLocationCoordinate2D(latitude: 41.8986, longitude: 12.4769),
            description: "The Pantheon is a former Roman temple and since the year 609 a Catholic church, in Rome, Italy, on the site of an earlier temple commissioned by Marcus Agrippa during the reign of Augustus.",
            imageNames: [
                "rome-pantheon-1",
                "rome-pantheon-2",
                "rome-pantheon-3",
            ],
            link: "https://en.wikipedia.org/wiki/Pantheon,_Rome"),
        Location(
            name: "Trevi Fountain",
            cityName: "Rome",
            coordinates: CLLocationCoordinate2D(latitude: 41.9009, longitude: 12.4833),
            description: "The Trevi Fountain is a fountain in the Trevi district in Rome, Italy, designed by Italian architect Nicola Salvi and completed by Giuseppe Pannini and several others. Standing 26.3 metres high and 49.15 metres wide, it is the largest Baroque fountain in the city and one of the most famous fountains in the world.",
            imageNames: [
                "rome-trevifountain-1",
                "rome-trevifountain-2",
                "rome-trevifountain-3",
            ],
            link: "https://en.wikipedia.org/wiki/Trevi_Fountain"),
        Location(
            name: "Eiffel Tower",
            cityName: "Paris",
            coordinates: CLLocationCoordinate2D(latitude: 48.8584, longitude: 2.2945),
            description: "The Eiffel Tower is a wrought-iron lattice tower on the Champ de Mars in Paris, France. It is named after the engineer Gustave Eiffel, whose company designed and built the tower. Locally nicknamed 'La dame de fer', it was constructed from 1887 to 1889 as the centerpiece of the 1889 World's Fair and was initially criticized by some of France's leading artists and intellectuals for its design, but it has become a global cultural icon of France and one of the most recognizable structures in the world.",
            imageNames: [
                "paris-eiffeltower-1",
                "paris-eiffeltower-2",
            ],
            link: "https://en.wikipedia.org/wiki/Eiffel_Tower"),
        Location(
            name: "Louvre Museum",
            cityName: "Paris",
            coordinates: CLLocationCoordinate2D(latitude: 48.8606, longitude: 2.3376),
            description: "The Louvre, or the Louvre Museum, is the world's most-visited museum and a historic monument in Paris, France. It is the home of some of the best-known works of art, including the Mona Lisa and the Venus de Milo. A central landmark of the city, it is located on the Right Bank of the Seine in the city's 1st arrondissement.",
            imageNames: [
                "paris-louvre-1",
                "paris-louvre-2",
                "paris-louvre-3",
            ],
            link: "https://en.wikipedia.org/wiki/Louvre"),
    ]
}

4. ViewModel 层

  4.1 创建位置信息的 ViewModel LocationsViewModel.swift

import Foundation
import MapKit
import SwiftUI

class LocationsViewModel: ObservableObject{
    
    /// All loaded locations Published
    @Published var locationes: [Location] = []
    
    /// Current location on map
    @Published var mapLocation: Location {
        didSet {
            // 设置地图位置,然后更新地图区域
            updateMapRegion(location: mapLocation)
        }
    }
    
    /// Current region on map :  这是地图上的当前区域
    @Published var mapRegion: MKCoordinateRegion = MKCoordinateRegion()
    /// 坐标跨度
    let mapSpan = MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
    
    /// Show list of locations : 显示位置列表
    @Published var showLocationsList: Bool = false
    /// Show location detail via sheet : 显示位置详情信息页面
    @Published var sheetLocation: Location? = nil
    
    init() {
        let locations = LocationsDataService.locations
        self.locationes = locations
        self.mapLocation = locations.first!
        self.updateMapRegion(location: locations.first!)
    }
    
    
    /// 更新地图区域
    private func updateMapRegion(location: Location){
        withAnimation(.easeInOut) {
            mapRegion = MKCoordinateRegion(
                // 中心点: 经纬度 latitude: 纬度  longitude: 经度
                center: location.coordinates,
                // 坐标跨度:
                span: mapSpan)
        }
    }
    
    /// 位置列表开关
    func toggleLocationsList(){
        withAnimation(.easeInOut) {
            // showLocationsList = !showLocationsList
            showLocationsList.toggle()
        }
    }
    
    /// 显示下一个位置
    func showNextLocation(location: Location){
        withAnimation(.easeInOut) {
            mapLocation = location
            showLocationsList = false
        }
    }
    
    /// 下一个按钮处理事件
    func nextButtonPressed(){
        // Get the current index
        guard let currentIndex = locationes.firstIndex(where: { $0 == mapLocation }) else {
            print("Could not find current index in locations array! Should naver happen.")
            return
        }
        
        // check if the nextIndex is valid: 检查下一个索引是否有校
        let nextIndex = currentIndex + 1
        guard locationes.indices.contains(nextIndex) else {
            // Next index is NOT avlid
            // Restart from 0
            guard let firstLocation = locationes.first else { return }
            showNextLocation(location: firstLocation)
            return
        }
        
        // Next index IS valid
        let nextLocation = locationes[nextIndex]
        showNextLocation(location: nextLocation)
    }
}

5. 创建 View 层

  5.1 位置列表 View

    1) 创建实现文件 LocationsListView.swift
import SwiftUI

/// 位置列表
struct LocationsListView: View {
    /// 环境变量中的 ViewModel
    @EnvironmentObject private var viewMode: LocationsViewModel
    
    var body: some View {
        List {
            ForEach(viewMode.locationes) { location in
                Button {
                    viewMode.showNextLocation(location: location)
                } label: {
                    listRowView(location: location)
                }
                .padding(.vertical, 4)
                .listRowBackground(Color.clear)
            }
        }
        .listStyle(.plain)
    }
}

extension LocationsListView {
    /// 列表行
    private func listRowView(location: Location) -> some View{
        HStack {
            if let imageName = location.imageNames.first {
                Image(imageName)
                    .resizable()
                    .scaledToFill()
                    .frame(width: 45, height: 45)
                    .cornerRadius(10)
            }
            
            VStack(alignment: .leading) {
                Text(location.name)
                    .font(.headline)
                
                Text(location.cityName)
                    .font(.headline)
            }
            .frame(maxWidth: .infinity, alignment: .leading)
        }
    }
}

struct LocationsListView_Previews: PreviewProvider {
    static var previews: some View {
        LocationsListView()
            .environmentObject(LocationsViewModel())
    }
}
    2) 效果图:

  5.2 位置预览 View

    1) 创建实现文件 LocationPreviewView.swift
import SwiftUI

/// 位置预览视图
struct LocationPreviewView: View {
    /// 环境变量中配置的 viewModel
    @EnvironmentObject private var viewModel: LocationsViewModel
    let location: Location
    
    var body: some View {
        HStack(alignment:.bottom, spacing: 0) {
            VStack(alignment: .leading, spacing: 16) {
                imageSection
                titleSection
            }
            
            VStack(spacing: 8) {
                learnMoreButton
                nextButton
            }
        }
        .padding(20)
        .background(
            RoundedRectangle(cornerRadius: 10)
                .fill(.ultraThinMaterial.opacity(0.7))
                .offset(y: 65)
        )
        .cornerRadius(10)
    }
}

extension LocationPreviewView {
    /// 图片部分
    private var imageSection: some View{
        ZStack {
            if let imageImage = location.imageNames.first{
                Image(imageImage)
                    .resizable()
                    .scaledToFill()
                    .frame(width: 100, height: 100)
                    .cornerRadius(10)
            }
        }
        .padding(6)
        .background(Color.white)
        .cornerRadius(10)
    }
    
    /// 标题部分
    private var titleSection: some View{
        VStack(alignment: .leading, spacing: 4) {
            Text(location.name)
                .font(.title2)
                .fontWeight(.bold)
            
            Text(location.cityName)
                .font(.subheadline)
        }
        .frame(maxWidth: .infinity, alignment: .leading)
    }
    
    /// 了解更多按钮
    private var learnMoreButton: some View{
        Button {
            viewModel.sheetLocation = location
        } label: {
            Text("Learn more")
                .font(.headline)
                .frame(width: 125, height: 35)
        }
        .buttonStyle(.borderedProminent)
    }
    
    /// 下一个按钮
    private var nextButton: some View{
        Button {
            viewModel.nextButtonPressed()
        } label: {
            Text("Next")
                .font(.headline)
                .frame(width: 125, height: 35)
        }
        .buttonStyle(.bordered)
    }
}

struct LocationPreviewView_Previews: PreviewProvider {
    static var previews: some View {
        ZStack {
            Color.black.ignoresSafeArea()
            LocationPreviewView(location: LocationsDataService.locations.first!)
                .padding()
        }
        .environmentObject(LocationsViewModel())
    }
}
    2) 效果图:

  5.3 位置注释 View

    1) 创建实现文件 LocationMapAnnotationView.swift
import SwiftUI

/// 位置注释视图
struct LocationMapAnnotationView: View {
    let accentColor = Color("AccentColor")
    
    var body: some View {
        VStack(spacing: 0) {
            Image(systemName: "map.circle.fill")
                .resizable()
                .scaledToFill()
                .frame(width: 30, height: 30)
                .font(.headline)
                .foregroundColor(.white)
                .padding(6)
                .background(accentColor)
            //.cornerRadius(36)
                .clipShape(Circle())
            
            Image(systemName: "triangle.fill")
                .resizable()
                .scaledToFill()
                .foregroundColor(accentColor)
                .frame(width: 10, height: 10)
                .rotationEffect(Angle(degrees: 180))
                .offset(y: -3)
                .padding(.bottom, 35)
        }
    }
}

#Preview {
    ZStack{
        Color.black.ignoresSafeArea()
        LocationMapAnnotationView()
    }
}
    2) 效果图:

  5.4 主页 View

    1) 创建实现文件 LocationsView.swift
import SwiftUI
import MapKit

/// 主页 View
struct LocationsView: View {
    @EnvironmentObject private var viewModel: LocationsViewModel
    let maxWidthForIpad: CGFloat = 700
    
    var body: some View {
        ZStack {
            mapLayer
                .ignoresSafeArea()
            
            VStack(spacing: 0) {
                header
                    .padding()
                    .frame(maxWidth: maxWidthForIpad)
                Spacer()
                locationsPreviewStack
            }
        }
        // .fullScreenCover 全屏显示
        .sheet(item: $viewModel.sheetLocation) { location in
            LocationDetailView(location: location)
        }
    }
}

extension LocationsView {
    /// 头View
    private var header: some View{
        VStack {
            Button {
                viewModel.toggleLocationsList()
            } label: {
                Text(viewModel.mapLocation.name + ", " + viewModel.mapLocation.cityName)
                    .font(.title2)
                    .fontWeight(.black)
                    .foregroundColor(.primary)
                    .frame(height: 55)
                    .frame(maxWidth: .infinity)
                    .animation(.none, value: viewModel.mapLocation)
                    .overlay(alignment: .leading) {
                        Image(systemName: "arrow.down")
                            .font(.headline)
                            .foregroundColor(.primary)
                            .padding()
                            .rotationEffect(Angle(degrees: viewModel.showLocationsList ? 180 : 0))
                    }
            }
            // 列表
            if viewModel.showLocationsList{
                LocationsListView()
            }
        }
        .background(.thickMaterial.opacity(0.7))
        .cornerRadius(10)
        .shadow(color: Color.black.opacity(0.3), radius: 20, x: 0, y: 15)
    }
    
    /// 地图 View
    private var mapLayer: some View{
        Map(coordinateRegion: $viewModel.mapRegion,
            annotationItems: viewModel.locationes,
            annotationContent: { location in
            // 地图标识颜色
            // MapMarker(coordinate: location.coordinates, tint: .blue)
            // 自定义标识
            MapAnnotation(coordinate: location.coordinates) {
                LocationMapAnnotationView()
                    .scaleEffect(viewModel.mapLocation == location ? 1 : 0.7)
                    .shadow(radius: 10)
                    .onTapGesture {
                        viewModel.showNextLocation(location: location)
                    }
            }
        })
    }
    
    /// 地址预览堆栈
    private var locationsPreviewStack: some View{
        ZStack {
            ForEach(viewModel.locationes) { location in
                // 显示当前地址
                if viewModel.mapLocation == location {
                    LocationPreviewView(location: location)
                        .shadow(color: Color.black.opacity(0.3), radius: 20)
                        .padding()
                        .frame(maxWidth: maxWidthForIpad)
                        .frame(maxWidth: .infinity)
                    // .opacity
                    // .transition(AnyTransition.scale.animation(.easeInOut))
                    // 添加动画
                        .transition(.asymmetric(
                            insertion: .move(edge: .trailing),
                            removal: .move(edge: .leading)))
                }
            }
        }
    }
}

struct LocationsView_Previews: PreviewProvider {
    static var previews: some View {
        LocationsView()
            .environmentObject(LocationsViewModel())
    }
}
    2) 效果图:

  5.5 位置详情页 View

    1) 创建实现文件 LocationDetailView.swift
import SwiftUI
import MapKit

/// 位置详情页视图
struct LocationDetailView: View {
    // @Environment(\.presentationMode) var presentationMode
    // @Environment(\.dismiss) var dismiss
    @EnvironmentObject private var viewModel: LocationsViewModel
    let location: Location
    
    var body: some View {
        ScrollView {
            VStack {
                imageSection
                VStack(alignment: .leading, spacing: 16){
                    titleSection
                    Divider()
                    descriptionSection
                    Divider()
                    mapLayer
                }
                .frame(maxWidth: .infinity, alignment: .leading)
                .padding()
            }
        }
        // 安全区
        .ignoresSafeArea()
        // 超薄材质,灰白色
        .background(.ultraThinMaterial)
        // 添加返回按钮
        .overlay(alignment: .topLeading) {
            backButton
        }
    }
}

extension LocationDetailView{
    /// 滑动切换图
    private var imageSection: some View{
        TabView {
            ForEach(location.imageNames, id: \.self) {
                Image($0)
                    .resizable()
                    .scaledToFill()
                    .frame(width: UIDevice.current.userInterfaceIdiom == .pad  ? nil : UIScreen.main.bounds.width)
                    .clipped()
            }
        }
        .frame(height: 500)
        .tabViewStyle(.page)
        .shadow(color: .black.opacity(0.3), radius: 20, y: 10)
    }
    
    /// 标题视图
    private var titleSection: some View{
        VStack(alignment: .leading, spacing: 8){
            Text(location.name)
                .font(.largeTitle)
                .fontWeight(.semibold)
                .foregroundStyle(.primary)
            Text(location.cityName)
                .font(.title3)
                .foregroundStyle(.secondary)
        }
    }
    
    /// 描述视图
    private var descriptionSection: some View{
        VStack(alignment: .leading, spacing: 16){
            Text(location.description)
                .font(.subheadline)
                .foregroundStyle(.secondary)
            
            if let url = URL(string: location.link) {
                Link("Read more on Wikipedia", destination: url)
                    .font(.headline)
                    .tint(.blue)
            }
        }
    }
    
    /// 地图 View
    private var mapLayer: some View{
        Map(coordinateRegion: .constant(MKCoordinateRegion(
            center: location.coordinates,
            span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))),
            annotationItems: [location]) { location in
            MapAnnotation(coordinate: location.coordinates){
                // 自定义标识
                LocationMapAnnotationView()
                    .shadow(radius: 10)
            }
        }
            .allowsHitTesting(false) // 禁止点击
            .aspectRatio(1, contentMode: .fit) // 纵横比
            .cornerRadius(30)
    }
    
    /// 返回按钮
    private var backButton: some View{
        Button{
           // dismiss.callAsFunction()
            viewModel.sheetLocation = nil
        } label: {
            Image(systemName: "xmark")
                .font(.headline)
                .padding(16)
                .foregroundColor(.primary)
                .background(.thickMaterial)
                .cornerRadius(10)
                .shadow(radius: 4)
                .padding()
        }
    }
}

#Preview {
    LocationDetailView(location: LocationsDataService.locations.first!)
        .environmentObject(LocationsViewModel())
}
    2) 效果图:

  5.6 启动结构体文件 SwiftfulMapAppApp.swift

import SwiftUI

@main
struct SwiftfulMapAppApp: App {
    @StateObject private var viewModel = LocationsViewModel()
    
    var body: some Scene {
        WindowGroup {
            LocationsView()
                .environmentObject(viewModel)
        }
    }
}

6. 整体效果:

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

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

相关文章

腾讯云服务器多少钱一年?腾讯云服务器88元一年,附优惠购买入口

腾讯云服务器可以以低至88元一年的价格购买!这个价格可以说是非常实惠。现在,让我们一起来了解腾讯云服务器的价格以及如何购买优惠的服务器。 如何购买88元一年的腾讯云服务器? 购买腾讯云服务器非常简单,只需按照以下步骤&…

SpringBoot实现IP地址归属地查询

SpringBoot实现IP地址归属地查询 功能特性 标准化的数据格式 每个 IP 数据段的 region 信息都固定了格式: 国家|区域|省份|城市|ISP,只有中国的数据绝大部分精确到了城市,其他国家部分数据只能定位到国家,后前的选项全部是 0。…

聊一聊前端面临的安全威胁与解决对策

前端是用户在使用您的网站或Web应用程序时首先体验到的东西。如果您的Web应用程序的前端受到侵害,它可能会影响整个布局,并造成糟糕的用户体验,可能难以恢复。集成前端安全变得越来越重要,本文将指导您通过可以应用于保护您的Web应…

基于SSM的教学管理系统设计与实现

末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:采用JSP技术开发 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目&#x…

腾讯云服务器价格表查询,腾讯云服务器怎么买便宜?

你是否需要搭建一个属于自己的网站,但是又不知道该如何选择和购买服务器?腾讯云服务器价格表查询,让你轻松了解各款服务器的价格及配置信息,帮助你选择最合适的服务器。同时,我们还为你提供了腾讯云服务器的优惠购买入…

利用jquery对HTML中的名字进行替代

想法&#xff1a;将网页中经常要修改的名字放在一个以jquery编写的js文件中&#xff0c;如果需要修改名字&#xff0c;直接修改js文件中的名字即可。 新建name_07.html文件&#xff0c;写入下面的代码&#xff1a; <!DOCTYPE html> <html> <head><meta …

【docker启动的Jenkins时,遇到时区问题处理】

1、查看容器中的时区 [rootlocalhost jenkins]# docker exec -it jenkins cat /etc/timezone Etc/UTC而本地使用的是Asia/shanghai [rootlocalhost jenkins]# timedatectl | grep Time zoneTime zone: n/a (CST, 0800)###查看 [rootlocalhost jenkins]# cd /usr/share/zoneinf…

UnitTest + Selenium 完成在线加法器自动化测试

1. 任务概述 利用 UnitTest 与 Selenium 编写自动化用例&#xff0c;测试在线加法器中的整数单次加法功能【如123 】 人工操作流程&#xff08;测试 12 是否等于 3&#xff09;&#xff1a; 打开在线加法器点击按钮1&#xff0c;再点击按钮&#xff0c;再点击按钮2&#xff0c…

opencv(2): 视频采集和录制

视频采集 相关API VideoCapture()cap.read()&#xff1a; 返回两个值&#xff0c;第一个参数&#xff0c;如果读到frame&#xff0c;返回 True. 第二个参数为相应的图像帧。cap.release() VideoCapture cv2.VideoCapture(0) 0 表示自动检测&#xff0c;如果在笔记本上运行&…

PHP写一个电商 Api接口需要注意哪些?考虑哪些?

随着互联网的飞速发展&#xff0c;前后端分离的开发模式越来越流行。编写一个稳定、可靠和易于使用的 API 接口是现代互联网应用程序的关键。本文将介绍在使用 thinkphp6 框架开发 电商API 接口时需要注意的要点和考虑的问题&#xff0c;并提供详细的逻辑步骤和代码案例。 1. …

Dubbo协议详解

前言特点应用场景Dubbo协议示例Dubbo协议的不足拓展 前言 Dubbo协议是一种高性能、轻量级的开源RPC框架&#xff0c;主要设计目的是解决分布式系统中服务调用的一些常见问题&#xff0c;例如服务负载均衡、服务注册中心、服务的远程调用等。它支持多种语言&#xff0c;例如Jav…

人工智能基础_机器学习038_中国人寿保费预测(EDA数据探索)_导包_数据探索_---人工智能工作笔记0078

注意 EDA是Exploratory Data Analysis(探索性数据分析)的缩写,它是一种统计分析方法,旨在了解数据的基本特征,并发现数据中的规律和模式。EDA通常是数据分析流程的开始阶段,主要使用可视化工具和统计指标来描述数据的基本特征,如数据的分布、中位数、均值、方差等。通过…

pip 问题

升级pip命令&#xff1a; python -m pip install --upgrade pippip不能下载pytorch&#xff1a; 这个问题我一直没解决。不知道有哪位大佬可以留言给我。把whl文件下载到本地也没有&#xff0c;pip不会进行本地文件夹搜索。

用归并排序算法merge_sort( )求解 逆序对的数量 降低时间复杂度为 nlogn

题目简述 给定一个序列有n个数&#xff0c;求n个数中逆序对的个数&#xff0c;逆序对的定义&#xff1a;i < j && a[i] > a[j]。 输入格式 第一行包含一个整数n。 第二行包含 n 个整数&#xff08;所有整数均在1~1e9范围内&#xff09;&#xff0c;表示整数数…

基于STM32微控制器的巡线小车控制研究

## 一、引言 巡线小车是一种常见的智能车型&#xff0c;通常用于参加各类智能车比赛或者教学实验。本文将基于STM32微控制器对巡线小车进行控制研究&#xff0c;主要包括硬件设计和软件编程两个方面。通过该研究&#xff0c;将实现让巡线小车沿着指定轨迹巡线行驶&#xff0c;并…

LEEDCODE 220 存在重复元素3

class Solution { public:int getId(int a, int valuediff){// 值// return a/(valuediff1);return a < 0 ? (a ) -) / (valuediff 1) - 1 : a / (valuediff 1);}public: unordered_map<int, int> bucket;bool containsNearbyAlmostDuplicate(vector<int>&am…

Flutter 中在单个屏幕上实现多个列表

今天&#xff0c;我将提供一个实际的示例&#xff0c;演示如何在单个页面上实现多个列表&#xff0c;这些列表可以水平排列、网格格式、垂直排列&#xff0c;甚至是这些常用布局的组合。 下面是要做的&#xff1a; 实现 让我们从创建一个包含产品所有属性的产品模型开始。 …

基于STM32的循迹小车项目实战

循迹小车是一种能够沿着预定路线行驶的智能小车&#xff0c;通过巡线传感器检测路面的线路&#xff0c;并根据检测结果调整行驶方向。本项目将基于STM32微控制器实现一个简单的循迹小车&#xff0c;通过学习和实践&#xff0c;帮助初学者熟悉STM32的开发流程和掌握循迹小车的实…

采用Nexus搭建Maven私服

采用Nexus搭建Maven私服 1.采用docker安装 1.创建数据目录挂载的目录&#xff1a; /usr/local/springcloud_1113/nexus3/nexus-data2.查询并拉取镜像docker search nexus3docker pull sonatype/nexus33.查看拉取的镜像docker images4.创建docker容器&#xff1a;可能出现启动…

轮播图(多个一起轮播)

效果图 class MainActivity : Activity(), Runnable {private lateinit var viewPager: ViewPagerprivate lateinit var bannerAdapter: BannerAdapterprivate val images ArrayList<Int>() // 存储图片资源的列表private val handler Handler() // 用于定时发送消息…