关于Android R文件的坑。本地图片资源id存了数据库,一开始显示正常,后来都乱序了。

针对项目中因图片ID存储导致的显示混乱问题,通过创建映射表并动态获取资源ID的方法,成功解决了这一难题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

项目要做一个效果,需要展示对应的数据,名字带图片,数据本地化后,图片id存入数据库,一开始显示正常,后来同事用了一段时间,突然发现图片都特么的乱了。

顿时我的心也乱了。。。   痛苦过后还得面对现实,解决方案当然是有的啦,灵光一现。在帮助类里这样写。

private static Map<String, Integer> appIconIdMap = new HashMap<>();

    static {
        // 考勤
        appIconIdMap.put(APP_NAME_1, R.drawable.new_office_my_signlog_icon);
        appIconIdMap.put(APP_NAME_2, R.drawable.new_office_my_signcount_icon);
        appIconIdMap.put(APP_NAME_3, R.drawable.new_office_my_go_out_icon);
        appIconIdMap.put(APP_NAME_4, R.drawable.new_office_my_leave_icon);
        appIconIdMap.put(APP_NAME_5, R.drawable.new_office_my_phone_sign_icon);
        appIconIdMap.put(APP_NAME_6, R.drawable.new_office_my_go_out_manage_icon);
        appIconIdMap.put(APP_NAME_7, R.drawable.new_office_my_go_out_sign_icon);
        appIconIdMap.put(APP_NAME_8, R.drawable.new_office_my_phonesign_out_timer_icon);
        appIconIdMap.put(APP_NAME_9, R.drawable.new_office_my_overtime_icon);
        appIconIdMap.put(APP_NAME_10, R.drawable.new_office_my_retroactive_icon);
        // 流程
        appIconIdMap.put(APP_NAME_11, R.drawable.new_make_mine_apply_icon);
        appIconIdMap.put(APP_NAME_12, R.drawable.new_make_wait_process_icon);
        appIconIdMap.put(APP_NAME_13, R.drawable.new_make_termination_process_icon);
        appIconIdMap.put(APP_NAME_14, R.drawable.new_make_notify_icon);
        appIconIdMap.put(APP_NAME_15, R.drawable.new_make_draft_box_icon);
        appIconIdMap.put(APP_NAME_16, R.drawable.new_make_process_play_icon);
        // 财务
        appIconIdMap.put(APP_NAME_17, R.drawable.new_office_my_loanv_icon);
        appIconIdMap.put(APP_NAME_18, R.drawable.new_office_my_wiped_icon);
        appIconIdMap.put(APP_NAME_19, R.drawable.new_office_my_account_changes_icon);
        // 人事
        appIconIdMap.put(APP_NAME_20, R.drawable.new_office_my_need_hand_icon);
        appIconIdMap.put(APP_NAME_21, R.drawable.new_office_my_talent_recommendation_icon);
        appIconIdMap.put(APP_NAME_22, R.drawable.new_office_my_interview_arrangements_icon);
        appIconIdMap.put(APP_NAME_23, R.drawable.new_office_my_interview_feedback_icon);
        appIconIdMap.put(APP_NAME_24, R.drawable.new_office_my_positive_staff_icon);
        appIconIdMap.put(APP_NAME_25, R.drawable.new_office_my_transfer_position_icon);
        appIconIdMap.put(APP_NAME_26, R.drawable.new_office_my_change_salary_icon);
        appIconIdMap.put(APP_NAME_27, R.drawable.new_office_my_resignation_application_icon);
        appIconIdMap.put(APP_NAME_28, R.drawable.new_office_my_contract_renewal_icon);
        appIconIdMap.put(APP_NAME_29, R.drawable.new_office_my_books_icon);
        // 行政
        appIconIdMap.put(APP_NAME_30, R.drawable.new_make_administrative_notify_icon);
        appIconIdMap.put(APP_NAME_31, R.drawable.new_make_administrative_meeting_icon);
        appIconIdMap.put(APP_NAME_32, R.drawable.new_make_official_seal_manager_icon);
        appIconIdMap.put(APP_NAME_33, R.drawable.new_make_administrative_official_seal);
        appIconIdMap.put(APP_NAME_34, R.drawable.new_make_administrative_my_car_icon);
        appIconIdMap.put(APP_NAME_35, R.drawable.new_make_administrative_about_car_icon);
        // 销售
        appIconIdMap.put(APP_NAME_36, R.drawable.new_sell_clue_icon);
        appIconIdMap.put(APP_NAME_37, R.drawable.new_sell_clue_manage_icon);
        appIconIdMap.put(APP_NAME_38, R.drawable.new_sell_chance_icon);
        appIconIdMap.put(APP_NAME_39, R.drawable.new_sell_customer_info_icon);
        appIconIdMap.put(APP_NAME_40, R.drawable.new_sell_contacts_icon);
        appIconIdMap.put(APP_NAME_41, R.drawable.new_sell_share_customer_icon);
        appIconIdMap.put(APP_NAME_42, R.drawable.new_sell_invoice_icon);
        appIconIdMap.put(APP_NAME_43, R.drawable.new_sell_agreement_icon);
        appIconIdMap.put(APP_NAME_44, R.drawable.new_sell_share_agreement_icon);
        // 商务财务
        appIconIdMap.put(APP_NAME_45, R.drawable.new_sell_billing_icon);
        appIconIdMap.put(APP_NAME_46, R.drawable.new_sell_refunds_icon);
        appIconIdMap.put(APP_NAME_47, R.drawable.new_sell_collection_verification_icon);
        // 远程
        appIconIdMap.put(APP_NAME_48, R.drawable.new_make_monitored_icon);
        appIconIdMap.put(APP_NAME_49, R.drawable.new_make_decryption_icon);
        appIconIdMap.put(APP_NAME_50, R.drawable.new_make_outgoing_icon);
        // 项目
        appIconIdMap.put(APP_NAME_51, R.drawable.new_make_project_icon);
        appIconIdMap.put(APP_NAME_52, R.drawable.new_make_schedule_icon);
        appIconIdMap.put(APP_NAME_53, R.drawable.new_make_task_icon);
        // 供应商
        appIconIdMap.put(APP_NAME_54, R.drawable.new_make_supplier_company_icon);
        appIconIdMap.put(APP_NAME_55, R.drawable.new_make_payment_icon);
        appIconIdMap.put(APP_NAME_56, R.drawable.new_make_wiped_icon);
        appIconIdMap.put(APP_NAME_57, R.drawable.new_make_business_icon);
        appIconIdMap.put(APP_NAME_58, R.drawable.purchase_requisition_icon);
        appIconIdMap.put(APP_NAME_59, R.drawable.procurement_execution_feedback_icon);
        appIconIdMap.put(APP_NAME_60, R.drawable.warehousing_appliaction_icon);
        // 资产
        appIconIdMap.put(APP_NAME_61, R.drawable.new_assets_office_supplies_collar_icon);
        appIconIdMap.put(APP_NAME_62, R.drawable.new_assets_collar_management_icon);
        appIconIdMap.put(APP_NAME_63, R.drawable.new_assets_my_demand_icon);
        appIconIdMap.put(APP_NAME_64, R.drawable.new_assets_my_fixed_icon);
        appIconIdMap.put(APP_NAME_65, R.drawable.new_assets_fixed_loan_icon);
        appIconIdMap.put(APP_NAME_66, R.drawable.new_assets_return_of_fixed_icon);
        appIconIdMap.put(APP_NAME_67, R.drawable.new_assets_warehousint_confirmation_icon);
    }

    /**
     * 我的应用
     * 根据appName动态获取app的iconId
     * @param appName
     * @return
     */
    public static Integer getAppIconId(String appName){
        if (appIconIdMap.containsKey(appName)){
            return appIconIdMap.get(appName);
        }
        return 0;
    }


