简介本资源是一份面向高校Android开发初学者与课程设计实践者的完整安卓应用项目聚焦学生信息管理系统的实战开发。项目基于Android Studio官方IDE构建覆盖UI布局XMLConstraintLayoutRecyclerView、数据存储SQLiteSharedPreferences、Activity生命周期、Intent跳转、权限适配、自定义Dialog与Menu等核心知识点可作为移动应用开发课程设计标准范例或自学进阶参考。压缩包共1426个文件含338个XML布局与资源文件、280个PNG图标素材、255个编译后class字节码、39个Java源码及12个APK安装包整体21.14MB结构清晰便于逐模块学习调试。目前已有3743人下载学习资源包含可直接运行的debug版APK、完整Gradle工程配置、调试用Logcat日志规范及典型错误处理提示助读者快速理解项目架构、复现功能并迁移至个人开发实践。1. 这不是“Hello World”练习一个能真跑起来的学生信息管理App从AS新建项目到SQLite增删改查全链路闭环你手头那份标着“Android Studio学生信息管理App安卓课程设计完整版”的压缩包大概率不是PPT截图或半成品Demo——它是一套经过真实设备验证、带完整UI交互、本地数据库持久化、且能直接导入AS运行的工程级实践样本。我见过太多学生卡在“建完Activity不会连数据库”“写完SQL语句但数据不显示”“ListView点击没反应却查不出错在哪”的阶段而这个资源的价值恰恰在于它把从New Project → 设计布局 → 创建Student实体类 → 封装SQLiteOpenHelper → 实现DAO层 → 绑定Adapter → 处理Intent跳转 → 完成CRUD操作”这条链路上所有容易断裂的节点用可调试、可打断点、可修改字段的真实代码串了起来。它适合两类人一是刚学完Java基础和Android四大组件、正卡在“理论懂但写不出完整App”的课程设计攻坚者二是需要快速搭建教学演示原型、又不想被Gradle版本冲突或Support库迁移问题拖垮时间的实训导师。别把它当模板抄要把它当黑匣子拆——每个.java文件里都藏着一个可复现的技术决策点。2. 从解压到运行AS环境适配与工程结构破译2.1 解压后第一眼该看什么识别工程类型与Gradle兼容性拿到Mystudent.rar后先解压推荐用7-Zip或WinRAR避免系统自带解压器对长路径支持不佳。解压后进入根目录重点观察三个文件build.gradleProject级查看gradleVersion和android gradle plugin版本号build.gradleModule: app级确认compileSdkVersion、targetSdkVersion及minSdkVersiongradle/wrapper/gradle-wrapper.properties核对distributionUrl指向的Gradle版本提示该工程若基于较老AS版本如3.x常见distributionUrlhttps\://services.gradle.org/distributions/gradle-4.6-all.zip对应AGP插件为3.2.1若为AS 4.2则多为gradle-6.7.1-bin.zip4.2.2插件。不要强行升级——直接匹配AS自带Gradle Wrapper最稳。2.2 AS导入实操选“Import project (Gradle, Eclipse, ADT)”而非“Open”打开Android Studio务必选择File → New → Import Project不是Open然后定位到解压后的Mystudent文件夹即含build.gradle的那一层。AS会自动识别为Gradle项目并触发同步。若弹出“Sync Now”提示点它若卡在“Building ‘Mystudent’…”超过2分钟检查是否因网络导致JCenter仓库超时2021年后JCenter已停服。此时需手动修改build.gradleProject级中的仓库配置// 替换原repositories块含jcenter()的 repositories { google() mavenCentral() // 替代jcenter() // 若有maven { url https://jitpack.io } 保留 }同步成功后Project面板应显示标准Android结构app模块下含java/源码、res/资源、assets/可选、libs/jar包。2.3 工程核心文件速览5个关键类定位逻辑主干文件路径类名核心职责是否必读app/src/main/java/com/example/mystudent/Student.javaStudent数据实体类含id、name、age、major等字段及getter/setter✅ 必读字段名决定DB表结构app/src/main/java/com/example/mystudent/DatabaseHelper.javaDatabaseHelper继承SQLiteOpenHelper定义建表SQL、onCreate/onUpgrade逻辑✅ 必读CRUD底层依赖app/src/main/java/com/example/mystudent/StudentDao.javaStudentDao封装insert/update/delete/query方法返回List 或boolean✅ 必读业务层调用入口app/src/main/java/com/example/mystudent/MainActivity.javaMainActivity主界面含ListViewAdd按钮负责加载数据并启动AddActivity✅ 必读UI与数据绑定app/src/main/java/com/example/mystudent/AddStudentActivity.javaAddStudentActivity添加/编辑界面含EditTextButton处理用户输入并调用Dao✅ 必读交互逻辑中枢注意包名com.example.mystudent是默认命名空间若你AS中新建项目用的是其他包名如com.mycompany.studentapp无需修改代码中的包声明——AS导入时会自动映射但需确保AndroidManifest.xml中application的android:package与之严格一致。3. SQLite本地数据库实现深度拆解从建表到事务安全3.1 DatabaseHelper建表SQL与版本演进的硬编码逻辑DatabaseHelper.java是整个数据持久化的基石。打开它你会看到类似如下结构public class DatabaseHelper extends SQLiteOpenHelper { private static final String DATABASE_NAME student.db; private static final int DATABASE_VERSION 1; // 初始版本号 private static final String TABLE_NAME student_table; // 建表SQL —— 字段名必须与Student.java完全一致 private static final String CREATE_TABLE CREATE TABLE TABLE_NAME ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER, major TEXT ); public DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_TABLE); // 执行建表 } Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // 版本升级时删除旧表重建简易做法生产环境慎用 db.execSQL(DROP TABLE IF EXISTS TABLE_NAME); onCreate(db); } }关键参数说明DATABASE_NAME生成的数据库文件名位于/data/data/package_name/databases/下不可含路径AUTOINCREMENT仅当字段为INTEGER PRIMARY KEY时生效保证id严格递增非必须但课程设计建议保留NOT NULL约束name字段不能为空插入时若传null会抛SQLExceptiononUpgrade()当DATABASE_VERSION从1改为2时触发此处是暴力重建——适合课程设计快速迭代但实际项目中应写增量ALTER语句。3.2 StudentDao面向对象封装的CRUD接口设计StudentDao.java将原始SQL操作包装成易用方法。重点看getAllStudents()和insertStudent()public class StudentDao { private DatabaseHelper dbHelper; private SQLiteDatabase db; public StudentDao(Context context) { dbHelper new DatabaseHelper(context); } // 查询全部学生返回ListStudent public ListStudent getAllStudents() { ListStudent studentList new ArrayList(); String selectQuery SELECT * FROM DatabaseHelper.TABLE_NAME; db dbHelper.getReadableDatabase(); Cursor cursor db.rawQuery(selectQuery, null); if (cursor.moveToFirst()) { do { Student student new Student(); student.setId(cursor.getInt(cursor.getColumnIndexOrThrow(id))); student.setName(cursor.getString(cursor.getColumnIndexOrThrow(name))); student.setAge(cursor.getInt(cursor.getColumnIndexOrThrow(age))); student.setMajor(cursor.getString(cursor.getColumnIndexOrThrow(major))); studentList.add(student); } while (cursor.moveToNext()); } cursor.close(); // ⚠️ 必须关闭否则内存泄漏 return studentList; } // 插入单个学生返回long型rowId public long insertStudent(Student student) { db dbHelper.getWritableDatabase(); ContentValues values new ContentValues(); values.put(name, student.getName()); values.put(age, student.getAge()); values.put(major, student.getMajor()); return db.insert(DatabaseHelper.TABLE_NAME, null, values); // 返回-1表示失败 } }逻辑说明与参数要点getReadableDatabase()vsgetWritableDatabase()查询用前者节省资源增删改必须用后者getColumnIndexOrThrow()比getColumnIndex()更安全字段不存在时直接抛异常便于调试ContentValues键值对容器key必须是建表SQL中定义的列名区分大小写insert()返回值成功返回新记录rowId0失败返回-1——这是判断插入是否成功的唯一可靠依据不能只看是否抛异常。3.3 在MainActivity中加载数据Adapter绑定与ListView刷新机制MainActivity.java的onCreate()中数据加载与UI更新是典型Android模式Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ListView listView findViewById(R.id.listView); Button btnAdd findViewById(R.id.btnAdd); // 初始化Dao并获取数据 StudentDao dao new StudentDao(this); ListStudent students dao.getAllStudents(); // 创建ArrayAdapter并绑定 ArrayAdapterStudent adapter new ArrayAdapter( this, android.R.layout.simple_list_item_2, // 系统内置两行布局 android.R.id.text1, // 主标题TextView ID students ); listView.setAdapter(adapter); // 设置Item点击事件跳转编辑 listView.setOnItemClickListener((parent, view, position, id) - { Student selected students.get(position); Intent intent new Intent(MainActivity.this, AddStudentActivity.class); intent.putExtra(student_id, selected.getId()); // 传递ID用于查询 startActivity(intent); }); // 添加按钮点击 btnAdd.setOnClickListener(v - { Intent intent new Intent(MainActivity.this, AddStudentActivity.class); startActivity(intent); }); }关键细节simple_list_item_2使用系统双行布局第一行显示toString()结果需重写Student类的toString()ArrayAdapter构造函数第3个参数android.R.id.text1是系统布局中主文本控件ID不能写错startActivity()后原Activity仍在栈中onResume()不会自动触发——因此列表刷新必须显式调用见下一节。4. 避坑学生信息管理App高频翻车现场与血泪修复方案4.1 现象App安装后闪退Logcat报android.database.sqlite.SQLiteException: no such table: student_table原因DatabaseHelper.onCreate()从未被执行。常见于①DatabaseHelper实例化后未调用getWritableDatabase()或getReadableDatabase()②StudentDao构造函数中只创建了dbHelper但未在任何方法中触发数据库打开③onCreate()内SQL语法错误如多了一个逗号、字段名拼错导致建表失败且无日志提示。解决在StudentDao的任意方法如getAllStudents()开头强制加一行dbHelper.getWritableDatabase(); // 强制触发onCreate()同时在onCreate()中添加日志Log.d(DBHelper, Creating table: CREATE_TABLE); db.execSQL(CREATE_TABLE); Log.d(DBHelper, Table created successfully);若日志未打印则证明onCreate()根本没走——检查DatabaseHelper是否被正确实例化。4.2 现象ListView显示空白但Logcat无报错getAllStudents()返回空List原因Cursor未移动到第一条记录。cursor.moveToFirst()返回false表示表为空或查询无结果但代码未处理此分支直接跳过循环体。解决修正getAllStudents()中的Cursor遍历逻辑if (cursor.moveToFirst()) { do { // ... 解析字段 } while (cursor.moveToNext()); } else { Log.w(StudentDao, No records found in student_table); // 加日志定位 } cursor.close();更彻底的方法在DatabaseHelper.onCreate()插入测试数据db.execSQL(CREATE_TABLE); // 插入一条测试数据确保表非空 db.execSQL(INSERT INTO TABLE_NAME (name, age, major) VALUES(张三, 20, 计算机));4.3 现象点击ListView某项跳转到AddActivity后EditText显示为空无法编辑原有数据原因AddStudentActivity中未正确接收Intent传递的student_id或接收到后未执行查询。常见错误①getIntent().getIntExtra(student_id, -1)返回-1默认值但未判断是否为有效ID② 查询SQL写成SELECT * FROM student_table WHERE id ?却用rawQuery(sql, new String[]{String.valueOf(id)})参数类型不匹配id是int传入String。解决在AddStudentActivity.onCreate()中Intent intent getIntent(); int studentId intent.getIntExtra(student_id, -1); if (studentId ! -1) { // 查询该学生信息 StudentDao dao new StudentDao(this); Student student dao.getStudentById(studentId); // 需自行实现此方法 if (student ! null) { etName.setText(student.getName()); etAge.setText(String.valueOf(student.getAge())); etMajor.setText(student.getMajor()); currentId studentId; // 记录当前编辑ID用于update操作 } }getStudentById()实现要点public Student getStudentById(int id) { String query SELECT * FROM TABLE_NAME WHERE id ?; db dbHelper.getReadableDatabase(); Cursor cursor db.rawQuery(query, new String[]{String.valueOf(id)}); // ✅ String.valueOf()确保类型一致 Student student null; if (cursor.moveToFirst()) { student new Student(); student.setId(cursor.getInt(0)); student.setName(cursor.getString(1)); student.setAge(cursor.getInt(2)); student.setMajor(cursor.getString(3)); } cursor.close(); return student; }4.4 现象多次点击“添加”按钮数据库中出现重复学生姓名年龄完全一样原因AddStudentActivity中“保存”按钮的点击事件未做空值校验且insertStudent()方法未检查name是否已存在课程设计常忽略此逻辑。解决在保存前增加校验btnSave.setOnClickListener(v - { String name etName.getText().toString().trim(); if (name.isEmpty()) { Toast.makeText(this, 姓名不能为空, Toast.LENGTH_SHORT).show(); return; } int age Integer.parseInt(etAge.getText().toString().trim()); String major etMajor.getText().toString().trim(); Student student new Student(); student.setName(name); student.setAge(age); student.setMajor(major); long result; if (currentId -1) { // 新增 result dao.insertStudent(student); } else { // 更新 student.setId(currentId); result dao.updateStudent(student); // 需实现updateStudent() } if (result ! -1) { Toast.makeText(this, 保存成功, Toast.LENGTH_SHORT).show(); finish(); // 关闭当前页 } else { Toast.makeText(this, 保存失败请重试, Toast.LENGTH_SHORT).show(); } });updateStudent()关键SQLpublic int updateStudent(Student student) { db dbHelper.getWritableDatabase(); ContentValues values new ContentValues(); values.put(name, student.getName()); values.put(age, student.getAge()); values.put(major, student.getMajor()); return db.update( DatabaseHelper.TABLE_NAME, values, id ?, // WHERE条件 new String[]{String.valueOf(student.getId())} // 条件参数 ); }4.5 现象AS编译通过但真机运行时报java.lang.NoClassDefFoundError: Failed resolution of: Landroidx/appcompat/app/AppCompatActivity原因工程使用了AndroidX库但build.gradleModule: app中未启用AndroidX迁移或gradle.properties缺少必要配置。解决确保gradle.properties包含android.useAndroidXtrue android.enableJetifiertrue检查build.gradleModule中依赖是否为AndroidXdependencies { implementation androidx.appcompat:appcompat:1.6.1 implementation com.google.android.material:material:1.10.0 implementation androidx.constraintlayout:constraintlayout:2.1.4 }若原工程用的是android.support.v7.app.AppCompatActivity需全局替换为androidx.appcompat.app.AppCompatActivity并在AndroidManifest.xml中将android:name.MainActivity改为android:nameandroidx.appcompat.app.AppCompatActivity实际应为具体Activity类名此处仅为示意。5. UI交互增强与数据验证实战让课程设计不止于“能跑”5.1 为EditText添加输入限制防止年龄填入负数或超大值原工程中etAge通常只是普通EditText用户可输入-5或999。课程设计加分项是加入输入校验。在AddStudentActivity的onCreate()中添加// 限制年龄为16-35之间的整数 etAge.addTextChangedListener(new TextWatcher() { Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} Override public void onTextChanged(CharSequence s, int start, int before, int count) {} Override public void afterTextChanged(Editable s) { if (s.toString().isEmpty()) return; try { int age Integer.parseInt(s.toString()); if (age 16 || age 35) { etAge.setError(年龄应在16-35之间); btnSave.setEnabled(false); // 禁用保存按钮 } else { etAge.setError(null); btnSave.setEnabled(true); } } catch (NumberFormatException e) { etAge.setError(请输入有效数字); btnSave.setEnabled(false); } } });参数说明setError()在EditText下方显示红色提示比Toast更符合表单规范btnSave.setEnabled(false)直接禁用提交按钮避免用户误点——这是比弹Toast更有效的防错设计。5.2 ListView条目点击反馈优化添加视觉高亮与长按菜单原工程仅实现点击跳转但真实App需提供操作反馈。在activity_main.xml的ListView中添加属性ListView android:idid/listView android:layout_widthmatch_parent android:layout_height0dp android:layout_weight1 android:choiceModesingleChoice !-- 启用单选模式 -- android:listSelectordrawable/list_selector !-- 自定义选中背景 -- /新建res/drawable/list_selector.xml?xml version1.0 encodingutf-8? selector xmlns:androidhttp://schemas.android.com/apk/res/android item android:state_pressedtrue android:drawablecolor/pressed_color / item android:state_selectedtrue android:drawablecolor/selected_color / item android:drawableandroid:color/transparent / /selector再在res/values/colors.xml中添加color namepressed_color#E0E0E0/color color nameselected_color#BBDEFB/color效果点击时变灰选中后变蓝符合Material Design规范。若需长按弹出“删除”菜单重写onCreateContextMenu()Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); menu.add(0, v.getId(), 0, 删除该学生); } Override public boolean onContextItemSelected(MenuItem item) { AdapterView.AdapterContextMenuInfo info (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); int position info.position; Student selected students.get(position); StudentDao dao new StudentDao(this); if (dao.deleteStudent(selected.getId())) { students.remove(position); adapter.notifyDataSetChanged(); // 刷新列表 Toast.makeText(this, 已删除, Toast.LENGTH_SHORT).show(); } return true; }deleteStudent()实现只需一行SQLpublic boolean deleteStudent(int id) { db dbHelper.getWritableDatabase(); return db.delete(DatabaseHelper.TABLE_NAME, id ?, new String[]{String.valueOf(id)}) 0; }5.3 数据导出为CSV课程设计隐藏加分技能很多课程设计要求“支持数据导出”而SQLite本身不支持直接导出。我们用纯Java实现轻量CSV生成在MainActivity中添加导出按钮及逻辑Button btnExport findViewById(R.id.btnExport); btnExport.setOnClickListener(v - { if (Build.VERSION.SDK_INT Build.VERSION_CODES.R) { // Android 11 使用Storage Access Framework exportToCsvSAF(); } else { // 旧版本使用WRITE_EXTERNAL_STORAGE权限 exportToCsvLegacy(); } }); private void exportToCsvLegacy() { try { File dir Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS); if (!dir.exists()) dir.mkdirs(); File file new File(dir, students_ System.currentTimeMillis() .csv); FileWriter writer new FileWriter(file); // 写入CSV头部 writer.append(ID,姓名,年龄,专业\n); // 写入数据 for (Student s : students) { writer.append(String.valueOf(s.getId())) .append(,).append(s.getName()) .append(,).append(String.valueOf(s.getAge())) .append(,).append(s.getMajor()) .append(\n); } writer.flush(); writer.close(); Toast.makeText(this, 导出成功 file.getAbsolutePath(), Toast.LENGTH_LONG).show(); } catch (IOException e) { e.printStackTrace(); Toast.makeText(this, 导出失败 e.getMessage(), Toast.LENGTH_SHORT).show(); } }注意Android 10 需在AndroidManifest.xml中声明uses-permission android:nameandroid.permission.WRITE_EXTERNAL_STORAGE android:maxSdkVersion28 /Android 11 必须用SAFStorage Access Framework此处略去实现细节因课程设计通常以Android 9-10为目标平台。6. 从“能跑”到“可交付”课程设计答辩前必做的5项验证与自检清单6.1 功能完整性验证覆盖CRUD全场景的Checklist别只测“添加学生”要模拟真实用户全流程。拿出一张纸逐项打钩场景操作步骤预期结果是否通过C创建1. 点击“添加”→填入姓名“李四”、年龄“21”、专业“软件工程”→点保存MainActivity列表末尾新增一项“李四”☐R读取2. 点击列表中“李四”→检查AddActivity中三个EditText是否正确回显EditText内容与添加时完全一致☐U更新3. 修改“李四”的专业为“人工智能”→点保存返回列表后该项专业列变为“人工智能”☐D删除4. 长按“李四”→点“删除该学生”→确认“李四”从列表消失且数据库中该记录被清除☐边界测试5. 添加姓名为空、年龄填“abc”、年龄填“-5”显示对应错误提示且不插入数据库☐提示每完成一项用ADB命令验证数据库真实状态adb shell→run-as com.example.mystudent→sqlite3 databases/student.db→.tables→SELECT * FROM student_table;这是比看UI更可靠的验证方式——UI可能缓存数据库不会说谎。6.2 性能与稳定性压测用100条数据检验Adapter承载力课程设计常被忽略的一点当学生数量从10条涨到100条时ListView是否会卡顿原工程用ArrayAdapter直接加载全部数据对小数据量友好但大数据量下效率低。验证方法在DatabaseHelper.onCreate()中插入100条测试数据for (int i 1; i 100; i) { db.execSQL(INSERT INTO TABLE_NAME (name, age, major) VALUES(学生 i , (18 i % 5) , 专业 (i % 3 1) )); }运行App观察首次加载列表是否明显延迟1秒快速滑动ListView检查是否掉帧或闪烁。优化方案进阶若发现卡顿将ArrayAdapter替换为BaseAdapter并实现getView()的复用逻辑public class StudentAdapter extends BaseAdapter { private ListStudent students; private LayoutInflater inflater; public StudentAdapter(Context context, ListStudent students) { this.students students; this.inflater LayoutInflater.from(context); } Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView null) { convertView inflater.inflate(android.R.layout.simple_list_item_2, parent, false); holder new ViewHolder(); holder.text1 convertView.findViewById(android.R.id.text1); holder.text2 convertView.findViewById(android.R.id.text2); convertView.setTag(holder); } else { holder (ViewHolder) convertView.getTag(); } Student s students.get(position); holder.text1.setText(s.getName()); holder.text2.setText(年龄 s.getAge() 专业 s.getMajor()); return convertView; } static class ViewHolder { TextView text1, text2; } }getView()中的convertView复用机制能让滑动性能提升3倍以上——这已是接近商用App的优化水位。6.3 答辩话术预演如何把技术点讲成“我主动选择的理由”答辩时老师最常问“为什么用SQLite而不是Room”“为什么不用RecyclerView”——别背概念用真实权衡回答“选SQLite而非Room”“Room是Jetpack组件封装了更多抽象层但课程设计要求清晰展示数据库底层操作。我需要让学生看到ContentValues如何映射字段、Cursor如何遍历结果、execSQL如何执行建表——这些在Room中被自动隐藏。等掌握SQLite后Room只是把Dao接口和Query注解替换掉手写SQL学习曲线更平滑。”“用ListView而非RecyclerView”“RecyclerView功能强大但需额外理解LayoutManager、ItemDecoration、ItemAnimator等概念。而ListView的ArrayAdapter和OnItemClickListener是Android开发最经典范式教材、实验指导书、历年考题均以此为基础。先扎牢地基再盖高楼。”“没加网络功能”“课程设计明确要求‘本地学生信息管理’核心目标是掌握Android组件通信Activity跳转、本地存储SQLite、UI渲染ListView三大主线。加入网络请求会引入OkHttp、Retrofit、线程切换AsyncTask已废弃、JSON解析等新知识偏离教学重点。若需扩展可在后续课程中作为‘进阶实验’补充。”6.4 最后一次构建检查APK瘦身与签名准备课程设计提交的APK常因包含调试符号或未签名被拒收。执行以下终局检查生成Release APKBuild → Generate Signed Bundle/APK → APK → Next用课程设计指定密钥若无新建一个Key store path选mystudent.jksAlias填mystudent_key密码统一设为123456检查APK大小正常Release版应 ≤ 4MB含所有资源。若 6MB检查app/src/main/res/下是否有未压缩的大图如photo.jpg用TinyPNG在线压缩验证签名命令行执行jarsigner -verify -verbose -certs app-release.apk输出中若含smk签名密钥和OK即签名成功安装测试adb install -r app-release.apk确认安装后能正常启动、无崩溃。从那以后我每次交付课程设计前都强制走一遍这5项验证功能Checklist打钩、ADB查数据库、100条数据滑动测试、答辩话术默写、Release APK签名验证。不是为了应付老师而是确保当别人在演示环节点开你的APK时那个ListView真的能稳稳地滑动那个“保存成功”的Toast真的会在0.3秒内弹出——这种确定性比任何PPT动画都更有说服力。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
开源电子海图引擎:S57解析与S52渲染的嵌入式ECDIS内核 简介:这是一套面向嵌入式与航海电子系统开发者的S57电子海图(ENC)引擎开源实现,专为Linux平台设计,完整支持IEC 61174标准下的ECDIS核心解析与渲染逻辑,适用于海事软件研发、电子海图教学及S52显示规范研究… · 2026/11/4 10:55:31
Dify开源AI平台:从Docker本地调试到云端生产部署全指南 1. 项目背景与核心价值去年在帮一家初创公司搭建内部知识管理系统时,第一次接触到Dify这个开源项目。当时被它"低代码AI"的定位吸引,经过几轮测试后发现确实能大幅降低企业级AI应用的开发门槛。最近半年我陆续在本地开发环境和云服务器上部署了… · 2026/11/4 10:54:30
具身智能 2026:启元人形机器人开发者生态的“安卓时刻”来了吗 行业分析方向 启元机器人 PGC 征文
一个贯穿全文的问题:人形机器人开发者生态,是否正从"封闭垂直"走向"开源平台化"?一、一个可验证的信号:开源底座正在快速成形图1 说明:启元机器人在 AtomGit 的… · 2026/11/4 12:16:06
MySQL学习(2)数据库创建与备份实战指南 🔥keyipatience:个人主页 🎬作者简介:C/C后端开发学习者 🌟专栏传送门:《c》《linux》《c高阶数据结构》《c数据结构与算法》《前端学习》 《Git》《Mysql》 ⭐️patience is key in life 创建数据库
create database… · 2026/11/4 12:16:06
ffmpeg HLS 分片‑copyts 参数时间戳拷贝带来的各类隐形故障 一、‑copyts 参数业务背景
很多同学在做直播转码、录播切片的时候,会接触 ffmpeg 的‑copyts参数。该参数作用是保留原始输入流的 PTS/DTS 时间戳,不重置时间戳。
很多网上示例,做直播录制直接加上‑copyts。但是新手不理解该参数副作用&a… · 2026/11/4 12:16:06
逻辑严谨吗?8款AI写作辅助网站综合榜,毕业论文轻松搞定! 写论文时是不是总卡在开头,脑袋空白无从下笔?文献看不完,却始终理不清逻辑主线?反复修改格式,查重率还是居高不下?
别担心,现在有AI论文写作工具帮你一键破局。本文深度实测8款热门AI论文辅助平… · 2026/11/4 12:16:06
HashMap 底层实现:数组容量为什么必须是 2 的幂 先给结论
因为要用 & (n-1) 代替取模。n 是 2 的幂时,n-1 的二进制全是 1,位与的结果刚好等价于取模,而且比取模快。
但更值钱的理由在扩容那一步,多数人不知道。
一个很常见的初始化
很多团队写代码时都会这么干:… · 2026/11/4 12:16:06
消防维保之——(防排烟系统)(一) 一、排烟系统组成
板式排烟口、远程执行机构,排烟防火阀、柔性接头、排烟风机、风机控制柜、排烟管道、挡烟垂壁、电动排烟窗。二、防烟系统组成
常闭式加压送风口、送风管道、防火阀、柔性接头、送风风机、风机控制柜。三、打开排烟口联动启动排烟风机
确认火灾报警… · 2026/11/4 12:15:06
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 3:16:49
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 6:59:29
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 7:02:38
AI辅助技术栈评估:提升研发决策效率60% 1. 项目背景与核心价值技术决策一直是研发团队面临的重大挑战。随着技术生态的快速演进,技术栈的选择、维护和迭代变得愈发复杂。我们团队在过去两年里,先后经历了三次重大技术栈迁移,每次都需要投入大量人力进行技术评估和风险分析。直到去年… · 2026/11/4 0:00:17
从生成Token到直接决策:Jev如何重塑AI Agent工程实践 1. 从"生成文字"到"拍板决策":Jev 到底在解决什么问题大多数人第一次接触大模型,脑子里建立的模型是"输入问题、输出文字"。你问它今天天气怎么样,它给你一段描述;你让它写个周报,它给你… · 2026/11/4 0:00:17
32G统一内存跑大模型:内存账、TPS实测与端云分流 想在一台 32G 统一内存的小主机上把大模型跑成"日常可用",这件事我前后折腾了小半年。标题里说的这台 Mac mini M6,是我们这次讨论的目标平台,具体到手规格以实物为准;但下面这套推算方法和实测思路,对任何统… · 2026/11/4 0:00:17
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 3:16:49
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 6:59:29
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 7:02:38