Android 天气APP(二十)增加欢迎页及白屏黑屏处理、展示世界国家/地区的城市数据

简介: Android 天气APP(二十)增加欢迎页及白屏黑屏处理、展示世界国家/地区的城市数据

文本最终效果图


2020072318144332.gif

前言


本来这一篇文章应该和上一篇放到一起的,但是考虑到篇幅的问题,我就分成两篇文章了,我真的很怕读者看的时候失去动力,动不动就七八万字的博客,看着就害怕。虽然主要是代码比较多,中间穿插一些讲解而已。开始吧!


正文


我相信很多APP都会有这个欢迎页的,也就是启动页面,常规的就是一个页面展示APP的定位,还有就是广告之类的。这个页面更多的功能其实是对APP冷启动和数据的处理,相当于一个缓冲区。先来看一下白屏黑屏的效果


20200723155319804.gif


可以看到虽然这个GIF很多,白屏和黑屏的时间也很短,一刹那间就过去了,但这个就是细节啊,你不处理能行吗?

再看优化后的


2020072316012857.gif


这样虽然说看上去没有啥太大的作用,但是体验就会比较好呀,你说呢?

好了接下来看怎么实现的。

一、欢迎页及黑白屏处理


先创建一个SplashActivity的EmptyActivity,

这里放入启动页的背景图片


20200723160551950.png


然后修改布局文件activity_splash.xml


20200723160624706.png


直接改为ImageView,OK,现在这里就不用管了,进入AndroidManifest.xml


20200723160810881.png


将MainActivit下面的intent-filter剪切到SplashActivity下来,意思就是把SplashActivity作为第一个页面。


2020072316100765.png


进入styles.xml新增


    <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowBackground">@android:color/transparent</item><!--透明背景-->


如下图所示


20200723161043913.png


这个意思就是将APP的所以主题背景改为透明,这样就不存在黑屏白屏的问题了,现在你运行一下之后就不会有这个黑屏白屏了,其实这种处理方式不止一种,更多的方法给我留言,我会考虑单独写一篇博客来讲这个白屏和黑屏的处理。然后我们要在SplashActivity中进行页面的跳转。


写一个延时跳转方法


  /**
     * 进入主页面
     */
    private void goToMain() {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                finish();
                Intent intent = new Intent(context, MainActivity.class);
                startActivity(intent);
            }
        }, 1000);
    }


然后在onCreate方法中调用即可。因为现在所有Activity的背景色都变了透明,所以记得在每个Acitivity对应的布局文件中,在主布局中,如果没有设置背景颜色就增加背景颜色,通常是白色就可以了,否则会出现诡异的现象。

这里先告一段落。


二、世界城市


首先把之前的关于热门城市的东西都删掉,这个里面和热门城市就已经没有关系了。

首先要获取到世界国家/地区的列表。和风提供的国家/地区的城市代码是用的.csv格式,也就是说需要在Android中需要读取CSV文件中的数据读取。可以看看这一篇文章Android 读取csv格式数据文件,


20200723162654669.png


这是我自己弄好的一个文件,和风的都是英文的,所以我就自己翻译好了,需要的就直接到项目中去下载好了,像这种文件,通常都是放在欢迎页中就要完成数据读取的。这里使用Sqlite来保存这些数据吧。在mvplibrary中的bean中新建一个Country实体


20200723162951540.png


代码如下:


package com.llw.mvplibrary.bean;
import org.litepal.crud.LitePalSupport;
public class Country extends LitePalSupport {
    private String name;//名称
    private String code;//代码
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
}

20200723163124697.png


新增一个映射,然后版本改为2。


数据库有了,然后就是数据读取和填充了,回到SplashActivity

