摘 要
随着智能手机的普及和移动互联网渗透率的不断提升,用户可以通过手机新闻客户端获取新闻资讯。在移动设备日益普及的背景下,人们的日常生活习惯经历了显著的演变,这一演变不仅影响了大众的日常行为,更在深层次上改变了大众获取信息的模式。在这样的背景下,移动端新闻阅读凭借其便捷性、实时性和互动性,逐渐成为了人们获取新闻资讯的主要方式。面对社会信息量的急剧增长和个性化需求的日益增强,设计一款高效、精准的焦点新闻APP显得尤为重要。
本次软件开发计划达到实现一个具有登陆/注册、新闻分区导航、新闻获取、新闻详情、新闻评论查看、讨论区、个性化个人信息修改等功能的新闻APP。
在软件开发过程之中,经过筛选最终将Android Studio作为核心的开发平台。这一决策基于其突出的性能和其作为一个有丰富功能的集成开发环境(IDE)优势显著。同时,依托于Java这一业界公认且广受欢迎的编程语言,以确保所开发APP的稳定性和卓越的跨平台兼容性。
在针对应用程序的本地数据持久化策略中,因为对SQL92标准的全面支持、出色的资源优化、高效的数据处理能力和稳定的运行表现,本研究选用SQLite数据库管理系统作为解决方案。SQLite的身份是一款轻量级的关系型数据库管理系统。
在用户界面设计方面,本程序严格遵循了UI/UX设计原则,采用了简洁明了的设计风格,去除冗余装饰,使用户能够快速定位所需功能。同时,本程序还注重细节处理,通过合理的布局、清晰的导航以及流畅的动画效果,进一步提升了用户的整体使用感受。
通过这些技术的运用,本次开发不仅能够提升应用的实用性,还能确保其在视觉上符合现代审美和用户操作习惯。
关键词:焦点新闻APP系统;Android Studio;Java;SQLite;爬虫
ABSTRACT
With the popularization of smart phones and the continuous improvement of mobile Internet penetration, users can obtain news information through the mobile news client.In this context, mobile news reading has gradually become the main way for people to obtain news information due to its convenience, real-time and interactivity. In the face of the rapid growth of social information and the increasing demand for personalization, it is particularly important to design an efficient and accurate focus news APP.
This software development plan achieves a news APP with functions such as login/registration, news section navigation, news acquisition, news details, news comment viewing, discussion forum, and personalized personal information modification.
In the process of software development, Android Studio was selected as the core development platform. This decision was made based on its outstanding performance and its advantages as a feature-rich integrated development environment (IDE). At the same time, it relies on Java, an industry-recognized and popular programming language, to ensure the stability and excellent cross-platform compatibility of the developed APP.
In the local data persistence strategy for applications, the SQLite database management system is selected as the solution because of the comprehensive support for the SQL92 standard, excellent resource optimization, efficient data processing capabilities and stable running performance. SQLite Identity is a lightweight relational database management system.
In terms of user interface design, the program strictly follows the UI/UX design principles, adopts a concise and clear design style, removes redundant decorations, and enables users to quickly locate the desired function. At the same time, the program also pays attention to details, and further enhances the overall user experience through reasonable layout, clear navigation, and smooth animation effects.
Through the use of these technologies, this development will not only improve the practicality of the application, but also ensure that it visually conforms to modern aesthetics and user habits.
Key words: Focus News App System; Android Studio; Java; SQLite;Crawler
目 录
1 绪论
1.1 研究的目的及意义
随着移动互联网的迅猛发展和智能手机的普及,新闻APP能够为大众提供一个便捷、高效的新闻获取渠道[1]。在当今时代,科学技术的飞速发展已经深刻地重塑了人类获取知识信息的途径与手段。信息获取由传统的相对单一的途径,变得更多元化与便利化。这种变革促使了人们的信息传递速度和范围的显著提升。这一变化可以说是翻天覆地的。如今,无需翻阅厚重的报纸或等待电视的新闻播报,仅需简单的触击操作,便可随时随地获取最新新闻动态。此举不仅极大地促进了信息的流通效率,还显著增强了社会的透明度,为公众提供了更深入的视角,让他们能够更好地理解并积极参与社会生活。新闻APP是当今用户获取讯息的重要途径,设计、改进新闻APP对于提升用户的体验具有重要作用[2]。
本研究的目标是在Android平台上开发一款功能完善、用户体验卓越的新闻APP。旨在构建一个满足用户个性化与精确性需求的新闻系统。为了提升用户体验,本文的应用程序特别引入了用户自定义机制,这一机制旨在根据用户的兴趣和阅读偏好来选择新闻分类。这一创新策略不仅紧跟数字化时代信息个性化消费的趋势,为用户构建了一个既高效又多元化的新闻阅读环境。通过这种方式,本程序确保了新闻内容的分发既契合用户的即时需求,又维持了信息传播的广度与深度,进而促进了新闻消费模式的革新与用户体验的全面提升。这种差异化的新闻定制将有效帮助用户快速、便捷地获取自己真正关心的新闻内容,以提高他们快速定位目标信息的效率。通过该方式,本程序能够更好地满足用户需求,提升他们的使用体验。
本程序致力于提供用户所需的新闻内容,以提升用户的体验和满意度。无论是国际国内的重大时事,还是日常生活中的琐事,用户都可以在本程序中快速找到并获取到他们感兴趣的新闻内容。本程序的目标是为用户提供一个个性化定制的新闻体验,确保新闻获取与他们的个人兴趣和需求紧密契合。在本研究中致力于不断优化应用程序的核心功能,以期达到提升体验的终极目标,确保用户能够获得最佳的新闻阅读体验,并享受个性化服务带来的便利。
1.2 国内外研究现状
1.3 研究内容及章节安排
本文的研究主要围绕三个核心方面展开。首先,本文深入分析了国内外新闻APP的当前发展态势,以期为本系统开发提供宝贵的参考经验和启示。本次开发致力于新闻APP功能的全面开发与实现,力求为用户提供多元化、全方位的新闻资讯服务,以满足他们日益增长的信息需求。这一努力旨在推动新闻APP行业的持续进步与发展。本程序的目标是确保系统能够满足用户在现实应用场景中的需求,为他们提供高效、便捷和愉悦的使用体验,同时提升系统的用户满意度和市场竞争力。论文的章节安排如下:
第一章:本章旨在探讨新闻APP的发展背景,并对国内外在该领域的研究现状进行系统的梳理和分析。本章将全面剖析新闻APP的发展趋势与背景,借鉴国内外相关研究成果,以期为后续的研究工作提供有力支撑。
第二章:需求分析。在本研究中,本文系统性地审视了构建焦点新闻应用程序的实践可能性,通过综合经济性、技术可行性与操作效率三个层面的探讨,对该项目的实施进行了全面而细致的评估。此外,本章还对各功能模块进行了细致的解构和评估,以确保APP的全面性和实用性。
第三章:系统开发工具及技术介绍。本章着重介绍了在开发焦点新闻APP过程中所采用的关键开发工具以及相关的技术手段,确保项目能够高效、稳定地推进。
第四章:总体设计。本章详尽地阐述了焦点新闻APP的核心设计理念,重点介绍了数据库架构的精心设计以及系统整体布局的全面规划。该陈述的目的在于为后续的APP开发工作奠定可靠的基础,以确保在开发过程中严格遵循预期的功能需求和性能标准,进而建立出符合用户期望的新闻APP。通过对该领域的系统研究和分析,可以准确把握用户需求和市场趋势,并在开发过程中充分考虑到这些因素。在设计和开发阶段,本程序将注重确保所需功能的完整实现,并保证性能达到预期的标准。在严谨的开发流程下,本次开发致力于构建一款不仅符合用户期待,更具备市场竞争力的高质量新闻APP。
第五章:详细设计与系统实现。本章节将详细阐述焦点新闻APP在多个方面的具体设计细节,并深入探讨系统实现过程中所遇到的各种具体情境与展现形式。
第六章:系统测试。为了确保焦点新闻应用程序能够满足用户需求并实现预定的功能目标,计划开展一系列系统的用例测试。
结论:本章旨在全面总结焦点新闻APP的核心内容、实现的完善程度以及尚待改进之处,以呈现其全面的开发成果与未来发展方向。
2 需求分析
2.1 可行性分析
略
2.1.1 经济可行性
2.1.2 技术可行性
略
2.1.3 操作可行性
略
2.2 功能需求分析
略
2.2.1 登录/注册功能
焦点新闻APP的登录注册页面构成了用户首次与APP进行交互的起点,是用户开启其新闻阅读体验的重要门户。对于已注册用户而言,他们仅需在登录界面输入个人用户名与密码,即可便捷地进入APP,享受个性化的服务。通过提供方便的登录方式和个性化服务,为已注册用户创造了更加便捷和舒适的使用环境,从而提高了用户的使用频率和忠诚度。该页面不仅为已经注册的用户提供了方便的使用途径,同时也为尚未注册的新用户提供了快捷的注册方式。将注册过程简化,让用户可以轻松注册成为新用户。本程序提供清晰的步骤指引和易于理解的提示信息,以帮助用户顺利通过注册流程。通过为用户提供易于操作的指引,本程序的目标是让注册过程变得简便易行,减少用户的困惑和犹豫,提高用户的满意度和完成注册的效率。
登录注册功能用例图如图2.1所示。

