基于Android平台开发,仿头条新闻app

相关视频教程在某站上面(🔍浩宇软件开发)

1. 项目模块功能思维导图

在这里插入图片描述

2. 项目涉及到的技术点

  1. 数据来源:聚合数据API
  2. 使用okhttp网络请求框架获取api数据
  3. 使用gson库解析json数据
  4. 使用RecyclerView+adapter实现新闻列表
  5. 使用SQLite数据库实现用户登录,注册,浏览历史记录
  6. 使用SharedPreferences 实现记住密码登录
  7. 使用TabLayout+ViewPager2实现新闻分类滑动
  8. 使用DrawerLayout+NavigationView实现抽屉
  9. 使用WebView实现新闻详情数据加载
  10. 使用Glide实现新闻图片加载

3. 项目截图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

3. 部分代码实现

  1. 欢迎页实现
public class WelcomeActivity extends AppCompatActivity {
    private TextView tvCountdown;

    private CountDownTimer countDownTimer;
    private long timeLeftInMillis = 3000; // 设置倒计时时长,单位为毫秒

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_welcome);
        //初始化控件
        tvCountdown =findViewById(R.id.tv_countdown);

        // 启动倒计时
        startCountdown();
    }

    private void startCountdown() {
        countDownTimer =new CountDownTimer(timeLeftInMillis,1000) {
            @Override
            public void onTick(long millisUntilFinished) {
                timeLeftInMillis = millisUntilFinished;
                int secondsRemaining = (int) (millisUntilFinished / 1000);
                tvCountdown.setText(secondsRemaining +" s | 跳转");
            }

            @Override
            public void onFinish() {
                //跳转到登录页面(看自己逻辑想跳转哪个页面)
                startActivity(new Intent(WelcomeActivity.this, MainActivity.class));
                // 倒计时结束后的操作,例如跳转到主页面
                finish();

            }
        }.start();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (countDownTimer != null) {
            countDownTimer.cancel();
        }
    }
}
  1. 首页实现
public class MainActivity extends AppCompatActivity {

    //    private String[] titles = {"娱乐", "军事", "教育", "文化", "将康", "财经", "体育", "汽车", "科技"};
    private List<TitleInfo> titles = new ArrayList<>();
    private TabLayout tab_layout;
    private ViewPager2 viewPager;

    private NavigationView nav_view;

    private TextView tv_username;
    private TextView tv_nickname;

    private ImageView btn_open_drawerLayout;
    private DrawerLayout drawer_layout;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //初始化title数据
        titles.add(new TitleInfo("推荐", "top"));
        titles.add(new TitleInfo("国内", "guonei"));
        titles.add(new TitleInfo("国际", "guoji"));
        titles.add(new TitleInfo("娱乐", "yule"));
        titles.add(new TitleInfo("体育", "tiyu"));
        titles.add(new TitleInfo("军事", "junshi"));
        titles.add(new TitleInfo("科技", "keji"));
        titles.add(new TitleInfo("财经", "caijing"));
        titles.add(new TitleInfo("游戏", "youxi"));
        titles.add(new TitleInfo("汽车", "qiche"));
        titles.add(new TitleInfo("健康", "jiankang"));


        //初始化控件
        tab_layout = findViewById(R.id.tab_layout);
        viewPager = findViewById(R.id.viewPager);
        nav_view = findViewById(R.id.nav_view);
        btn_open_drawerLayout = findViewById(R.id.btn_open_drawerLayout);
        drawer_layout = findViewById(R.id.drawer_layout);
        //注意事项, 不能直接findViewById
        //        tv_username=findViewById(R.id.nav_view);
        //        tv_username = nav_view.getHeaderView(0).findViewById(R.id.tv_username);
        tv_username = nav_view.getHeaderView(0).findViewById(R.id.tv_username);
        tv_nickname = nav_view.getHeaderView(0).findViewById(R.id.tv_nickname);