新增如下代码:

  private List<Country> list;//数据列表
    private void initCountryData() {
        list = LitePal.findAll(Country.class);
        if (list.size() > 0){//有数据了
            goToMain();
        }else {//第一次加载
            InputStreamReader is = null;
            try {
                is = new InputStreamReader(getAssets().open("world_country.csv"), "UTF-8");
                BufferedReader reader = new BufferedReader(is);
                reader.readLine();
                String line;
                while ((line = reader.readLine()) != null) {
                    String[] result = line.split(",");
                    Country country = new Country();
                    country.setName(result[0]);
                    country.setCode(result[1]);
                    country.save();
                }
                goToMain();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


在这方法中,先获取是否存在数据,不存在则读取文件中的数据,遍历每一行,遍历之后逗号截取为字符串数组分别赋值,最后保存数据,保存好之后则进入主页面,如果已有数据则直接进入主页面。其实我们还可以把权限请求也放到这个里面来,这样就不用在MainActivity中进行动态权限请求了。很多APP都会在第一启动的时候请求所需要的权限,达到一劳永逸。


20200723163949358.png


现在只要进入到MainActivity中就开始定位,当然如果你没有权限你就看不到这个页面。


现在回到SplashActivity中,先继承BaseActivity,实现两个对应的方法,删除onCreate方法。


  private RxPermissions rxPermissions;//权限请求框架
    @Override
    public void initData(Bundle savedInstanceState) {
        StatusBarUtil.transparencyBar(context);//透明状态栏
        rxPermissions = new RxPermissions(this);//实例化这个权限请求框架,否则会报错
        permissionVersion();//权限判断
    }
  //权限判断
    private void permissionVersion() {
        if (Build.VERSION.SDK_INT >= 23) {//6.0或6.0以上
            //动态权限申请
            permissionsRequest();
        } else {//6.0以下
            //发现只要权限在AndroidManifest.xml中注册过,均会认为该权限granted  提示一下即可
            ToastUtils.showShortToast(this, "你的版本在Android6.0以下,不需要动态申请权限。");
        }
    }
    //动态权限申请
    private void permissionsRequest() {//使用这个框架需要制定JDK版本,建议用1.8
        rxPermissions.request(Manifest.permission.ACCESS_FINE_LOCATION)
                .subscribe(granted -> {
                    if (granted) {//申请成功
                        //得到权限可以进入APP
                        //加载世界国家数据到本地数据库,已有则不加载
                        initCountryData();
                    } else {//申请失败
                        finish();
                        ToastUtils.showShortToast(this, "权限未开启");
                    }
                });
    }


这个代码之前我已经解释过一次,就不再做解释了。现在这个欢迎页的使命就已经完成了。OK,世界城市数据有了,下面就是怎么显示的问题了。


在app的ui包下,新建WorldCityActivity。然后修改activity_world_city.xml,代码如下:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:fitsSystemWindows="true"
    android:background="@color/shallow_gray"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.WorldCityActivity">
    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/white"
        app:layout_constraintEnd_toEndOf="parent"
        app:navigationIcon="@mipmap/icon_return"
        app:contentInsetLeft="@dimen/dp_16"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:popupTheme="@style/AppTheme.PopupOverlay">
        <TextView
            android:id="@+id/tv_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:textSize="@dimen/sp_16"
            android:textColor="@color/black"
            android:text="国家/地区" />
    </androidx.appcompat.widget.Toolbar>
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv"
        android:background="@color/white"
        android:layout_marginTop="@dimen/dp_2"
        android:paddingBottom="@dimen/dp_10"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>


在contract包下新建一个WorldCityContract


代码如下:

package com.llw.goodweather.contract;
import com.llw.goodweather.api.ApiService;
import com.llw.goodweather.bean.WorldCityResponse;
import com.llw.mvplibrary.base.BasePresenter;
import com.llw.mvplibrary.base.BaseView;
import com.llw.mvplibrary.net.NetCallBack;
import com.llw.mvplibrary.net.ServiceGenerator;
import retrofit2.Call;
import retrofit2.Response;
/**
 * 世界城市订阅器
 */
public class WorldCityContract {
    public static class WorldCityPresenter extends BasePresenter<IWorldCityView> {
        /**
         * 世界城市  V7
         * @param range  类型
         */
        public void worldCity(String range) {
            ApiService service = ServiceGenerator.createService(ApiService.class, 4);//指明访问的地址
            service.worldCity(range).enqueue(new NetCallBack<WorldCityResponse>() {
                @Override
                public void onSuccess(Call<WorldCityResponse> call, Response<WorldCityResponse> response) {
                    if(getView() != null){
                        getView().getWorldCityResult(response);
                    }
                }
                @Override
                public void onFailed() {
                    if(getView() != null){
                        getView().getDataFailed();
                    }
                }
            });
        }
    }
    public interface IWorldCityView extends BaseView {
        //热门城市返回数据 V7
        void getWorldCityResult(Response<WorldCityResponse> response);
        //错误返回
        void getDataFailed();
    }
}


layout下新建一个item_country_list.xml


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:background="@color/white"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <TextView
        android:id="@+id/tv_country_name"
        android:text="国家"
        android:padding="@dimen/dp_16"
        android:textColor="@color/black"
        android:textSize="@dimen/sp_16"
        android:ellipsize="end"
        android:background="?android:attr/selectableItemBackground"
        android:maxLines="1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <LinearLayout
        android:layout_marginLeft="@dimen/dp_16"
        android:background="@color/line_gray"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dp_1"/>
</LinearLayout>


用于展示世界上的国家/地区

然后就是适配器了。在adapter下新建一个CountryAdapter,代码如下:


package com.llw.goodweather.adapter;
import androidx.annotation.Nullable;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import com.llw.goodweather.R;
import com.llw.mvplibrary.bean.Country;
import java.util.List;
/**
 * 国家列表适配器
 */
public class CountryAdapter extends BaseQuickAdapter<Country, BaseViewHolder> {
    public CountryAdapter(int layoutResId, @Nullable List<Country> data) {
        super(layoutResId, data);
    }
    @Override
    protected void convert(BaseViewHolder helper, Country item) {
        helper.setText(R.id.tv_country_name,item.getName());
        helper.addOnClickListener(R.id.tv_country_name);
    }
}

20200723170101891.png


继承MvpActivity,然后实现方法,下面会逐个说明

初始化这个页面控件,和创建对象


  @BindView(R.id.tv_title)
    TextView tvTitle;
    @BindView(R.id.toolbar)
    Toolbar toolbar;
    @BindView(R.id.rv)
    RecyclerView rv;
    CountryAdapter mAdapter;
    List<Country> mList = new ArrayList<>();


在initData中,这个其实就是取代了onCreate,所以onCreate可以删掉了。


  @Override
    public void initData(Bundle savedInstanceState) {
        StatusBarUtil.setStatusBarColor(context, R.color.white);//白色底  状态栏
        StatusBarUtil.StatusBarLightMode(context);//黑色字体
        Back(toolbar);
        initList();
    }
  @Override
    public int getLayoutId() {
        return R.layout.activity_world_city;
    }
    @Override
    protected WorldCityContract.WorldCityPresenter createPresent() {
        return new WorldCityContract.WorldCityPresenter();
    }
    /**
     * 初始化列表数据
     */
    private void initList() {
        mList = LitePal.findAll(Country.class);
        mAdapter = new CountryAdapter(R.layout.item_country_list, mList);
        rv.setLayoutManager(new LinearLayoutManager(context));
        rv.setAdapter(mAdapter);
        mAdapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
            @Override
            public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
                showLoadingDialog();
                mPresent.worldCity(mList.get(position).getCode());
            }
        });
    }