图2.1 登录注册功能用例图
2.2.2 新闻详情功能
新闻详情功能是本APP的核心功能,为用户提供了一种直观且便捷的新闻浏览与获取方式。当用户在APP界面中识别出吸引其注意力的新闻标题并执行点击操作,APP后台随即响应迅速呈现出该新闻的全面详细内容。
新闻详情功能用例图如图2.2所示。

图2.2 新闻详情功能用例图
2.2.3 分区导航功能
在焦点新闻APP中,分区导航功能占据举足轻重的地位,它为用户提供了一个高效且便捷的导航机制,使用户能够在精心设计的多个新闻分区中迅速找到并定位他们希望深入阅读的内容。此功能在筛选庞大的新闻信息库时发挥着不可或缺的关键作用,极大地提高了信息筛选的效率和准确性。
分区导航功能用例图如图2.3所示。

图2.3 分区导航功能用例图
2.2.4 评论查看功能
在焦点新闻APP中,评论查看功能至关重要,为用户提供了洞察新闻内容深层含义的窗口。该功能的设计初衷是为用户提供一个快速便利的入口,从而使得他们能够快速访问同新闻内容联系紧密的评论区。通过此途径,用户可以接触到更丰富多元、维度广阔的观点与见解,大幅提升他们的新闻阅读体验。
通过的评论查看功能引入,用户不仅能阅读到新闻发布者提供的官方信息,还能进一步发现更多用户分享的真实反馈及独到见解。这一功能拓展了用户的视野,使他们能够获得更加全面、深入的新闻解读。这些评论可能包含对新闻事件的深度剖析、不同视角的解读,或是与新闻紧密相关的个人经历与反思。这种互动与讨论不仅丰富了新闻内容的展现形式,也使用户能够更全面地把握新闻事件的来龙去脉,形成更为全面和客观的认知。
评论查看功能用例图如图2.4所示。

