Androidstudio开发,天气预报APP

1.项目功能思维导图

在这里插入图片描述

2. 项目涉及到的技术点

  1. 数据来源:和风天气API
  2. 使用okhttp网络请求框架获取api数据
  3. 使用gson库解析json数据
  4. 使用RecyclerView+adapter实现未来7天列表展示和天气指数
  5. 使用PopupMenu 实现弹出选项框
  6. 使用动画+定时器实现欢迎页倒计时和logo动画
  7. 使用TextToSpeech 实现语音播报

3.项目截图

请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

4.部分代码功能实现

  1. 欢迎页实现
public class WelcomeActivity extends AppCompatActivity {

    private TextView tvCountdown;
    private CardView card_logo;
    private CountDownTimer countDownTimer;
    private long timeLeftInMillis = 1000; // 设置倒计时时长,单位为毫秒



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

        //初始化控件
        tvCountdown = findViewById(R.id.tv_countdown);
        card_logo = findViewById(R.id.card_logo);
        // 启动倒计时
        startCountdown();

        //实现logo缩放动画
        startAnim();

    }

    private void startAnim() {
        ViewCompat.animate(card_logo)
                .scaleX(1.0f)
                .scaleY(1.0f)
                .setDuration(1000)
                .setListener(new ViewPropertyAnimatorListener() {
                    @Override
                    public void onAnimationStart(View view) {

                    }

                    @Override
                    public void onAnimationEnd(View view) {

                    }

                    @Override
                    public void onAnimationCancel(View view) {

                    }
                })
                .start();
    }


    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 IndicesActivity extends AppCompatActivity {
    private String city_id;

    private RecyclerView recyclerView;
    private IndicesListAdapter mIndicesListAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_indices);
        //获取跳转传值
        city_id = getIntent().getStringExtra("city_id");
        //获取生活指数
        getWeatherIndices(city_id);
        //初始化控件
        initViews();
        //初始化适配器
        mIndicesListAdapter = new IndicesListAdapter();
        //设置适配器
        recyclerView.setAdapter(mIndicesListAdapter);
        //设置监听器
        setListener();
    }


    /**
     * 初始化控件
     */
    private void initViews() {
        recyclerView = findViewById(R.id.recyclerView);

    }

    /**
     * 设置监听器
     */
    private void setListener() {

        findViewById(R.id.toolbar).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });
    }


    /**
     * 获取生活指数
     */
    private void getWeatherIndices(String city_id) {
        OkGo.<String>get("https://devapi.qweather.com/v7/indices/1d")
                .params("location", city_id)
                .params("key", ApiConstants.APP_KEY)
                .params("type", "0")
                .execute(new StringCallback() {
                    @Override
                    public void onStart(Request<String, ? extends Request> request) {
                        super.onStart(request);
                        ProgressDialogUtils.showProgressDialog(IndicesActivity.this);
                    }

                    @Override
                    public void onSuccess(Response<String> response) {
                        IndicesInfo indicesInfo = new Gson().fromJson(response.body(), IndicesInfo.class);
                        if (null != indicesInfo && indicesInfo.getCode().equals("200")) {
                            mIndicesListAdapter.setIndicesInfoList(indicesInfo.getDaily());
                        }
                    }

                    @Override
                    public void onFinish() {
                        super.onFinish();
                        ProgressDialogUtils.hideProgressDialog();
                    }
                });
    }
}
  1. 城市搜索
public class SearchActivity extends AppCompatActivity {
    private EditText et_search_city;
    private RecyclerView recyclerView;
    private LinearLayoutCompat ll_empty;

    private SearchListAdapter mSearchListAdapter;

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