/**
     * 世界城市返回
     * @param response
     */
    @Override
    public void getWorldCityResult(Response<WorldCityResponse> response) {
            }
    /**
     * 失败异常返回
     */
    @Override
    public void getDataFailed() {
        dismissLoadingDialog();
        ToastUtils.showShortToast(context,"其他异常");
    }


现在只要运行就会看到国家/地区数据


image.gif


然后做这个点击之后的城市弹窗

首先是弹窗的布局。


layout下新建window_world_city_list.xml

代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:fitsSystemWindows="true"
    android:background="@color/white"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    tools:ignore="MissingDefaultResource">
    <TextView
        android:id="@+id/tv_title"
        android:gravity="center"
        android:textColor="@color/black"
        android:padding="@dimen/dp_16"
        android:textSize="@dimen/sp_16"
        android:textStyle="bold"
        android:layout_width="@dimen/dp_240"
        android:layout_height="?attr/actionBarSize"/>
    <View
        android:layout_width="@dimen/dp_240"
        android:layout_height="@dimen/dp_2"
        android:background="@color/line_gray"/>
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_city"
        android:layout_width="@dimen/dp_240"
        android:layout_height="match_parent"/>
</LinearLayout>


adapter中新建一个WorldCityAdapter,代码如下:


package com.llw.goodweather.adapter;
import androidx.annotation.Nullable;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import com.llw.goodweather.R;
import com.llw.goodweather.bean.WorldCityResponse;
import java.util.List;
/**
 * 国家/地区中的城市适配器
 */