图2.4 评论查看功能用例图
2.2.5 讨论区功能
在焦点新闻APP的架构中,讨论区模块占据着举足轻重的地位,成为社交互动中不可或缺的一环。本文致力于构建一个开放、包容的交流平台,旨在激发用户积极分享其独到见解和独特观点,以促进深入且富有价值的讨论。这种环境旨在培养用户的逻辑思维和批判性分析能力,同时促进用户间的深入沟通与互动。在这个平台上,用户可以自由表达个人的思考,与其他用户进行探讨。这种开放性的互动环境旨在积极促动用户的参与,激发多元观点的交流与碰撞,从而帮助用户更全面地理解和接纳多样化的意见,推动思想层面的深入交融与拓展。在这个平台上,用户不仅能够畅所欲言,还可以根据自己的意愿随时保留或删除自己的发言,确保了言论的自主性和灵活性。
讨论区功能用例图如图2.5所示。

图2.5 讨论区记录功能用例图
在焦点新闻APP中,用户主页功能占据着核心地位,为用户提供了一个整合管理个人信息的便捷界面。用户可以在该功能界面上轻松地进行个性化设置和调整,以满足其偏好和需求。具体来说,用户可以利用用户主页功能来更改自己的用户名,从而在APP中展现一个更具个人特色的身份标识,实现个性化的展示。
用户主页功能用例图如图2.6所示。

图2.6 用户主页功能用例图
3 系统开发工具及技术介绍
处理器:Intel Core i7;
内存:16GB;
操作系统:Microsoft Windows10;
数据库:SQLite。
3.2 开发工具
3.2.1 Visual Studio Code
略
3.2.2 Android studio
略
3.2.3 Navicat
略
3.3 开发技术
3.3.1 Node.js
略
3.3.2 Android开发
3.3.2 SQLite数据库
略
4 总体设计
4.1 概要设计
系统总共分为五大模块,分别为加载模块、登录注册模块、新闻模块、讨论模块、用户主页模块。
系统功能结构图如图4.1所示。