        // 1. 初始化控件
        initViews();
        //创建适配器
        mSearchListAdapter = new SearchListAdapter();
        //设置适配器
        recyclerView.setAdapter(mSearchListAdapter);
        // 2. 点击事件
        setListener();


    }

    /**
     * 初始化控件
     */
    private void initViews() {
        et_search_city = findViewById(R.id.et_search_city);
        recyclerView = findViewById(R.id.recyclerView);
        ll_empty = findViewById(R.id.ll_empty);
    }

    /**
     * 点击事件
     */
    private void setListener() {
        findViewById(R.id.btn_search).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 1. 获取输入框的值
                String cityName = et_search_city.getText().toString().trim();
                // 2. 判断是否为空
                if (cityName.isEmpty()) {
                    // 提示用户
                    Toast.makeText(SearchActivity.this, "城市名不能为空", Toast.LENGTH_SHORT).show();
                } else {
                    searchCity(cityName);
                }
            }
        });


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


        //recyclerView点击事件
        mSearchListAdapter.setOnItemClickListener(new SearchListAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(CityLocationInfo.LocationDTO locationDTO) {
                // 1. 获取城市名
                String cityName = locationDTO.getName();
                Intent intent = new Intent();
                intent.putExtra("cityName", cityName);
                intent.putExtra("id", locationDTO.getId());
                //设置跳转回传的值
                setResult(1000, intent);
                // 3. 关闭当前界面
                finish();
            }
        });
    }


    /**
     * 城市搜索
     */
    private void searchCity(String cityName) {

        OkGo.<String>get("https://geoapi.qweather.com/v2/city/lookup").params("location", cityName).params("key", ApiConstants.APP_KEY).execute(new StringCallback() {
            @Override
            public void onStart(Request<String, ? extends Request> request) {
                super.onStart(request);
                ProgressDialogUtils.showProgressDialog(SearchActivity.this);
            }

            @Override
            public void onSuccess(Response<String> response) {
                CityLocationInfo cityLocationInfo = new Gson().fromJson(response.body(), CityLocationInfo.class);
                if (null != cityLocationInfo && cityLocationInfo.getCode().equals("200")) {
                    if (null != mSearchListAdapter) {
                        mSearchListAdapter.setCityLocationInfoList(cityLocationInfo.getLocation());
                    }
                    //判断是否显示空布局
                    if (mSearchListAdapter.getItemCount() > 0) {
                        ll_empty.setVisibility(View.GONE);
                    } else {
                        ll_empty.setVisibility(View.VISIBLE);
                    }
                } else {
                    Toast.makeText(SearchActivity.this, "未查询到该城市", Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onError(Response<String> response) {
                super.onError(response);
            }

            @Override
            public void onFinish() {
                super.onFinish();
                ProgressDialogUtils.hideProgressDialog();
            }
        });


    }
}

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

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

相关文章

《梦醒蝶飞:释放Excel函数与公式的力量》10.2 COMPLEX函数

第二节 10.2 COMPLEX函数 10.2.1函数简介 COMPLEX函数是Excel中的一个工程函数&#xff0c;用于将实部和虚部组合成一个复数。复数广泛应用于工程、电气、物理等领域&#xff0c;COMPLEX函数提供了方便的复数表示和计算方法。 10.2.2语法&#xff1a; COMPLEX(real_num, i_…

SpringSecurity-SpirngBoot-方法级授权(SpringSecurity6.3新特性)(四)

SpringSecurity-SpirngBoot-方法级授权&#xff08;SpringSecurity6.3新特性&#xff09;&#xff08;四&#xff09; 本章使用SpringSecurity6.3新特性实现数据级别的鉴权&#xff0c;主要的目的是实现不同权限的用户查询同一个方法&#xff0c;限制一些内容只能拥有特定权限…

自定义@AnonymousAccess注解

一.目的&#xff1a; 自定义AnonymousAccess注解&#xff0c;可以直接在controller上添加该注解使请求绕过权限验证进行匿名访问&#xff0c;便于快速调用调试以及部分不需要进行安全验证的接口。而不是每次都需要去SecurityConfig文件中进行修改。 二.流程&#xff1a; 三.实…

阿里MotionShop——AI视频工具:一键替换视频人物为3D虚拟角色~

近期AI相关的新奇应用层出不穷&#xff0c;今天小元老师要给大家安利一个由阿里巴巴研发的AI视频生成技术——MotionShop&#xff01; 1、一键替换3D虚拟角色 MotionShop通过视频处理、角色检测、背景修复等多重步骤&#xff0c;能够将视频中的人物角色&#xff0c;一键转换成…

Pytorch(笔记7损失函数类型)

前言 损失函数&#xff08;Loss Function&#xff09;&#xff1a;是定义在单个样本上的&#xff0c;是指一个样本的误差&#xff0c;度量模型一次预测的好坏。 代价函数&#xff08;Cost Function&#xff09;成本函数经验风险&#xff1a;是定义在整个训练集上的&#xff0c…

文件防止拷贝如何实现?这些攻略给你了

在信息爆炸的时代&#xff0c;数据安全成为企业和个人不可忽视的重要环节。文件的非法拷贝不仅可能侵犯知识产权&#xff0c;还可能导致敏感信息的泄露&#xff0c;进而引发严重的后果。 因此&#xff0c;了解并掌握文件防止拷贝的方法和技术至关重要。本文将详细介绍几种常见…

压测jmeter 插件 之 tps和响应时间图

1. 背景 进行压测ing 2. 需要插件 TPS 和 响应时间 3. 插件 在 选项-最下面-plugins Manager 在 Available Plugins 中 搜索 &#xff1a;jpgc - Standard Set 重启安装就好啦

12--RabbitMQ消息队列

前言&#xff1a;前面一章内容太多&#xff0c;写了kafka&#xff0c;这里就写一下同类产品rabbitmq&#xff0c;rabbitmq内容较少&#xff0c;正好用来过度一下&#xff0c;概念还是会用一些例子来说明&#xff0c;实际部署的内容会放在概念之后。 1、基础概念 1.1、MQ消息队…

年化15.73%:创业板指数布林带突破Backtrader策略(代码+数据)

原创文章第582篇&#xff0c;专注“AI量化投资、世界运行的规律、个人成长与财富自由"。 昨天咱们使用backtrader&#xff0c;重写了创业板动量趋势择时&#xff1a;年化19.2%&#xff1a;backtraderquantstats实现创业板动量择时(代码数据) 今天咱们换一个通道指标&#…

计算机视觉研究方向初学习,计算机视觉都有什么方向??!到底是干什么的?!

计算机视觉研究方向初学习&#xff0c;计算机视觉都有什么方向&#xff1f;&#xff1f;&#xff01;到底是干什么的&#xff1f;&#xff01; 语义分割图像分类目标检测和定位实例分割、全景分割物体跟踪姿态估计人脸识别人体识别图像增强风格迁移图像生成视觉问答视频分析光学…

C++视觉开发 七.模板匹配

模板匹配是一种基于图像处理的技术&#xff0c;用于在目标图像中寻找与给定模板图像最相似的部分。通过设定的模板&#xff0c;将目标图像与模板图像比较&#xff0c;计算其相似度&#xff0c;实现对目标图像的判断。 目录 一.手写数字识别 重要函数&#xff1a; 1.cv::glob…

【已解决】腾讯云安装了redis,但是本地访问不到,连接不上

汇总了我踩过的所有问题。 查看配置文件redis.conf 1、把bind 127.0.0.1给注释掉&#xff08;前面加个#就是&#xff09;或者改成bind 0.0.0.0&#xff0c;因为刚下载时它是默认只让本地访问。&#xff08;linux查找文档里的内容可以输入/后面加需要匹配的内容&#xff0c;然后…

FAO(脂肪酸β-氧化,Fatty acid beta-oxidation)应用实例

一、FAOBlue及其香豆素衍生物的吸收光谱和荧光光谱 在PBS缓冲液&#xff08;pH 7.4&#xff09;中&#xff0c;FAO代谢后释放的FAOBlue和香豆素衍生物的吸收光谱&#xff08;左&#xff09;、荧光光谱&#xff08;右&#xff09;。 FAOBlue经过FAO转化为香豆素衍生物后&#…

同步时钟系统支持多种校时方式

在当今数字化、信息化高速发展的时代&#xff0c;时间的准确性和同步性变得至关重要。无论是金融交易、通信网络、交通运输&#xff0c;还是工业生产、科学研究等领域&#xff0c;都离不开一个精确且同步的时钟系统。而同步时钟系统之所以能够在众多领域发挥关键作用&#xff0…

使用Python绘制箱线图并分析数据

使用Python绘制箱线图并分析数据 在这篇博客中&#xff0c;我们将探讨如何使用Python中的pandas库和matplotlib库来绘制箱线图&#xff0c;并分析数据文件中的内容。箱线图是一种常用的图表类型&#xff0c;用于展示数据的分布情况及其统计特性&#xff0c;如中位数、四分位数…

程序员日志之DNF手游强化20攻略

目录 传送门正文日志1、概要2、炭的获取3、强化 传送门 SpringMVC的源码解析&#xff08;精品&#xff09; Spring6的源码解析&#xff08;精品&#xff09; SpringBoot3框架&#xff08;精品&#xff09; MyBatis框架&#xff08;精品&#xff09; MyBatis-Plus SpringDataJP…

全能型CAE/CFD建模工具SimLab 详解Part1: Geomtry,轻松集成力学、电磁学、疲劳优化等功能

SimLab的建模功能 SimLab集成了结构力学&#xff0c;流体力学&#xff0c;电磁学&#xff0c;疲劳和优化等功能&#xff0c;是全能型的CAE / CFD建模工具。 具有强大的几何、网格编辑功能&#xff0c;能够快速的清理复杂模型&#xff0c;减少手动修复的工作量&#xff0c;提高…

websocket推送消息,模拟推送

上一篇文章&#xff1a;什么是webSocket&#xff1f;以及它的一些相关理论知识 背景&#xff1a; MQTT 的发布/订阅模式与 WebSocket 的双向通信特性相结合。 通过将 MQTT 与 WebSocket 结合使用&#xff0c;可以在 Web 应用中实现高效、实时的消息传输&#xff0c;特别适用于…

C# 下sendmessage和postmessage的区别详解与示例

文章目录 1、SendMessage2、PostMessage3、两者的区别&#xff1a; 总结 在C#中&#xff0c;SendMessage和PostMessage是两个用于Windows编程的API&#xff0c;它们用于向窗口发送消息。这两个方法都位于System.Windows.Forms命名空间中&#xff0c;通常用于自动化Windows应用程…

AI应用观:从“卷模型”到“卷应用”的时代跨越

在2024年世界人工智能大会的舞台上&#xff0c;百度创始人李彦宏的发言如同一股清流&#xff0c;为当前如火如荼的人工智能领域注入了深刻的思考。他提出的“大家不要卷模型&#xff0c;要卷应用”的观点&#xff0c;不仅是对当前AI技术发展趋势的精准洞察&#xff0c;更是对未…