public class WorldCityAdapter extends BaseQuickAdapter<WorldCityResponse.TopCityListBean, BaseViewHolder> {
    public WorldCityAdapter(int layoutResId, @Nullable List<WorldCityResponse.TopCityListBean> data) {
        super(layoutResId, data);
    }
    @Override
    protected void convert(BaseViewHolder helper, WorldCityResponse.TopCityListBean item) {
        helper.setText(R.id.tv_city,item.getName());
        helper.addOnClickListener(R.id.tv_city);
    }
}


回到WorldCityActivity


20200723173410618.png


新增


20200723173454249.png


点击列表中某一个国家,然后获取到这个code,通过code来请求接口获取城市数据,然后返回中将城市的数据传递到弹窗中,在弹窗中渲染数据。


20200723173539107.png


城市弹窗代码如下:

  /**
     * 城市弹窗
     */
    private void showCityWindow(String countryName,List<WorldCityResponse.TopCityListBean> list) {
        LiWindow liWindow = new LiWindow(context);
        final View view = LayoutInflater.from(context).inflate(R.layout.window_world_city_list, null);
        TextView windowTitle = (TextView) view.findViewById(R.id.tv_title);
        windowTitle.setText(countryName);
        RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.rv_city);
        liWindow.showRightPopupWindowMatchParent(view);//显示弹窗
        mCityAdapter = new WorldCityAdapter(R.layout.item_city_list,list);
        recyclerView.setLayoutManager(new LinearLayoutManager(context));
        recyclerView.setAdapter(mCityAdapter);
        mCityAdapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
            @Override
            public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
                Intent intent = new Intent(context, WorldCityWeatherActivity.class);
                intent.putExtra("name",list.get(position).getName());
                intent.putExtra("locationId",list.get(position).getId());
                startActivity(intent);
                liWindow.closePopupWindow();
            }
        });
    }


到此为止,世界城市这个也买那就写完了,然后就写这个城市点击之后的天气查询了。在ui包下新建一个WorldCityWeatherActivity,作为点击跳转的Activity。


在修改布局之前先在mvplibrary下的values中的colors.xml中新增一个

20200723174356295.png


<color name="world_city_color">#243440</color>


然后修改activity_world_city_weather.xml


这里面用到一个icon_hot_city_bg_2的图片

20200724093920718.png

20200724093920732.png


这里提供两个给你选择


<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:background="@color/world_city_color"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.WorldCityWeatherActivity">
    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:background="@mipmap/icon_hot_city_bg_2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:contentInsetStart="0dp"
            android:fitsSystemWindows="true"
            app:layout_scrollFlags="scroll|enterAlways"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">
                <TextView
                    android:padding="@dimen/dp_12"
                    android:id="@+id/tv_title"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="城市"
                    android:textColor="@color/white"
                    android:textSize="@dimen/sp_18" />
                <!--温度-->
                <RelativeLayout
                    android:padding="@dimen/dp_12"
                    android:gravity="top"
                    android:orientation="horizontal"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">
                    <!--气候图标-->
                    <ImageView
                        android:id="@+id/iv_weather_state"
                        android:layout_width="@dimen/dp_150"
                        android:layout_height="@dimen/dp_150" />
                    <LinearLayout
                        android:layout_marginRight="@dimen/dp_20"
                        android:layout_alignParentRight="true"
                        android:orientation="vertical"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:gravity="center_horizontal">
                        <TextView
                            android:layout_alignParentRight="true"
                            android:id="@+id/tv_temperature"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:text="0°"
                            android:textColor="@color/white"
                            android:textSize="@dimen/sp_48" />
                        <TextView
                            android:id="@+id/tv_weather_state"
                            android:layout_toRightOf="@+id/tv_temperature"
                            android:text="天气状态"
                            android:textColor="@color/white"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"/>
                        <!--最高温和最低温-->
                        <LinearLayout
                            android:layout_marginTop="@dimen/dp_8"
                            android:orientation="horizontal"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content">
                            <TextView
                                android:id="@+id/tv_tem_max"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:layout_marginLeft="@dimen/dp_8"
                                android:textColor="@color/white"
                                android:textSize="@dimen/sp_14" />
                            <TextView
                                android:id="@+id/tv_tem_min"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:layout_marginLeft="@dimen/dp_8"
                                android:alpha="0.5"
                                android:textColor="@color/white"
                                android:textSize="@dimen/sp_14" />
                        </LinearLayout>
                        <!--风信息-->
                        <TextView
                            android:layout_marginTop="@dimen/dp_8"
                            android:textSize="@dimen/sp_14"
                            android:id="@+id/tv_wind_state"
                            android:textColor="@color/white"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"/>
                    </LinearLayout>
                </RelativeLayout>
            </LinearLayout>
        </androidx.appcompat.widget.Toolbar>
    </com.google.android.material.appbar.AppBarLayout>
    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true"
        android:orientation="vertical"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/rv_hourly"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@color/world_city_color" />
        </LinearLayout>
    </androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>