图4.1 APP功能结构图
焦点新闻APP的登录注册模块,是每位新用户与这款应用建立联系的第一步。在这个信息化、数字化的时代,一个简洁、直观的登录注册流程对于吸引和留住用户至关重要。
当用户首次打开焦点新闻APP时,他们首先会看到的是清晰明了的登录和注册选项。对于新用户而言,注册流程被设计得尽可能简单易懂,无需复杂的步骤或繁琐的验证。
4.1.2 新闻模块设计
在当前数字媒体时代,焦点新闻应用程序(APP)的核心功能正在于其特别的新闻模块设计。该模块通过提供多元化的新闻内容选择和深度互动体验,极大地满足了用户的个性化信息需求。在这一架构下,用户得以依据个人偏好,自由选择不同的新闻频道。
为了提升用户的导航流畅性,新闻模块配备了直观易用的导航栏,使用户能够轻松跳转至如用户主页、讨论区等其他功能模块,进而享受更加多元化、个性化的服务与应用体验。
4.1.3 讨论模块设计
焦点新闻APP中的讨论模块为用户提供了一个专门的交流平台,让他们可以尽情表达对新闻内容的观点,分享评论和独到见解。在此模块中,用户的思维得以自由驰骋,毫无束缚,充分展现个人的思想深度与广度。
在深化用户界面交互的优化工作中,本应用引入了一项创新导航功能,使用户能够在不同模块间流畅切换,无需复杂步骤,极大节省了用户的时间和精力。该导航栏的设计遵循简洁直观的准则,界面清晰,使用户能迅速定位并跳转到所需的功能模块。这一改进不仅显著优化了用户的使用体验,而且提升了服务效率与便捷性。如今,用户借助这一高效的导航工具,可以快速跳转至新闻模块、用户主页等多种功能板块,享受更为丰富的服务内容。这一设计显著提升了用户在应用中切换模块的灵活性和操作效率,为用户带来了更为流畅便捷的使用体验。
4.1.4 用户主页模块设计
焦点新闻APP的个人信息管理模块旨在为用户提供高度个性化的设置选项。用户可在此轻松修改头像和昵称,彰显其独特的个人风格和身份特征。此外,该功能还提供了方便的账户退出选项,以满足用户在不同使用情境下的需求。
4.1.5 加载模块设计
当用户打开这款APP时,首先将看到一个展示本APP logo的启动画面。这一页面并非仅作过渡之用,而是承载着塑造APP品牌形象和引导用户继续使用的双重使命。
除了标志之外,启动页面巧妙地融合了一个计时器元素。这种设计不仅使得用户体验的流畅性被保障,同时也体现了该应用对用户时间的考量。通过这种设计,用户不必花费过多的时间和精力来等待使用服务,而可以更加轻松地完成操作。当然,为了满足那些希望快速启动应用的用户需求,启动页面特别设计了一个醒目的“跳过”按钮。这一按键的设计考虑了用户的个性化需求,允许用户根据自己的喜好选择是否等待。
4.2 数据库设计
数据库设计基本原则[10]:
(1)尽量减少重复数据,从而提升访问数据库的速度。
(2)在设计数据表结构时,需要充分考虑到表的灵活性和适应性,即需要考虑到表的动态性从而应对未来可能出现的各种新需求和新场景。通过灵活的设计,可以确保表的结构可以随着业务的发展和变化而进行调整和扩展。这样的设计方法能够为系统提供更好的可扩展性和适应性,使得数据的管理更加高效和方便。因此,在设计数据表结构时,动态适应性是一个重要的考虑因素,值得深入思考和研究。
(3)在优化数据存储架构的过程中,关键策略之一是采用数据分区的策略,将不同主题或业务领域的数据分别存储在独立的数据表中。通过将不同主题的数据分别存储在不同的表中,可以更加灵活地进行数据访问和操作,从而提高系统的性能和稳定性
(4)遵循第三范式。这样做可以提高数据的整体质量和一致性。
(5)一对一关联模式。
4.2.1 E-R图
根据数据库原则和系统的需求分析,焦点新闻APP的E-R模型如图4.2所示。

图4.4 焦点新闻APP E-R模型图
4.2.2 数据库信息表
在焦点新闻APP的后台数据库中,为了高效地管理用户信息和讨论。其中,用户信息表通常用来存储用户的个人资料,如用户名、密码、头像、昵称等,而讨论信息表则用来存储用户发表的各种讨论内容,如评论内容、发表时间、发表人等。
当需要查询某个用户发表的所有讨论,或者需要知道某个讨论是由哪位用户发表的时,就需要将用户信息表和讨论信息表进行关联查询。构建了一个查询语句 query,使用了 SQL 的 JOIN 操作符,将两个表关联起来。在这里,使用了 SENDER 和ID字段进行关联。下面的为各个数据信息表的功能和数据结构描述:
Users信息表用于存放用户的各类信息。
表结构如表4.1所示。
表4.1 Users信息表
| 序号 | 字段名称 | 数据类型 | 长度 | 允许为空 | 描述 |
| 1 | Users_ID | INTEGER | N/A | 否 | ID(主键) |
| 2 | Users_USERNAME | TEXT | 20 | 否 | 用户名 |
| 3 | Users_PASSWORD | TEXT | 20 | 否 | 密码 |
| 4 | User_NickName | TEXT | N/A | 否 | 昵称 |
| 5 | User_avatar_path | TEXT | N/A | 否 | 头像路径 |
barrages信息表用于存放讨论的各类信息。
表结构如表4.2所示。
表4.2 barrages信息表
| 序号 | 字段名称 | 数据类型 | 长度 | 允许为空 | 描述 |
| 1 | barrages_id | INTEGER | N/A | 否 | ID(主键) |
| 2 | barrages_content | TEXT | N/A | 否 | 评论内容 |
| 3 | barrages_sender | TEXT | N/A | 否 | 发送者 |
| 4 | barrages_timestamp | LONG | N/A | 否 | 时间戳 |
5 详细设计与系统实现
5.1 登录注册模块的设计与实现
该页面以用户体验为核心,设计精心。在屏幕的中上部,用户可以清晰地看到一个独特的APP标志,作为该应用程序的视觉象征,给用户留下了深刻的印象。这个标志位于显眼的位置,旨在吸引用户的注意力,并提供独特而易于记忆的图形形象。紧接着,下方是直观的用户名和密码输入框,用户只需在此处填入个人账号信息,便能迅速完成登录流程。同时,为照顾尚未注册的新用户,页面底部特别设置了注册引导按钮,通过明确的图标和文字指引,帮助新用户迅速找到注册路径,完成账号的创建。该登录注册界面的布局设计简洁明了,操作流程简便流畅,使用户能够轻松上手,从而为他们提供了一段顺畅而友好的初始使用体验。
登录注册模块界面图如图5.1、5.2:


图5.2 登录注册模块注册界面图
当用户首次启动焦点新闻APP并尝试登录时,若尚未拥有账号,用户可以导航至注册页面。用户需要遵循系统的指引来设置这些凭证,并确保它们足够安全,以保护他们的个人信息免受未经授权的访问。
在用户完成注册流程后,其可重新进入登录界面进行身份验证。注册和登录流程旨在为用户提供便捷、流畅的使用体验。本程序始终将提供卓越的用户体验作为首要目标,因此本程序的流程设计旨在为用户带来极大的便利与舒适,同时竭力消除一切潜在的繁琐步骤和不必要的干扰,确保用户在使用过程中的顺畅与愉悦。
该界面流程图如图5.3:

图5.3 登录注册模块流程图
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
initView();
clear();
register();
home();
}
private void home() {
mLoginButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String username = user.getText().toString().trim();
String password = pass.getText().toString().trim();
if (username.isEmpty() || password.isEmpty()) {
Toast.makeText(getApplicationContext(), "请输入账号或密码", Toast.LENGTH_SHORT).show();
return;
}
boolean result = mDatabaseHelper.checkUser(username, password);
if (result) {
Toast.makeText(getApplicationContext(), "登陆成功", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);
} else {
Toast.makeText(getApplicationContext(), "账号或密码错误", Toast.LENGTH_SHORT).show();
}
}
});
}
private void register() {
loginRegister.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(LoginActivity.this, RegisterActivity.class);
startActivity(intent);
}
});
}
private void clear() {
imgUserDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
user.setText("");
}
});
imgPassDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
pass.setText("");
}
});
}
……
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "Users.db";
private static final String TABLE_NAME = "users_table";
private static final String COL_1 = "ID";
private static final String COL_2 = "USERNAME";
private static final String COL_3 = "PASSWORD";
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + TABLE_NAME + " (ID INTEGER PRIMARY KEY AUTOINCREMENT, USERNAME TEXT, PASSWORD TEXT)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
public boolean insertData(String username, String password) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(COL_2, username);
contentValues.put(COL_3, password);
long result = db.insert(TABLE_NAME, null, contentValues);
return result != -1;
}
public boolean checkUser(String username, String password) {
SQLiteDatabase db = this.getWritableDatabase();
String selection = "USERNAME" + " = ?" + " AND " + "PASSWORD" + " = ?";
String[] selectionArgs = { username, password };
Cursor cursor = db.query(TABLE_NAME, null, selection, selectionArgs, null, null, null);
int count = cursor.getCount();
cursor.close();
return count > 0;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register);
mUserNameEditText = findViewById(R.id.username_edittext);
mPasswordEditText = findViewById(R.id.password_edittext);
mRePasswordEditText = findViewById(R.id.repassword);
registerButton = findViewById(R.id.register_button);
loginTv = findViewById(R.id.tv_login);
mDatabaseHelper = new DatabaseHelper(this);
loginTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(RegisterActivity.this, LoginActivity.class);
startActivity(intent);
}
});
registerButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String username = mUserNameEditText.getText().toString().trim();
String password = mPasswordEditText.getText().toString().trim();
String repassword = mRePasswordEditText.getText().toString().trim();
if (username.isEmpty() || password.isEmpty()|| repassword.isEmpty()) {
Toast.makeText(getApplicationContext(), "请输入账号或密码或重复密码", Toast.LENGTH_SHORT).show();
return;
}
if (!password.equals(repassword)) {
Toast.makeText(getApplicationContext(), "两次密码不一致", Toast.LENGTH_SHORT).show();
return;
}
boolean result = mDatabaseHelper.insertData(username, password);
if (result) {
Toast.makeText(getApplicationContext(), "注册成功", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(RegisterActivity.this, LoginActivity.class);
startActivity(intent);
finish();
} else {
Toast.makeText(getApplicationContext(), "注册失败", Toast.LENGTH_SHORT).show();
}
}
});
5.2 新闻模块设计与实现
首先是供用户大致浏览并找寻自己感兴趣的新闻内容的新闻模块,主体部分为一个新闻列表。
新闻模块界面图如图5.4;

图5.4 新闻模块界面图
新闻模块引入了一个细致分类的新闻导航栏,该导航栏按新闻类型进行划分,如“新闻”、“军事”、“娱乐”、“运动”和“手机”等,用户可根据个人兴趣快速定位至感兴趣的新闻类别,有效减少了在海量信息中筛选的时间。
新闻分类导航界面图如图5.5;