        //btn_open_drawerLayout打开抽屉
        btn_open_drawerLayout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                drawer_layout.open();
            }
        });


        //nav_view点击事件
        nav_view.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {

                if (item.getItemId() == R.id.nav_history) {
                    //跳转到历史记录
                    Intent intent = new Intent(MainActivity.this, HistoryListActivity.class);
                    startActivity(intent);
                } else if (item.getItemId() == R.id.nav_update_pwd) {

                    //判断是否登录
                    UserInfo userInfo = UserInfo.getUserInfo();
                    if (null != userInfo) {
                        startActivity(new Intent(MainActivity.this, UpdatePwdActivity.class));
                    } else {
                        Toast.makeText(MainActivity.this, "请先登录~~", Toast.LENGTH_SHORT).show();
                    }

                } else if (item.getItemId() == R.id.nav_about) {
                    startActivity(new Intent(MainActivity.this, AboutActivity.class));


                } else if (item.getItemId() == R.id.nav_exit) {
                    UserInfo userInfo = UserInfo.getUserInfo();
                    if (null!=userInfo){
                        new AlertDialog.Builder(MainActivity.this).setTitle("温馨提示").setMessage("确认要退出登录吗?").setPositiveButton("确认", new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialog, int which) {

                                        startActivity(new Intent(MainActivity.this, LoginActivity.class));
                                        UserInfo.setUserInfo(null);
                                    }
                                }).setNegativeButton("取消", new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialog, int which) {

                                    }
                                })
                                .show();
                    }else {
                        Toast.makeText(MainActivity.this, "请先登录~~", Toast.LENGTH_SHORT).show();
                    }

                }

                return true;
            }
        });


        //viewPager 需要设置一个adapter
        viewPager.setAdapter(new FragmentStateAdapter(this) {
            @NonNull
            @Override
            public Fragment createFragment(int position) {
                String title = titles.get(position).getPy_title();
                TabNewsFragment tabNewsFragment = TabNewsFragment.newInstance(title);
                return tabNewsFragment;
            }

            @Override
            public int getItemCount() {
                return titles.size();
            }
        });

        //tab_layout点击事件
        tab_layout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                //设置viewPager选中当前页
                viewPager.setCurrentItem(tab.getPosition(), false);

            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {

            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {

            }
        });


        //tab_layout和viewPager关联在一起
        TabLayoutMediator tabLayoutMediator = new TabLayoutMediator(tab_layout, viewPager, new TabLayoutMediator.TabConfigurationStrategy() {
            @Override
            public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) {
                tab.setText(titles.get(position).getTitle());
            }
        });


        //这句话一定不能少
        tabLayoutMediator.attach();

    }

    @Override
    protected void onResume() {
        super.onResume();

        UserInfo userInfo = UserInfo.getUserInfo();
        if (null != userInfo) {
            tv_username.setText(userInfo.getUsername());
            tv_nickname.setText(userInfo.getNickname());
        } else {
            tv_username.setText("请登录");
            tv_nickname.setText("");
            //登录点击事件
            tv_username.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent intent = new Intent(MainActivity.this, LoginActivity.class);
                    startActivity(intent);
                }
            });
        }


    }
}
  1. 新闻详情页
public class NewsDetailsActivity extends AppCompatActivity {
    private NewsInfo.ResultDTO.DataDTO dataDTO;
    private Toolbar toolbar;
    private WebView mWebView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_news_details);

        //初始化控件
        toolbar = findViewById(R.id.toolbar);
        mWebView = findViewById(R.id.webView);

        //获取传递的数据
        dataDTO = (NewsInfo.ResultDTO.DataDTO) getIntent().getSerializableExtra("dataDTO");

        //设置数据
        if (null != dataDTO) {
            toolbar.setTitle(dataDTO.getTitle());
            mWebView.loadUrl(dataDTO.getUrl());

            //添加历史记录
            String dataDTOJson = new Gson().toJson(dataDTO);
            UserInfo userInfo = UserInfo.getUserInfo();
            if (userInfo!=null){
                HistoryDbHelper.getInstance(NewsDetailsActivity.this).addHistory(userInfo.getUsername(),dataDTO.getUniquekey(),dataDTOJson);
            }else {
                HistoryDbHelper.getInstance(NewsDetailsActivity.this).addHistory(null,dataDTO.getUniquekey(),dataDTOJson);
            }

        }


        //返回
        toolbar.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });






    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【thingsbord源码编译】 显示node内存不足