这里页面用了协调布局,形成折叠效果,具体的说明可以看这一篇文章Android 折叠式布局,这也是之前写的文章了,说明可能会详细一些,可以看看。


布局写好之后,在app中的contract下创建一个WorldCityWeatherContract订阅器,代码如下:


package com.llw.goodweather.contract;
import com.llw.goodweather.api.ApiService;
import com.llw.goodweather.bean.DailyResponse;
import com.llw.goodweather.bean.HourlyResponse;
import com.llw.goodweather.bean.NowResponse;
import com.llw.mvplibrary.base.BasePresenter;
import com.llw.mvplibrary.base.BaseView;
import com.llw.mvplibrary.net.NetCallBack;
import com.llw.mvplibrary.net.ServiceGenerator;
import retrofit2.Call;
import retrofit2.Response;
/**
 * 世界城市天气订阅器
 */
public class WorldCityWeatherContract {
    public static class WorldCityWeatherPresenter extends BasePresenter<IWorldCityWeatherView> {
        /**
         * 实况天气  V7版本
         * @param location  城市名
         */
        public void nowWeather(String location){//这个3 表示使用新的V7API访问地址
            ApiService service = ServiceGenerator.createService(ApiService.class,3);
            service.nowWeather(location).enqueue(new NetCallBack<NowResponse>() {
                @Override
                public void onSuccess(Call<NowResponse> call, Response<NowResponse> response) {
                    if(getView() != null){
                        getView().getNowResult(response);
                    }
                }
                @Override
                public void onFailed() {
                    if(getView() != null){
                        getView().getDataFailed();
                    }
                }
            });
        }
        /**
         * 天气预报  V7版本   7d 表示天气的数据 为了和之前看上去差别小一些,这里先用七天的
         * @param location   城市名
         */
        public void dailyWeather(String location){//这个3 表示使用新的V7API访问地址
            ApiService service = ServiceGenerator.createService(ApiService.class,3);
            service.dailyWeather("7d",location).enqueue(new NetCallBack<DailyResponse>() {
                @Override
                public void onSuccess(Call<DailyResponse> call, Response<DailyResponse> response) {
                    if(getView() != null){
                        getView().getDailyResult(response);
                    }
                }
                @Override
                public void onFailed() {
                    if(getView() != null){
                        getView().getDataFailed();
                    }
                }
            });
        }
        /**
         * 逐小时预报(未来24小时)
         * @param location   城市名
         */
        public void hourlyWeather(String location){
            ApiService service = ServiceGenerator.createService(ApiService.class,3);
            service.hourlyWeather(location).enqueue(new NetCallBack<HourlyResponse>() {
                @Override
                public void onSuccess(Call<HourlyResponse> call, Response<HourlyResponse> response) {
                    if(getView() != null){
                        getView().getHourlyResult(response);
                    }
                }
                @Override
                public void onFailed() {
                    if(getView() != null){
                        getView().getDataFailed();
                    }
                }
            });
        }
    }
    public interface IWorldCityWeatherView extends BaseView {
        /*               以下为V7版本新增               */
        //实况天气
        void getNowResult(Response<NowResponse> response);
        //天气预报  7天
        void getDailyResult(Response<DailyResponse> response);
        //逐小时天气预报
        void getHourlyResult(Response<HourlyResponse> response);
        //错误返回
        void getDataFailed();
    }
}