最后通过getAppIconId(appName)方法获取到id,这样动态添加就解决了这个问题了,

其实,我发现我们的R文件(自动生成),每次启动的资源id可能都会变化,资源id的int值并不是一成不变的,所以每次都静态统计一次,然后通过对应的key来取对应的drawable的resource id。这样动态解决了这个问题了。哈哈哈~    

如果大家有什么问题,欢迎一起交流~~~


<think>我们正在开发个移动端在线考试系统,使用Vue3作为前端框架。根据引用内容,我们可以参考已有的在线考试系统设计,但需要针对移动端进行优化。以下是架构和实现方案的详细说明: ### 1. 技术选型 - **前端框架**:Vue3 + Composition API + TypeScript(提高代码可维护性) - **状态管理**:Pinia(轻量级且更适合Vue3) - **UI框架**:Vant4(专为移动端设计的Vue3组件库) - **路由管理**:Vue Router 4 - **HTTP客户端**:Axios(封装请求拦截器,处理Token等) - **构建工具**:Vite(开发体验好,打包速度快) ### 2. 项目结构 ``` src/ ├── assets/ # 静态资源 ├── components/ # 公共组件 ├── composables/ # 组合式API(自定义hook) ├── router/ # 路由配置 ├── store/ # Pinia状态管理 ├── services/ # API服务层(封装axios请求) ├── views/ # 页面组件 ├── utils/ # 工具函数 ├── App.vue └── main.ts ``` ### 3. 关键功能模块 #### 3.1 用户认证 - 登录/注册(短信验证码登录) - Token管理(JWT) - 路由守卫(检查登录状态) 示例代码(路由守卫): ```javascript // router/index.js router.beforeEach((to, from, next) => { if (to.meta.requiresAuth && !store.state.user.token) { next({ name: 'Login' }) } else { next() } }) ``` #### 3.2 考试模块 - 考试列表(分页加载) - 考试详情(倒计时、题目切换) - 答题卡(标记已答/未答) - 交卷处理(计算分数、提交答案) #### 3.3 后台管理(移动端简化版) - 教师端:创建考试、管理试题、查看成绩 - 学生端:参加考试、查看历史成绩 #### 3.4 实时通信(可选) - WebSocket实现考试倒计时同步、强制交卷等 ### 4. 移动端优化 - **响应式布局**:使用rem/vw单位,配合postcss-pxtorem插件 - **手势支持**:题目滑动切换(使用@vueuse/gesture) - **性能优化**: - 懒加载题目图片 - 使用keep-alive缓页面 - 分包加载(Vite动态import) ### 5. 后端接口 根据引用[1][2][3],后端可选择: - Spring Boot(Java) - Node.js(Express/Koa) - 接口规范:RESTful API,返回统格式(如{code, data, msg}) ### 6. 部署 - 前端:Nginx部署(配置gzip压缩、路由history模式fallback) - 后端:Tomcat(Spring Boot)或PM2(Node.js) ### 7. 安全考虑 - HTTPS传输 - 敏感操作验证(如交卷时二次确认) - 防作弊措施:随机题目顺序、选项乱序、切屏检测(移动端需调用原生能力) ### 8. 扩展性 - 使用插件化设计(如支付插件支持多种支付方式) - 微前端架构(未来可接入其他子系统) §§相关问题§§ 1. 移动端在线考试系统如何实现防作弊功能? 2. Vue3中如何使用Pinia进行状态管理? 3. 如何优化Vue3移动端应用的加载速度? 4. 移动端考试系统如何与原生设备交互(如调用摄像头)?</think>### 移动端在线考试系统架构与实现方案(Vue3) #### 、整体架构设计 采用 **前后端分离架构**: ``` 移动端(Vue3) → API网关 → 后端服务(Spring Boot/Node.js) ↑ 实时通信(WebSocket) ↓ 数据库(MySQL/MongoDB) ``` #### 二、技术栈选型 | 模块 | 技术方案 | 说明 | |--------------|-----------------------------------|----------------------------------------------------------------------| | **前端框架** | Vue3 + Composition API + TypeScript | 核心框架,使用`<script setup>`语法提升开发效率 | | **UI组件库** | Vant4 或 NutUI | 专为移动端设计的Vue3组件库(推荐Vant4) | | **状态管理** | Pinia | 轻量级状态管理,替代Vuex | | **路由管理** | Vue Router 4 | 支持路由守卫、懒加载 | | **网络请求** | Axios + 拦截器 | 封装请求/响应拦截器,处理Token验证 | | **实时通信** | Socket.IO | 用于考试倒计时同步、强制交卷等场景 | | **构建工具** | Vite | 极速的开发/构建体验 | #### 三、核心模块实现方案 1. **认证模块** ```vue // 使用Vant4组件实现登录 <template> <van-form @submit="handleLogin"> <van-field v-model="username" placeholder="请输入账号"/> <van-field v-model="password" type="password" placeholder="请输入密码"/> <van-button block type="primary">登录</van-button> </van-form> </template> <script setup> import { ref } from 'vue' import { login } from '@/api/auth' const username = ref('') const password = ref('') const handleLogin = async () => { const { token } = await login({ username: username.value, password: password.value }) localStorage.setItem('token', token) // 储Token } </script> ``` 2. **考试模块关键逻辑** - **防作弊设计**: ```javascript // 监听页面可见性变化 document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'hidden') { warnCheating() // 触发警告 } }) ``` - **答题卡数据结构**: ```typescript interface Question { id: number type: 'single' | 'multiple' | 'essay' content: string options?: string[] answer?: string | string[] // 学生答案 } ``` 3. **实时通信(Socket.IO)** ```javascript // 考试倒计时同步 socket.on('exam-timer', (remainingTime) => { examStore.updateTimer(remainingTime) }) // 强制交卷事件 socket.on('force-submit', () => { showDialog({ message: '考试已结束' }) submitExam() }) ``` #### 四、性能优化策略 1. **加载优化**: - 使用Vite的**动态导入**拆分代码 ```javascript const ExamPaper = defineAsyncComponent(() => import('@/views/ExamPaper.vue')) ``` 2. **渲染优化**: - 虚拟滚动长列表(使用`vue-virtual-scroller`) 3. **缓策略**: ```javascript // 本地答题进度 watch(examStore.answers, (newVal) => { localStorage.setItem(`exam-${examId}`, JSON.stringify(newVal)) }, { deep: true }) ``` #### 五、安全设计 1. **接口安全**: - JWT令牌验证(Axios拦截器) ```javascript axios.interceptors.request.use(config => { config.headers.Authorization = `Bearer ${localStorage.getItem('token')}` return config }) ``` 2. **数据加密**: - 敏感数据使用AES加密传输 3. **防作弊机制**: - 随机题目顺序 - 切屏检测 - 答题过程录像(需要调用原生摄像头) #### 六、部署方案 1. **移动端发布**: - 使用**Capacitor**打包为原生APP(支持iOS/Android) ```bash npm install @capacitor/core @capacitor/cli npx cap init npx cap add android npx cap add ios ``` 2. **后端对接**: - RESTful API 设计规范 - 参考现有系统架构[^1][^2] > 实现建议:优先开发最小可用版本(包含登录、考试、交卷功能),后续迭代增加防作弊、实时监控等高级功能。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值