编译thingsbord显示报错 FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory问题原因分析 重新安装java版本 编译通过

小程序多次扫描获取sence失败------ivx

扫码图片被告知侵权了&#xff0c;删除了&#xff0c;如果有需要的同学可以自己尝试。或者直接联系我。 在微信小程序里面有一个函数 wx.getEnterOptionsSync() 功能描述 获取本次小程序启动时的参数。如果当前是冷启动&#xff0c;则返回值与 App.onLaunch 的回调参数一致&am…

前端最全面试题【最新版本2024-7月】

文章目录 最常见问题javascript篇Javascript的运行机制javascript的数据类型怎样判断变量的类型数据类型转换闭包的优缺点v-if和v-for哪个优先级更高&#xff1f; 如果两个同时出现&#xff0c;应该怎么优化得到更好的性能&#xff1f;HTML5的新特性和CSS3的新特性div 上下居中…

SpringBoot注解--11--@JSONField @JsonProperty

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一个问题&#xff1a;后端实体类isXXX开头的属性&#xff0c;传到前端后自动去掉is解决方法&#xff1a; JsonProperty和JSONField1.简介2.注解的区别2.1 底层框架不…

政安晨【零基础玩转各类开源AI项目】基于Ubuntu系统部署ComfyUI:功能最强大、模块化程度最高的Stable Diffusion图形用户界面和后台

目录 ComfyUI的特性介绍 开始安装 做点准备工作 在Conda虚拟环境中进行 依赖项的安装 运行 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: 零基础玩转各类开源AI项目 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&…

Rust vs Go: 特点与应用场景分析

目录 介绍Rust的特点Go的特点Rust的应用场景Go的应用场景总结 介绍 Rust和Go&#xff08;Golang&#xff09;是现代编程语言中两个非常流行的选择。凭借各自的独特优势和广泛的应用场景&#xff0c;吸引了大量开发者的关注。本文将详细介绍Rust和Go的特点&#xff0c;并探讨它…

【理解串】

目录 一、串的基本概念二、串的基本操作及实现三、串的存储实现3.1、静态数组实现3.2、动态数组实现 四、串的朴素模式匹配4.1、算法思想4.2、代码实现 五、KMP算法5.1、算法思想5.2、求模式串的next数组5.2、代码实现 一、串的基本概念 串&#xff1a;即字符串&#xff08;st…

一行命令快速导出、导入Python的依赖环境(Python)

文章目录 一、pip1、导出2、导入 二、Conda&#xff08;简&#xff09;1、导出1、导入 一、pip 1、导出 在Pycharm的Terminal窗口输入如下命令&#xff0c;即可将环境导出至文件requirements.txt。 pip freeze > C:\Users\sdl\Deskto\requirements.txt也可以在DOS界面执行…

Python 核心编程

Python 核心编程 1. 数据类型1.1 整型 int1.2 浮点数 float1.3 布尔类型 bool1.4 字符串 str1.5 列表 list1.6 元组 tuple1.7 集合 set1.8 字典 dict 2. 逻辑结构、文件操作2.1 分支结构和三元表达2.2 循环和遍历2.3 目录和路径2.4 文件操作 3. 函数、类、异常处理3.1 函数3.2 …

[Flask笔记]一个完整的Flask程序