在layout下新建一个item_weather_hourly_world_list.xml布局文件,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:orientation="horizontal"
    android:paddingLeft="@dimen/dp_16"
    android:paddingRight="@dimen/dp_16">
    <!--时间-->
    <TextView
        android:id="@+id/tv_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="上午10:00"
        android:textColor="#FFF"
        android:textSize="14sp" />
    <!--气候图标-->
    <RelativeLayout
        android:layout_width="@dimen/dp_70"
        android:layout_height="@dimen/dp_70">
        <View
            android:layout_width="@dimen/dp_1"
            android:layout_height="match_parent"
            android:layout_centerHorizontal="true"
            android:background="#374552" />
        <ImageView
            android:id="@+id/iv_weather_state"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_centerInParent="true"
            android:background="@mipmap/icon_100" />
    </RelativeLayout>
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        <!--天气状况文字描述-->
        <TextView
            android:id="@+id/tv_weather_state"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="晴"
            android:textColor="#FFF"
            android:textSize="@dimen/sp_16" />
        <!--温度-->
        <TextView
            android:id="@+id/tv_temperature"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="25℃"
            android:textColor="#FFF"
            android:textSize="@dimen/sp_16" />
    </LinearLayout>
    <!--风描述-->
    <TextView
        android:gravity="center"
        android:id="@+id/tv_wind_info"
        android:layout_width="@dimen/dp_0"
        android:layout_weight="1"
        android:layout_height="wrap_content"
        android:layout_marginLeft="@dimen/dp_16"
        android:textColor="#FFF"
        android:textSize="@dimen/sp_14" />
</LinearLayout>


然后写适配器,在adapter包下新建一个HourlyWorldCityAdapter,代码如下:


package com.llw.goodweather.adapter;
import android.os.Build;
import android.widget.ImageView;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import com.llw.goodweather.R;
import com.llw.goodweather.bean.HourlyResponse;
import com.llw.goodweather.utils.DateUtils;
import com.llw.goodweather.utils.WeatherUtil;
import java.util.List;
/**
 * V7 API 热门城市  逐小时预报数据列表适配器
 */
public class HourlyWorldCityAdapter extends BaseQuickAdapter<HourlyResponse.HourlyBean, BaseViewHolder> {
    public HourlyWorldCityAdapter(int layoutResId, @Nullable List<HourlyResponse.HourlyBean> data) {
        super(layoutResId, data);
    }
    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    protected void convert(BaseViewHolder helper, HourlyResponse.HourlyBean item) {
        /**
         * V7 API 涉及到时间的,都会返回 2020-07-16T09:39+08:00  这种格式
         * 所以最好写一个通用的返回进行处理 方法已经写好了使用可以了
         */
        String time = DateUtils.updateTime(item.getFxTime());
        helper.setText(R.id.tv_time, WeatherUtil.showTimeInfo(time) + time)//时间
                .setText(R.id.tv_temperature, item.getTemp() + "℃")
                .setText(R.id.tv_weather_state, item.getText())
                .setText(R.id.tv_wind_info, item.getWindDir() + "," + item.getWindScale() + "级");//温度
        //天气状态图片
        ImageView weatherStateIcon = helper.getView(R.id.iv_weather_state);
        int code = Integer.parseInt(item.getIcon());//获取天气状态码,根据状态码来显示图标
        WeatherUtil.changeIcon(weatherStateIcon, code);
    }
}


然后回到WorldCityWeatherActivity中,

20200723175526252.png

  @BindView(R.id.tv_title)
    TextView tvTitle;//城市
    @BindView(R.id.toolbar)
    Toolbar toolbar;//标题bar
    @BindView(R.id.tv_temperature)
    TextView tvTemperature;//温度
    @BindView(R.id.iv_weather_state)
    ImageView ivWeatherState;//天气状况图片
    @BindView(R.id.tv_tem_max)
    TextView tvTemMax;//最高温
    @BindView(R.id.tv_tem_min)
    TextView tvTemMin;//最低温
    @BindView(R.id.rv_hourly)
    RecyclerView rvHourly;//逐小时列表
    @BindView(R.id.tv_weather_state)
    TextView tvWeatherState;//天气状态文字描述
    @BindView(R.id.tv_wind_state)
    TextView tvWindState;//风状态文字描述
    HourlyWorldCityAdapter mAdapter;//逐小时列表适配器
    List<HourlyResponse.HourlyBean> mList = new ArrayList<>();//列表数据


