文本最終效果圖
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICMyYTMvw1dvwlMvwlM3VWaWV2Zh1Wa-YWan5iNjdWN3BDOqFTMvwFOwUjN4ITNtUGall3LcVmdhNXLwRHdo9CXt92YucWbpRWdvx2Yx5yazF2Lc9CX6MHc0RHaiojIsJye.gif)
前言
本來這一篇文章應該和上一篇放到一起的,但是考慮到篇幅的問題,我就分成兩篇文章了,我真的很怕讀者看的時候失去動力,動不動就七八萬字的部落格,看着就害怕。雖然主要是代碼比較多,中間穿插一些講解而已。開始吧!
正文
我相信很多APP都會有這個歡迎頁的,也就是啟動頁面,正常的就是一個頁面展示APP的定位,還有就是廣告之類的。這個頁面更多的功能其實是對APP冷啟動和資料的處理,相當于一個緩沖區。先來看一下白屏黑屏的效果
可以看到雖然這個GIF很多,白屏和黑屏的時間也很短,一刹那間就過去了,但這個就是細節啊,你不處理能行嗎?
再看優化後的
這樣雖然說看上去沒有啥太大的作用,但是體驗就會比較好呀,你說呢?
好了接下來看怎麼實作的。
一、歡迎頁及黑白屏處理
先建立一個SplashActivity的EmptyActivity,
這裡放入啟動頁的背景圖檔
然後修改布局檔案activity_splash.xml
直接改為ImageView,OK,現在這裡就不用管了,進入AndroidManifest.xml
将MainActivit下面的intent-filter剪切到SplashActivity下來,意思就是把SplashActivity作為第一個頁面。
進入styles.xml新增
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item><!--透明背景-->
複制
如下圖所示
這個意思就是将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格式資料檔案,
這是我自己弄好的一個檔案,和風的都是英文的,是以我就自己翻譯好了,需要的就直接到項目中去下載下傳好了,像這種檔案,通常都是放在歡迎頁中就要完成資料讀取的。這裡使用Sqlite來儲存這些資料吧。在mvplibrary中的bean中建立一個Country實體
代碼如下:
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;
}
}
複制
新增一個映射,然後版本改為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都會在第一啟動的時候請求所需要的權限,達到一勞永逸。
現在隻要進入到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);
}
}
複制
繼承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,"其他異常");
}
複制
現在隻要運作就會看到國家/地區資料
然後做這個點選之後的城市彈窗
首先是彈窗的布局。
在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
新增
點選清單中某一個國家,然後擷取到這個code,通過code來請求接口擷取城市資料,然後傳回中将城市的資料傳遞到彈窗中,在彈窗中渲染資料。
城市彈窗代碼如下:
/**
* 城市彈窗
*/
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中新增一個
<color name="world_city_color">#243440</color>
複制
然後修改activity_world_city_weather.xml
這裡面用到一個icon_hot_city_bg_2的圖檔
這裡提供兩個給你選擇
<?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中,
@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
回到MainActivity
修改熱門城市為世界城市,id也改了。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICMyYTMvw1dvwlMvwlM3VWaWV2Zh1Wa-YWan5iNjdWN3BDOqFTMvwFOwUjN4ITNtUGall3LcVmdhNXLwRHdo9CXt92YucWbpRWdvx2Yx5yazF2Lc9CX6MHc0RHaiojIsJye.gif)