前面讲过Flask是一个轻量级Web开发框架&#xff0c;为什么说是轻量级的呢&#xff0c;因为它用短短几行代码就能运行起来&#xff0c;我们一起来看看最简单的flask框架。 安装Flask 在看Flask框架之前我们需要先安装flask模块&#xff0c;学过python的肯定都知道&#xff0c;…

2.5 计算机网络

声明&#xff1a;文章参考的《系统架构设计师教程&#xff08;第二版&#xff09;》&#xff0c;如有侵权&#xff0c;本人将立即修改和删除。 利用通信线路将地理上分散的、具有独立功能的计算机系统和通信设备按不同的形式连接起来&#xff0c;并依靠网络软件以及通信协议实现…

《昇思25天学习打卡营第18天|onereal》

RNN实现情感分类 概述 情感分类是自然语言处理中的经典任务&#xff0c;是典型的分类问题。本节使用MindSpore实现一个基于RNN网络的情感分类模型&#xff0c;实现如下的效果&#xff1a; 输入: This film is terrible 正确标签: Negative 预测标签: Negative输入: This film…

Android数据库基础

目录 1、安卓数据存储方式 2、数据库事务 数据库事务的特性(ACID) 事务的隔离级别 事务总结 3、ContetProvider 作用 ​编辑 统一资源标识符URI ​编辑 MIME类型 ContentProvider主要方法 4、ContentResolver 作用 主要方法 使用案例 辅助工具类 ContentUris Uri…

matlab 有倾斜的椭圆函数图像绘制

matlab 有倾斜的椭圆函数图像绘制 有倾斜的椭圆函数图像绘制xy交叉项引入斜线负向斜线成分正向斜线成分 x^2 y^2 xy 1 &#xff08;负向&#xff09;绘制结果 x^2 y^2 - xy 1 &#xff08;正向&#xff09;绘制结果 有倾斜的椭圆函数图像绘制 为了确定椭圆的长轴和短轴的…

torchplus

https://gitee.com/hj_research/torchplus 一、安装 pip install tplus

Linux磁盘-创建分区

作者介绍&#xff1a;简历上没有一个精通的运维工程师。希望大家多多关注作者&#xff0c;下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 Linux磁盘涉及到的命令不是很多&#xff0c;但是在实际运维中的作用却很大&#xff0c;因为Linux系统及业务都会承载到硬盘…

评估指标:精确率(Precision)、召回率(Recall)、F1分数(F1 Score)

评估指标&#xff1a;精确率&#xff08;Precision&#xff09;、召回率&#xff08;Recall&#xff09;、F1分数&#xff08;F1 Score&#xff09; 前言相关介绍1. 准确率&#xff08;Accuracy&#xff09;2. 精确率&#xff08;Precision&#xff09;3. 召回率&#xff08;Re…

君子签电子合同推动企业人事管理变革,降本提效

在日益复杂的人力资源管理领域&#xff0c;合同签署与管理成为HR面临的一大挑战。面对庞大的合同量、繁琐的审批流程、频繁的岗位变动以及离职时的合同管理难题&#xff0c;传统方式已难以满足高效、安全、合规的需求。 君子签针对HR面临的挑战和需求&#xff0c;打造智能合同…

Alertmanager告警配置

1、告警概述及说明 告警能力在Prometheus的架构中被划分成两个独立的部分。 通过在Prometheus中定义AlertRule(告警规则)&#xff0c;Prometheus会周期性的对告警规则进行计算&#xff0c;如果满足告警触发条件就会向Alertmanager发送告警信息。 当Alertmanager接收到 Promethe…

java项目如何配置不同环境变量 以及 原理

如何配置不同的profile 首先&#xff0c;一个java项目&#xff0c;需要有不同的环境配置&#xff0c;打包时&#xff0c;自动使用对应的配置。那么&#xff0c;如何实现呢&#xff1f; 在你的Spring Boot项目的src/main/resources目录下创建或添加一个application.yml文件。这…