初始化 ,删除掉onCreate方法


   @Override
    public void initData(Bundle savedInstanceState) {
        initView();
    }


  /**
     * 初始化页面
     */
    private void initView() {
        StatusBarUtil.transparencyBar(context);//设置状态栏背景颜色
        Back(toolbar);
        showLoadingDialog();//加载弹窗
        mAdapter = new HourlyWorldCityAdapter(R.layout.item_weather_hourly_world_list, mList);
        rvHourly.setLayoutManager(new LinearLayoutManager(context));
        rvHourly.setAdapter(mAdapter);
        String locationId = getIntent().getStringExtra("locationId");//获取上一个页面传递过来的城市id
        tvTitle.setText(getIntent().getStringExtra("name"));//城市名称显示
        mPresent.nowWeather(locationId);//查询实况天气
        mPresent.dailyWeather(locationId);//查询天气预报
        mPresent.hourlyWeather(locationId);//查询逐小时天气预报
    }


  @Override
    public int getLayoutId() {
        return R.layout.activity_world_city_weather;
    }
    @Override
    protected WorldCityWeatherContract.WorldCityWeatherPresenter createPresent() {
        return new WorldCityWeatherContract.WorldCityWeatherPresenter();
    }


接口返回处理和异常返回处理


  /**
     * 实况天气返回  V7
     *
     * @param response
     */
    @Override
    public void getNowResult(Response<NowResponse> response) {
        if (response.body().getCode().equals(Constant.SUCCESS_CODE)) {
            Typeface typeface = Typeface.createFromAsset(getAssets(), "fonts/GenJyuuGothic-ExtraLight.ttf");
            tvTemperature.setText(response.body().getNow().getTemp() + "°");
            tvTemperature.setTypeface(typeface);//使用字体
            int code = Integer.parseInt(response.body().getNow().getIcon());//获取天气状态码,根据状态码来显示图标
            WeatherUtil.changeIcon(ivWeatherState, code);//调用工具类中写好的方法
            tvWeatherState.setText("当前:" + response.body().getNow().getText());
            tvWindState.setText(response.body().getNow().getWindDir()+"   "+response.body().getNow().getWindScale()+"级");
        } else {
            ToastUtils.showShortToast(context, CodeToStringUtils.WeatherCode(response.body().getCode()));
        }
    }
    /**
     * 天气预报 V7
     *
     * @param response
     */
    @Override
    public void getDailyResult(Response<DailyResponse> response) {
        if (response.body().getCode().equals(Constant.SUCCESS_CODE)) {
            if (response.body().getDaily() != null && response.body().getDaily().size() > 0) {
                tvTemMax.setText(response.body().getDaily().get(0).getTempMax());
                tvTemMin.setText(" / " + response.body().getDaily().get(0).getTempMin());
            } else {
                ToastUtils.showShortToast(context, "暂无天气预报数据");
            }
        } else {
            ToastUtils.showShortToast(context, CodeToStringUtils.WeatherCode(response.body().getCode()));
        }
    }
    /**
     * 逐小时天气预报 V7
     *
     * @param response
     */
    @Override
    public void getHourlyResult(Response<HourlyResponse> response) {
        if (response.body().getCode().equals(Constant.SUCCESS_CODE)) {
            List<HourlyResponse.HourlyBean> data = response.body().getHourly();
            if (data != null && data.size() > 0) {
                mList.clear();
                mList.addAll(data);
                mAdapter.notifyDataSetChanged();
                dismissLoadingDialog();
            } else {
                ToastUtils.showShortToast(context, "逐小时天气查询不到");
            }
        } else {
            ToastUtils.showShortToast(context, CodeToStringUtils.WeatherCode(response.body().getCode()));
        }
    }
    //异常返回
    @Override
    public void getDataFailed() {
        dismissLoadingDialog();
        ToastUtils.showShortToast(context, "请求超时");
    }


还差最后一步,修改window_add.xml