图5.5 新闻分类导航界面图
新闻详情界面是用户详细了解自己在新闻列表中找寻到的感兴趣的新闻内容的界面。
当用户点击感兴趣的新闻标题时,即可进入新闻详情页面,进一步了解新闻的完整内容。
新闻详情界面图如图5.6;

图5.6 新闻详情界面图
在详情页的底部,还特别设置了评论区入口,方便用户查看并接收了解其他用户观点。
评论区界面图如图5.7;

图5.7 评论区界面图
更多评论界面图如图5.8:

图5.8 更多评论界面图
该界面流程图如图5.9:

图5.9 新闻模块流程图
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
tabLayout();
setFragment(0);
BottomNavigationView bottomNavigationView = findViewById(R.id.bottom_navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_discover:
startActivity(new Intent(MainActivity.this, MainActivity.class));
item.setChecked(true);
return true;
case R.id.menu_discuss:
startActivity(new Intent(MainActivity.this, BarrageActivity.class));
item.setChecked(true);
return true;
case R.id.menu_profile:
startActivity(new Intent(MainActivity.this, ProfileActivity.class));
item.setChecked(true);
return true;
}
return false;
}
});
}
private void tabLayout() {
String[] types = {"新闻", "军事", "娱乐", "运动", "手机"};
tabLayout.removeAllTabs();
tabLayout.setTabTextColors(getResources().getColor(R.color.black), getResources().getColor(R.color.red));
tabLayout.setSelectedTabIndicatorColor(Color.RED);
for (String type : types) {
TabLayout.Tab tab = tabLayout.newTab().setText(type);
tabLayout.addTab(tab);
}
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
int position = tab.getPosition();
setFragment(position);
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
private void setFragment(int id) {
Fragment fragment = null;
switch (id) {
case 0:
fragment = new HeadlinesFragment();
break;
case 1:
fragment = new NewsHighlightskFragment();
break;
case 2:
fragment = new EntertainmentNewsFragment();
break;
case 3:
fragment = new SportsNewsFragment();
break;
case 4:
fragment = new FilmsFragment();
break;
}
getSupportFragmentManager().beginTransaction().replace(R.id.fr, fragment).commit();
}
……
5.3 讨论模块设计与实现
屏幕的主体区域被用作评论展示区,为用户提供了清晰、直观的浏览体验,使所有讨论内容一目了然。在屏幕下方,设有专门的编辑区域,用户可以在此输入并编辑自己的评论,轻松将其发布至讨论区。
为了增强评论的辨识度和视觉吸引力,在每条评论的左侧都增加了用户头像,以此赋予评论更鲜明的个性化特色。同时,在评论的上方,系统清晰地标注了发布时间,使用户能够迅速把握讨论的动态和进展。
此外,为了进一步优化用户的参与体验,在每条评论的末尾精心设置了删除按钮。这一设计不仅增强了用户操作的便捷性,还有效促进了积极的互动和意见交流,从而大幅提升了整体的用户满意度。
讨论模块界面图如图5.10:

图5.10 讨论模块界面图
该界面流程图如图5.11:

图5.11 讨论模块流程图
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_barrage);
recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
dbHelper = new DatabaseHelper(this);
barragess = dbHelper.getAllUserBarrages();
barrageAdapter = new BarrageAdapter(this, barragess);
recyclerView.setAdapter(barrageAdapter);
barrageAdapter.setBarrageAdapterListener(this);
inputBarrage = findViewById(R.id.input_barrage);
BottomNavigationView bottomNavigationView = findViewById(R.id.bottom_navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
@SuppressLint("NonConstantResourceId")
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == R.id.menu_discover) {
startActivity(new Intent(BarrageActivity.this, MainActivity.class));
item.setChecked(true);
return true;
} else if (item.getItemId() == R.id.menu_discuss) {
startActivity(new Intent(BarrageActivity.this, BarrageActivity.class));
item.setChecked(true);
return true;
} else if (item.getItemId() == R.id.menu_profile) {
startActivity(new Intent(BarrageActivity.this, ProfileActivity.class));
item.setChecked(true);
return true;
} else {
return false;
}
}
});
findViewById(R.id.send_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String content = inputBarrage.getText().toString();
long timestamp = System.currentTimeMillis();
if (!content.isEmpty()) {
Barrage newBarrage = new Barrage(content, String.valueOf(id), timestamp);
dbHelper.addBarrage(newBarrage);
barragess = dbHelper.getAllUserBarrages();
barrageAdapter.Refresh(barragess);
inputBarrage.setText("");
Toast.makeText(BarrageActivity.this, "发送成功!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(BarrageActivity.this, "请输入内容!", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onBarrageDeleted(int position) {
Log.d("BarrageActivity", "尝试删除的弹幕位置:" + position);
Log.d("BarrageActivity", "当前弹幕列表大小:" + barragess.size());
if (position < 0 || position >= barragess.size()) {
Log.e("BarrageActivity", "位置越界错误:尝试删除的位置是 " + position + ",但列表大小为 " + barragess.size());
return;
}
UserBarrage deletedBarrage = barragess.remove(position);
if (deletedBarrage.getSender().equals(String.valueOf(id)) ){
dbHelper.deleteBarrage(deletedBarrage.getId());
barragess = dbHelper.getAllUserBarrages();
barrageAdapter.Refresh(barragess);
}else {
Toast.makeText(this, "别人的弹幕不能删除", Toast.LENGTH_SHORT).show();
}
//barrageAdapter.notifyItemRemoved(position);
}
5.4 个人界面模块设计与实现
界面顶部被专门划分出一个区域用以展示用户头像,允许用户根据个人偏好选择图像。
紧随用户头像之后的是昵称输入栏,它是用户在虚拟社区中的独特标识。同样地,昵称也支持用户自定义修改。这种简便的修改方式不仅提升了用户体验,也增加了用户在网络空间中的自由度。
在昵称输入框的下方,添加了一个“保存修改”按钮。用户在完成头像和昵称的更改后点击即可完成修改。若修改成功,系统将显示相应提示,确保用户知晓更改已生效。
最后,为了满足用户退出当前账户的需求,在界面底部设置了一个“退出登录”按钮。
个人模块界面图如图5.12:

图5.12 个人模块界面图
该界面流程图如图5.13:

图5.13 个人模块流程图
private static class UserManager {
private static User currentUser;
public static User getCurrentUser() {
return currentUser;
}
public static void setCurrentUser(User user) {
currentUser = user;
}
public static void logout() {
currentUser = null;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_profile);
profileImage = findViewById(R.id.profile_image);
TextView nicknameText = findViewById(R.id.nickname_text);
saveChangesButton = findViewById(R.id.save_changes_button);
logoutButton = findViewById(R.id.logout_button);
dbHelper = new DatabaseHelper(this);
userProfile = dbHelper.getUserProfileById(id);
User currentUser = UserManager.getCurrentUser();
if (userProfile==null||userProfile.getProfileImage()==null){
}else {
nicknameText.setText(userProfile.getNickname());
Bitmap ImageBitmaps = BitmapFactory.decodeByteArray(userProfile.getProfileImage(), 0, userProfile.getProfileImage().length);
profileImage.setImageBitmap(ImageBitmaps);
ImageBitmap = ImageBitmaps;
}
if (currentUser != null) {
nicknameEdit.setText(currentUser.getNickname());
setProfileImage(currentUser);
}
BottomNavigationView bottomNavigationView = findViewById(R.id.bottom_navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == R.id.menu_discover) {
startActivity(new Intent(ProfileActivity.this, MainActivity.class));
item.setChecked(true);
return true;
} else if (item.getItemId() == R.id.menu_discuss) {
startActivity(new Intent(ProfileActivity.this, BarrageActivity.class));
item.setChecked(true);
return true;
} else if (item.getItemId() == R.id.menu_profile) {
startActivity(new Intent(ProfileActivity.this, ProfileActivity.class));
item.setChecked(true);
return true;
} else {
return false;
}
}
});
profileImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
selectImage();
}
});
nicknameText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(ProfileActivity.this);
builder.setTitle("输入昵称");
final EditText inputEditText = new EditText(ProfileActivity.this);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT);
inputEditText.setLayoutParams(lp);
builder.setView(inputEditText);
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String nickname = inputEditText.getText().toString();
nicknameText.setText(nickname);
}
});
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.show();
}
});
saveChangesButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (ImageBitmap != null) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
ImageBitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] imageData = stream.toByteArray();
dbHelper.updateNicknameAndProfileImage(id,nicknameText.getText().toString() , imageData);
Toast.makeText(ProfileActivity.this, "保存成功", Toast.LENGTH_SHORT).show();
}
}
});
logoutButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
UserManager.logout();
Intent intent = new Intent(ProfileActivity.this, LoginActivity.class);
startActivity(intent);
finish();
}
});
}
private void selectImage() {
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, PICK_IMAGE_REQUEST);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK && data != null && data.getData() != null) {
Uri imageUri = data.getData();
try {
ImageBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageUri);
profileImage.setImageBitmap(ImageBitmap);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void setProfileImage(User user) {
Glide.with(this)
.load(user.getProfileImage())
.into(profileImage);
}
。
private static class User {
private String id;
private String nickname;
private String profileImage;
public User(String id, String nickname, String profileImage) {
this.id = id;
this.nickname = nickname;
this.profileImage = profileImage;
}
public String getId() {
return id;
}
public String getNickname() {
return nickname;
}
public String getProfileImage() {
return profileImage;
}
public void setProfileImage(String profileImage) {
this.profileImage = profileImage;
}
}
}
5.5 加载模块设计与实现
在加载页面通过突出展示Logo,能够有效地吸引用户的注意力,并让他们快速识别和认知品牌。这个设计决策旨在提高用户对应用的辨识度和记忆度,并为他们创造一个独特而令人难忘的视觉体验,底部有一个动态倒计时。进一步在界面左上角设置了一个醒目的“跳过”按钮,为用户提供了两种选择:他们既可以选择等待倒计时自然结束,也可以选择立即点击该按钮,从而迅速进入应用。
加载界面图如图5.14:

图5.14 加载模块界面图
该界面结构图如图5.15:

图5.15 加载模块结构图
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
countdownText = findViewById(R.id.countdown_text);
skipButton = findViewById(R.id.skip_button);
handler.postDelayed(runnable, 5000);
new CountDownTimer(6000, 1000) {
public void onTick(long millisUntilFinished) {
countdownText.setText("倒计时" + millisUntilFinished / 1000);
}
public void onFinish() {
handler.removeCallbacks(runnable);
countdownText.setVisibility(View.GONE);
skipButton.setVisibility(View.VISIBLE);
}
}.start();
skipButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
toMainActivity();
}
});
}
private void toMainActivity() {
startActivity(new Intent(this, LoginActivity.class));
finish();
}
}
6 系统测试
在系统测试阶段,本文专注于两个核心板块进行深入且细致的测试工作,以确保系统的整体性能和用户体验达到最优。本文采用黑盒测试,首先着重进行功能模块的测试,这是确保系统稳定性和可靠性的核心环节,对于整个系统的顺畅运行具有至关重要的作用。在当前的阶段,针对系统的所有功能模块开展了一系列全面而详尽的测试工作。这些测试的主要目的是验证每个模块是否能够准确无误地执行预设功能,并在接收到用户请求时,快速、精准地作出响应。通过这一系列的测试,旨在确保系统的稳定性和可靠性,为用户提供更加优质的服务体验。细致地检查每个功能模块的性能表现,以确保它们能够完全满足任务书所规定的各项功能和性能指标[11]。
6.2 测试用例
登陆注册模块测试用例如表6.1所示。
表6.1 登录注册模块测试用例
| 测试行为 | 预测结果 | 实际结果 |
| 点击注册按钮 | 跳转注册界面 | 与预测结果相同 |
| 不按照要求进行注册 | 弹出错误提示 | 与预测结果相同 |
| 按照要求进行注册 | 注册成功 | 与预测结果相同 |
| 在登陆界面输入错误的用户名或密码 | 弹出错误提示 | 与预测结果相同 |
| 在登录界面输入正确的用户名密码 | 弹出登陆成功提示并进入APP新闻界面 | 与预测结果相同 |
新闻模块测试用例如表6.2所示。
表6.2 新闻模块测试用例
| 测试行为 | 预测结果 | 实际结果 |
| 点击分区导航栏 | 切换不同的分区 | 与预测结果相同 |
| 滑动新闻表 | 上下切换 | 与预测结果相同 |
表6.2 新闻模块测试用例(续表)
| 测试行为 | 预测结果 | 实际结果 |
| 点击新闻标题 | 显示新闻详情 | 与预测结果相同 |
| 点击评论区按钮 | 跳转到评论部分 | 与预测结果相同 |
| 点击查看更多评论 | 进入完整的评论页面 | 与预测结果相同 |
| 点击底部导航栏 | 切换到各界面 | 与预测结果相同 |
讨论模块测试用例如表6.3所示。
表6.3 讨论模块测试用例
| 测试行为 | 预测结果 | 实际结果 |
| 点击输入框 | 弹出编辑框 | 与预测结果相同 |
| 未输入点击上传按钮 | 弹出不能为空提示 | 与预测结果相同 |
| 输入完毕点击上传按钮 | 提示上传成功并显示在列表 | 与预测结果相同 |
| 点击自己发布的删除按钮 | 选取的评论在列表中消失 | 与预测结果相同 |
| 点击他人发布的删除按钮 | 弹出禁止操作提示 | 与预测结果相同 |
| 点击推荐按钮 | 推荐按钮常亮 | 与预测结果相同 |
| 点击底部导航栏 | 切换到各界面 | 与预测结果相同 |
个人模块测试用例如表6.4所示。
表6.4 个人模块测试用例
| 测试行为 | 预测结果 | 实际结果 |
| 点击头像 | 弹出图片选择器选择图片更换头像 | 与预测结果相同 |
| 点击昵称输入框 | 跳出编辑框 | 与预测结果相同 |
| 点击保存更改 | 弹出修改成功 | 与预测结果相同 |
| 点击退出登录 | 返回到登录注册界面 | 与预测结果相同 |
| 点击底部导航栏 | 切换到各界面 | 与预测结果相同 |
加载模块测试用例如表6.5所示。
表6.5 加载模块测试用例
| 测试行为 | 预测结果 | 实际结果 |
| 等待倒计时结束 | 进入APP登录页面 | 与预测结果相同 |
| 点击跳过 | 进入APP登录页面 | 与预测结果相同 |
7 演示视频
基于Android的焦点新闻APP
3629

被折叠的 条评论
为什么被折叠?