20200723182743510.png


回到MainActivity


20200723182907332.png


20200723182935386.png


修改热门城市为世界城市,id也改了。


2020072318144332.gif

相关文章
|
1月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
125 4
|
4月前
|
开发工具 Android开发 开发者
Android平台如何不推RTMP|不发布RTSP流|不实时录像|不回传GB28181数据时实时快照?
本文介绍了一种在Android平台上实现实时截图快照的方法,尤其适用于无需依赖系统接口的情况,如在RTMP推送、RTSP服务或GB28181设备接入等场景下进行截图。通过底层模块(libSmartPublisher.so)实现了截图功能,封装了`SnapShotImpl.java`类来管理截图流程。此外,提供了关键代码片段展示初始化SDK实例、执行截图、以及在Activity销毁时释放资源的过程。此方案还考虑到了快照数据的灵活处理需求,符合GB/T28181-2022的技术规范。对于寻求更灵活快照机制的开发者来说,这是一个值得参考的设计思路。
|
5月前
|
XML 自然语言处理 Android开发
🌐Android国际化与本地化全攻略!让你的App走遍全球无障碍!🌍
【7月更文挑战第28天】在全球化背景下,实现Android应用的国际化与本地化至关重要 for 用户基础扩展。本文通过旅游指南App案例,介绍全攻略。步骤包括资源文件拆分与命名、适配布局与方向、处理日期时间及货币格式、考虑文化习俗及进行详尽测试。采用Android Studio支持,创建如`res/values-en/strings.xml`等多语言资源文件夹,使用灵活布局解决文本长度差异问题,并通过用户反馈迭代优化。最终,打造一款能无缝融入全球各地文化的App。
218 3
|
3月前
|
定位技术 Android开发 iOS开发
引入百度地图,安卓出现白屏问题
引入百度地图,安卓出现白屏问题
193 57
|
2月前
|
JSON API 网络安全
App数据的爬取
App数据的爬取
44 3
|
2月前
|
存储 大数据 数据库
Android经典面试题之Intent传递数据大小为什么限制是1M?
在 Android 中,使用 Intent 传递数据时存在约 1MB 的大小限制,这是由于 Binder 机制的事务缓冲区限制、Intent 的设计初衷以及内存消耗和性能问题所致。推荐使用文件存储、SharedPreferences、数据库存储或 ContentProvider 等方式传递大数据。
97 0
|
4月前
|
存储 SQL JSON
【Azure Logic App】微软云逻辑应用连接到数据库,执行存储过程并转换执行结果为JSON数据
【Azure Logic App】微软云逻辑应用连接到数据库,执行存储过程并转换执行结果为JSON数据
【Azure Logic App】微软云逻辑应用连接到数据库,执行存储过程并转换执行结果为JSON数据
|
4月前
|
缓存
【Azure Function】Function App代码中使用Managed Identity认证获取Blob数据时遇见400报错
【Azure Function】Function App代码中使用Managed Identity认证获取Blob数据时遇见400报错
【Azure Function】Function App代码中使用Managed Identity认证获取Blob数据时遇见400报错
|
4月前
|
JSON Java Android开发
Android 开发者必备秘籍:轻松攻克 JSON 格式数据解析难题,让你的应用更出色!
【8月更文挑战第18天】在Android开发中,解析JSON数据至关重要。JSON以其简洁和易读成为首选的数据交换格式。开发者可通过多种途径解析JSON,如使用内置的`JSONObject`和`JSONArray`类直接操作数据,或借助Google提供的Gson库将JSON自动映射为Java对象。无论哪种方法,正确解析JSON都是实现高效应用的关键,能帮助开发者处理网络请求返回的数据,并将其展示给用户,从而提升应用的功能性和用户体验。
115 1
|
4月前
|
缓存 API Android开发
Android经典实战之Kotlin Flow中的3个数据相关的操作符:debounce、buffer和conflate
本文介绍了Kotlin中`Flow`的`debounce`、`buffer`及`conflate`三个操作符。`debounce`过滤快速连续数据,仅保留指定时间内的最后一个;`buffer`引入缓存减轻背压;`conflate`仅保留最新数据。通过示例展示了如何在搜索输入和数据流处理中应用这些操作符以提高程序效率和用户体验。
57 6