上一篇:Android 天气APP(十五)增加城市搜索、历史搜索记录
添加搜索城市
新版-------------------
在上一篇文章中我们实现了通过推荐城市添加城市的方法,那么如果推荐的城市里没有没有我们所需要的城市时,应该怎么办呢?通过搜索城市,本篇文章将给添加城市的弹窗,增加一个搜索城市的功能,这样我们既能通过推荐城市快速找到城市、也能通过搜索城市找我们想找的城市。
一、修改添加城市弹窗布局
这里我们首先要修改的是dialog_add_city.xml中的代码,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_color"
android:orientation="vertical"
tools:ignore="MissingDefaultResource">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navigationIcon="@drawable/ic_round_close_24"
app:title="添加城市"
app:titleCentered="true" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/lay_search_city"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="@color/white"
app:layout_constraintTop_toBottomOf="@+id/toolbar">
<ImageView
android:id="@+id/iv_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_round_search_24" />
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/et_search_city"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:background="@null"
android:hint="请输入城市名称"
android:imeOptions="actionSearch"
android:singleLine="true"
android:textSize="@dimen/sp_14"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/iv_clear"
app:layout_constraintStart_toEndOf="@+id/iv_search"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/iv_clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_round_close_24" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!--推荐城市列表-->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_recommend_city"
android:layout_width="@dimen/dp_0"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_1"
android:background="@color/bg_color"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/lay_search_city" />
<!--搜索城市列表-->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_search_city"
android:layout_width="@dimen/dp_0"
android:layout_height="wrap_content"
android:minHeight="500dp"
android:layout_marginTop="@dimen/dp_2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/lay_search_city" />
</androidx.constraintlayout.widget.ConstraintLayout>
这里在xml中增加了搜索城市的布局和搜索城市的列表,预览如图所示:
然后还修改了搜索城市列表的最小高度,这是设置一下是为了避免搜索结果过少时页面不友好。这里你会看到有一个搜索图标,在drawable下新建一个ic_round_search_24.xml,代码如下所示:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#000000"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M15.5,14h-0.79l-0.28,-0.27c1.2,-1.4 1.82,-3.31 1.48,-5.34 -0.47,-2.78 -2.79,-5 -5.59,-5.34 -4.23,-0.52 -7.79,3.04 -7.27,7.27 0.34,2.8 2.56,5.12 5.34,5.59 2.03,0.34 3.94,-0.28 5.34,-1.48l0.27,0.28v0.79l4.25,4.25c0.41,0.41 1.08,0.41 1.49,0 0.41,-0.41 0.41,-1.08 0,-1.49L15.5,14zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" />
</vector>
二、添加搜索城市item和适配器
这里添加了列表,下面就是添加一个item,在layout下新建一个item_search_city_rv.xml,代码如下所示:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tv_city_name"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginBottom="@dimen/dp_1"
android:background="@color/white"
android:gravity="center_vertical"
android:paddingStart="@dimen/dp_16"
android:text="搜索城市Item"
android:textSize="@dimen/sp_16" />
然后我们写适配器,在adapter包下新建一个SearchCityAdapter类,代码如下所示:
public class SearchCityAdapter extends RecyclerView.Adapter<SearchCityAdapter.ViewHolder> {
private final List<SearchCityResponse.LocationBean> beans;
private OnClickItemCallback onClickItemCallback;//视图点击
//关键字
private String targetStr;
public SearchCityAdapter(List<SearchCityResponse.LocationBean> cities) {
this.beans = cities;
}
public void setOnClickItemCallback(OnClickItemCallback onClickItemCallback) {
this.onClickItemCallback = onClickItemCallback;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
ItemSearchCityRvBinding binding = ItemSearchCityRvBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
ViewHolder viewHolder = new ViewHolder(binding);
//添加视图点击事件
binding.getRoot().setOnClickListener(v -> {
if (onClickItemCallback != null) {
onClickItemCallback.onItemClick(viewHolder.getAdapterPosition());
}
});
return viewHolder;
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
SearchCityResponse.LocationBean bean = beans.get(position);
String result = bean.getName() + " , " + bean.getAdm2() + " , " + bean.getAdm1() + " , " + bean.getCountry();
if (targetStr != null && targetStr.length() > 0) {
holder.binding.tvCityName.setText(matcherSearchText(result, targetStr));
} else {
holder.binding.tvCityName.setText(result);
}
}
@Override
public int getItemCount() {
return beans.size();
}
/**
* 改变颜色
*
* @param content 输入的文本
*/
public void changTxColor(String content) {
targetStr = content;
notifyDataSetChanged();
}
/**
* 改变一段文本中第一个关键字的文字颜色
*
* @param string 文本字符串
* @param keyWord 关键字
* @return
*/
public static CharSequence matcherSearchText(String string, String keyWord) {
SpannableStringBuilder builder = new SpannableStringBuilder(string);
int indexOf = string.indexOf(keyWord);
if (indexOf != -1) {
builder.setSpan(new ForegroundColorSpan(ContextCompat.getColor(WeatherApp.getContext(),R.color.yellow)), indexOf, indexOf + keyWord.length(), SPAN_EXCLUSIVE_EXCLUSIVE);
}
return builder;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public ItemSearchCityRvBinding binding;
public ViewHolder(@NonNull ItemSearchCityRvBinding itemSearchCityRvBinding) {
super(itemSearchCityRvBinding.getRoot());
binding = itemSearchCityRvBinding;
}
}
}
这个适配器中唯一值得说一下的就是SpannableStringBuilder ,通过这个可以设置一行文字多种颜色,其他的地方都是基本使用。
三、添加搜索城市功能
修改AddCityDialog,代码如下所示:
public class AddCityDialog {
private static final MutableLiveData<SearchCityResponse> searchCityResponseMutableLiveData = new MutableLiveData<>();
private static final MutableLiveData<String> failed = new MutableLiveData<>();
private static final List<SearchCityResponse.LocationBean> beans = new ArrayList<>();
private static final SearchCityAdapter searchCityAdapter = new SearchCityAdapter(beans);
/**
* 显示弹窗
*/
public static void show(Context context, List<String> cities, SelectedCityCallback selectedCityCallback) {
BottomSheetDialog dialog = new BottomSheetDialog(context);
DialogAddCityBinding binding = DialogAddCityBinding.inflate(LayoutInflater.from(context), null, false);
//搜索城市适配器Item点击监听
searchCityAdapter.setOnClickItemCallback(position -> {
if (selectedCityCallback != null) {
selectedCityCallback.selectedCity(beans.get(position).getName());
dialog.dismiss();
}
});
binding.rvSearchCity.setLayoutManager(new LinearLayoutManager(context));
binding.rvSearchCity.setAdapter(searchCityAdapter);
//输入框监听
binding.etSearchCity.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
binding.ivClear.setVisibility(s.toString().isEmpty() ? View.INVISIBLE : View.VISIBLE);
}
});
//输入框焦点监听
binding.etSearchCity.setOnFocusChangeListener((v, hasFocus) ->
BottomSheetBehavior.from(Objects.requireNonNull(dialog.findViewById(com.google.android.material.R.id.design_bottom_sheet)))
.setState(hasFocus ? BottomSheetBehavior.STATE_EXPANDED : BottomSheetBehavior.STATE_COLLAPSED));
//输入法的回车按钮监听
binding.etSearchCity.setOnEditorActionListener((v, actionId, event) -> {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
String cityName = binding.etSearchCity.getText().toString().trim();
if (cityName.isEmpty()) {
Toast.makeText(context, "请输入城市名称", Toast.LENGTH_SHORT).show();
return false;
}
searchCityAdapter.changTxColor(cityName);
BottomSheetBehavior.from(Objects.requireNonNull(dialog.findViewById(com.google.android.material.R.id.design_bottom_sheet)))
.setState(BottomSheetBehavior.STATE_EXPANDED);
//搜索城市
SearchCityRepository.getInstance().searchCity(searchCityResponseMutableLiveData, failed, cityName);
//关闭输入法
InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
// 关闭屏幕上的输入法软键盘
inputMethodManager.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
return false;
});
//清除输入框内容按钮
binding.ivClear.setOnClickListener(v -> {
binding.etSearchCity.setText("");
binding.rvRecommendCity.setVisibility(View.VISIBLE);
binding.rvSearchCity.setVisibility(View.GONE);
});
//搜索城市返回
searchCityResponseMutableLiveData.observe((LifecycleOwner) context, searchCityResponse -> {
//有数据就隐藏推荐城市列表,否则就提示没有找到对应的城市数据
if (searchCityResponse.getLocation() == null || searchCityResponse.getLocation().size() <= 0) {
binding.rvRecommendCity.setVisibility(View.VISIBLE);
binding.rvSearchCity.setVisibility(View.GONE);
Toast.makeText(context, "未找到相应城市,请重新搜索。", Toast.LENGTH_SHORT).show();
} else {
binding.rvRecommendCity.setVisibility(View.GONE);
binding.rvSearchCity.setVisibility(View.VISIBLE);
beans.clear();
beans.addAll(searchCityResponse.getLocation());
searchCityAdapter.notifyDataSetChanged();
}
});
RecommendCityAdapter recommendCityAdapter = new RecommendCityAdapter(cities);
recommendCityAdapter.setOnClickItemCallback(position -> {
if (selectedCityCallback != null) {
selectedCityCallback.selectedCity(cities.get(position));
dialog.dismiss();
}
});
//网格布局管理器 一行3个
GridLayoutManager manager = new GridLayoutManager(context, 3);
binding.rvRecommendCity.setLayoutManager(manager);
binding.rvRecommendCity.setAdapter(recommendCityAdapter);
binding.toolbar.setNavigationOnClickListener(v -> dialog.dismiss());
binding.rvRecommendCity.setVisibility(View.VISIBLE);
binding.rvSearchCity.setVisibility(View.GONE);
dialog.setContentView(binding.getRoot());
dialog.show();
}
public interface SelectedCityCallback {
void selectedCity(String cityName);
}
}
这里代码主要就是增加输入框的处理,从上面开始先进行变量的声明。
这里就是搜索城市适配器的点击监听,你会发现这里和推荐城市列表点击的处理是一样的。
然后是输入框的输入监听,当输入后显示这个删除图标。
这里是输入框的焦点监听,当获取了焦点之后就展开整个弹窗布局,用户体验会好一些。
这里就是输入法的监听,通过XML中EditText的设置,输入法的回车按钮会变成搜索按钮,这里我们就是对搜索按钮进行点击监听,如果输入了内容,就搜索城市,通过将关键字传递到适配器中,适配器会在渲染数据的时候根据这个去设置文字的颜色。点击搜索按钮时,关闭输入法。最后这里有一个删除图标的点击事件,两个列表恢复之前的状态。
搜索城市的返回数据处理,下面运行一下:
四、删除城市
现在对于我的城市添加来说已经做了很多了,下面需要删除城市,修改ManageCityActivity中的initView()方法,代码如下所示:
ItemTouchHelper helper = new ItemTouchHelper(new ItemTouchHelper.Callback() {
@Override
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
//控制快速滑动的方向
int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(0, swipeFlags);
}
@Override
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
return false;
}
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
//显示提示弹窗
showDeleteCity(viewHolder.getAdapterPosition());
}
});
//关联recyclerView
helper.attachToRecyclerView(binding.rvCity);
通过这个代码,适配器Item就可以左右滑动了,添加位置如下图所示:
这里滑动之后显示一个弹窗,弹窗方法代码如下所示:
private void showDeleteCity(int position) {
// 声明对象
AlertDialog dialog;
AlertDialog.Builder builder = new AlertDialog.Builder(this)
.setTitle("删除城市")
.setIcon(R.drawable.ic_round_delete_forever_24)
.setMessage("您确定要删除吗?")
.setPositiveButton("确定", (dialog1, which) -> {
dialog1.dismiss();
}).setNegativeButton("取消", (dialog12, which) -> {
dialog12.dismiss();
});
dialog = builder.create();
dialog.show();
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(Color.GRAY);
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(Color.BLACK);
}
这里弹窗有一个确定和取消,里面的内容我们还没有写的,这里首先弹窗有一个图标,在drawable下新建一个ic_round_delete_forever_24.xml,代码如下所示:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="@color/black"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6V19zM9.17,12.59c-0.39,-0.39 -0.39,-1.02 0,-1.41c0.39,-0.39 1.02,-0.39 1.41,0L12,12.59l1.41,-1.41c0.39,-0.39 1.02,-0.39 1.41,0s0.39,1.02 0,1.41L13.41,14l1.41,1.41c0.39,0.39 0.39,1.02 0,1.41s-1.02,0.39 -1.41,0L12,15.41l-1.41,1.41c-0.39,0.39 -1.02,0.39 -1.41,0c-0.39,-0.39 -0.39,-1.02 0,-1.41L10.59,14L9.17,12.59zM18,4h-2.5l-0.71,-0.71C14.61,3.11 14.35,3 14.09,3H9.91c-0.26,0 -0.52,0.11 -0.7,0.29L8.5,4H6C5.45,4 5,4.45 5,5s0.45,1 1,1h12c0.55,0 1,-0.45 1,-1S18.55,4 18,4z" />
</vector>
下面在ManageCityViewModel中添加如下代码:
/**
* 删除我的城市数据
*/
public void deleteMyCityData(MyCity myCity) {
CityRepository.getInstance().deleteMyCityData(myCity);
}
/**
* 删除我的城市数据
*/
public void deleteMyCityData(String cityName) {
CityRepository.getInstance().deleteMyCityData(cityName);
}
下面就是调用这个删除的方法了,如下图所示:
这里在点击确定就移除View,然后数据库中数据移除,点击取消的话就恢复之前的View,运行一下看看。
五、文章源码
欢迎 Star 和 Fork
第十六篇文章源码地址: GoodWeather-New-16
旧版-------------------
反正国内的天气也做好了,不妨做一下做一下国外的天气,国外的天气数据的展示,我将重新设计一套UI,区别于国内的天气UI,开搞~
运行效果图
正文
既然要访问国外的天气,那么肯定不能像国内这样,省、市、区/县这样来详细展示,所以只展示热门的城市,因为我相信那些去国外玩的朋友肯定也不会去一个不知名的地方吧,不然回来之后你怎么炫耀呢?
① API地址
和风天气有这方面的API,白嫖侠申请出战!地址如下
https://search.heweather.net/top?key=3086e91d66c04ce588a7f538f917c7f4&group=overseas&number=50&lang=zh
可以用浏览器测试一下
然后返回数据生成一个实体bean
在bean包下创建一个HotCityResponse,代码如下:
package com.llw.goodweather.bean;
import java.util.List;
/**
* 热门城市数据实体
*/
public class HotCityResponse {
private List<HeWeather6Bean> HeWeather6;
public List<HeWeather6Bean> getHeWeather6() {
return HeWeather6;
}
public void setHeWeather6(List<HeWeather6Bean> HeWeather6) {
this.HeWeather6 = HeWeather6;
}
public static class HeWeather6Bean {
private String status;
private List<BasicBean> basic;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public List<BasicBean> getBasic() {
return basic;
}
public void setBasic(List<BasicBean> basic) {
this.basic = basic;
}
public static class BasicBean {
/**
* cid : FR2988507
* location : 巴黎
* parent_city : 巴黎
* admin_area : 法兰西岛大区
* cnty : 法国
* lat : 48.85694504
* lon : 2.35138893
* tz : +1.00
* type : city
*/
private String cid;
private String location;
private String parent_city;
private String admin_area;
private String cnty;
private String lat;
private String lon;
private String tz;
private String type;
public String getCid() {
return cid;
}
public void setCid(String cid) {
this.cid = cid;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public String getParent_city() {
return parent_city;
}
public void setParent_city(String parent_city) {
this.parent_city = parent_city;
}
public String getAdmin_area() {
return admin_area;
}
public void setAdmin_area(String admin_area) {
this.admin_area = admin_area;
}
public String getCnty() {
return cnty;
}
public void setCnty(String cnty) {
this.cnty = cnty;
}
public String getLat() {
return lat;
}
public void setLat(String lat) {
this.lat = lat;
}
public String getLon() {
return lon;
}
public void setLon(String lon) {
this.lon = lon;
}
public String getTz() {
return tz;
}
public void setTz(String tz) {
this.tz = tz;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
}
}
然后打开ApiService.java
/**
* 海外热门城市
*/
@GET("/top?key=3086e91d66c04ce588a7f538f917c7f4&group=overseas&number=50&lang=zh")
Call<HotCityResponse> hotCity();
② 热门城市订阅器
在contract包下创建HotCityContract
代码如下:
package com.llw.goodweather.contract;
import android.content.Context;
import com.llw.goodweather.api.ApiService;
import com.llw.goodweather.bean.HotCityResponse;
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 HotCityContract {
public static class HotCityPresenter extends BasePresenter<IHotCityView> {
/**
* 热门城市城市 - 海外
* @param context
*/
public void hotCity(final Context context) {
ApiService service = ServiceGenerator.createService(ApiService.class, 2);//指明访问的地址
service.hotCity().enqueue(new NetCallBack<HotCityResponse>() {
@Override
public void onSuccess(Call<HotCityResponse> call, Response<HotCityResponse> response) {
if(getView() != null){
getView().getHotCityResult(response);
}
}
@Override
public void onFailed() {
if(getView() != null){
getView().getDataFailed();
}
}
});
}
}
public interface IHotCityView extends BaseView {
//热门城市返回数据
void getHotCityResult(Response<HotCityResponse> response);
//错误返回
void getDataFailed();
}
}
接下来就要创建一个新的Activity,用于展示热门城市的数据
③ 创建Activity并设计布局
接下来就可以设计布局,再此之前,要放一些需要的东西,
首先在mvplibrary里增加一些颜色
<color name="orange">#F38A50</color><!--橘色-->
<color name="shallow_orange">#FFEFD5</color><!--浅橘色-->
<color name="black_3">#454545</color><!--黑色3-->
<color name="gray">#BABABA</color><!--灰色-->
<color name="pink">#FFBCB3</color><!--粉色-->
<color name="pink_one">#FDEBE8</color><!--浅粉色-->
然后是图标,一个小飞机图片
下面是图片
上面是图片
因为是白色,你看不见很正常,保存到本地就可以了。
然后是样式,一个渐变的圆角背景
在app的drawable下创建shape_orange_8.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="8dp" />
<gradient
android:startColor="@color/orange"
android:centerColor="@color/orange"
android:endColor="#FDC03D"
android:angle="225" />
</shape>
现在可以创建布局了
app下面的layout下创建item_hot_city_list.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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.cardview.widget.CardView
android:id="@+id/item_hot_city"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/dp_6"
android:foreground="@drawable/bg_white"
app:cardBackgroundColor="@color/white"
app:cardCornerRadius="@dimen/dp_8"
app:cardElevation="@dimen/dp_4">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:layout_width="@dimen/dp_80"
android:layout_height="@dimen/dp_80"
android:background="@drawable/shape_orange_8"
android:gravity="center"
android:padding="@dimen/dp_20"
android:src="@mipmap/icon_hot_city" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical"
android:orientation="vertical"
android:paddingLeft="@dimen/dp_16">
<TextView
android:id="@+id/tv_hot_city_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="巴黎"
android:textColor="@color/black_3"
android:textSize="@dimen/sp_16"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_cnty_and_area"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_8"
android:text="巴黎"
android:textColor="@color/gray"
android:textSize="@dimen/sp_14"
android:textStyle="bold" />
</LinearLayout>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="@dimen/dp_12"
android:src="@mipmap/icon_open_orange" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
然后修改HotCityActivity对应的activity_hot_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:layout_width="match_parent"
android:fitsSystemWindows="true"
android:background="@color/shallow_orange"
android:orientation="vertical"
android:layout_height="match_parent"
tools:context=".ui.HotCityActivity">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/orange"
app:layout_constraintEnd_toEndOf="parent"
app:navigationIcon="@mipmap/icon_return_white"
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/white"
android:text="海外热门城市" />
</androidx.appcompat.widget.Toolbar>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
④ 创建热门城市列表适配及数据渲染
在adapter包下创建HotCityAdapter.java
代码如下:
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.HotCityResponse;
import java.util.ArrayList;
import java.util.List;
/**
* 热门城市列表适配器
*/
public class HotCityAdapter extends BaseQuickAdapter<HotCityResponse.HeWeather6Bean.BasicBean, BaseViewHolder> {
public HotCityAdapter(int layoutResId, @Nullable List<HotCityResponse.HeWeather6Bean.BasicBean> data) {
super(layoutResId, data);
}
@Override
protected void convert(BaseViewHolder helper, HotCityResponse.HeWeather6Bean.BasicBean item) {
helper.setText(R.id.tv_hot_city_name,item.getLocation())
.setText(R.id.tv_cnty_and_area,item.getCnty()+" —— "+item.getAdmin_area());
helper.addOnClickListener(R.id.item_hot_city);
}
}
然后就是到HotCityActivity里去使用了
@BindView(R.id.toolbar)
Toolbar toolbar;//标题bar
@BindView(R.id.rv)
RecyclerView rv;//列表
List<HotCityResponse.HeWeather6Bean.BasicBean> mList = new ArrayList<>();
HotCityAdapter mAdapter;
连接之前创建好的订阅,需要实现五个方法。
initData
@Override
public void initData(Bundle savedInstanceState) {
showLoadingDialog();//加载弹窗
StatusBarUtil.setStatusBarColor(context, R.color.orange);//白色状态栏
Back(toolbar);//返回
initList();//初始化列表数据
}
getLayoutId
@Override
public int getLayoutId() {
return R.layout.activity_hot_city;
}
createPresent
@Override
protected HotCityContract.HotCityPresenter createPresent() {
return new HotCityContract.HotCityPresenter();
}
getHotCityResult
//返回数据处理
@Override
public void getHotCityResult(Response<HotCityResponse> response) {
dismissLoadingDialog();
if (("ok").equals(response.body().getHeWeather6().get(0).getStatus())) {
//数据赋值
if(response.body().getHeWeather6().get(0).getBasic()!=null && response.body().getHeWeather6().get(0).getBasic().size()>0){
mList.clear();
mList.addAll(response.body().getHeWeather6().get(0).getBasic());
mAdapter.notifyDataSetChanged();
runLayoutAnimation(rv);//刷新适配器
}else {
ToastUtils.showShortToast(context,"数据为空");
}
} else {
ToastUtils.showShortToast(context, response.body().getHeWeather6().get(0).getStatus());
}
}
getDataFailed
//异常返回
@Override
public void getDataFailed() {
dismissLoadingDialog();
ToastUtils.showShortToast(context, "请求超时");
}
还有一个initList
private void initList() {
mAdapter = new HotCityAdapter(R.layout.item_hot_city_list,mList);
rv.setLayoutManager(new LinearLayoutManager(context));
rv.setAdapter(mAdapter);
//item 点击事件
mAdapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
@Override
public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
//点击之后的业务逻辑处理
}
});
mPresent.hotCity(context);
}
将原来的onCreate方法删掉,现在这个页面的代码是写完了,但是从哪里进来呢,这个入口还没有写呢,修改window_add.xml
增加的代码如下:
<TextView
android:id="@+id/tv_hot_city"
android:gravity="center"
android:layout_width="@dimen/dp_140"
android:layout_height="@dimen/dp_48"
android:text="热门城市"
android:foreground="@drawable/bg_white"
android:textColor="@color/black"
android:textSize="@dimen/sp_16"/>
然后进入MainActivity找到showAddWindow方法,增加
然后运行起来
数据就加载出来了。
⑤ 热门城市的天气信息展示
这里就需要创建一个新的页面了,因为我希望区别于MainActivity的样式。
在ui包下创建一个新的Activity,名为HotCityWeatherActivity.java,对应的xml文件是activity_hot_city_weather.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:layout_width="match_parent"
android:orientation="vertical"
android:fitsSystemWindows="true"
android:layout_height="match_parent"
tools:context=".ui.HotCityWeatherActivity">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_50"
android:background="@color/pink"
app:layout_constraintEnd_toEndOf="parent"
app:navigationIcon="@mipmap/icon_return_white"
app:contentInsetLeft="@dimen/dp_16"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:popupTheme="@style/AppTheme.PopupOverlay">
</androidx.appcompat.widget.Toolbar>
<LinearLayout
android:orientation="vertical"
android:gravity="center_horizontal"
android:background="@color/pink"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/dp_20">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textSize="@dimen/sp_24"
android:textColor="@color/white"
android:text="城市" />
<!--温度-->
<RelativeLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_temperature"
android:layout_centerHorizontal="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textColor="@color/white"
android:textSize="72sp" />
<TextView
android:layout_toRightOf="@+id/tv_temperature"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="℃"
android:textColor="@color/white"
android:textSize="24sp" />
</RelativeLayout>
<LinearLayout
android:gravity="center"
android:layout_marginTop="12dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<!--气候图标-->
<ImageView
android:id="@+id/iv_weather_state"
android:scaleType="fitXY"
android:background="@mipmap/icon_100"
android:layout_width="@dimen/dp_24"
android:layout_height="@dimen/dp_24"/>
<!--最高温和最低温-->
<TextView
android:id="@+id/tv_tem_max"
android:textColor="@color/white"
android:textSize="@dimen/sp_14"
android:layout_marginLeft="@dimen/dp_8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/tv_tem_min"
android:textColor="@color/white"
android:textSize="@dimen/sp_14"
android:alpha="0.5"
android:layout_marginLeft="@dimen/dp_8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
然后创建一个订阅器
代码如下:
package com.llw.goodweather.contract;
import android.content.Context;
import com.llw.goodweather.api.ApiService;
import com.llw.goodweather.bean.HotCityResponse;
import com.llw.goodweather.bean.WeatherResponse;
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 HotCityWeatherContract {
public static class HotCityPresenter extends BasePresenter<IHotCityView> {
/**
* 天气所有数据
* @param context
* @param location
*/
public void weatherData(final Context context,String location){
ApiService service = ServiceGenerator.createService(ApiService.class,0);
service.weatherData(location).enqueue(new NetCallBack<WeatherResponse>() {
@Override
public void onSuccess(Call<WeatherResponse> call, Response<WeatherResponse> response) {
if(getView() != null){
getView().getWeatherDataResult(response);
}
}
@Override
public void onFailed() {
if(getView() != null){
getView().getDataFailed();
}
}
});
}
}
public interface IHotCityView extends BaseView {
//查询天气所有数据
void getWeatherDataResult(Response<WeatherResponse> response);
//错误返回
void getDataFailed();
}
}
然后修改HotCityWeatherActivity页面的代码
首先衔接这个订阅器
然后绑定控件
@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;//最低温
相应实现那五个方法,这里我就一起贴出来
@Override
public void initData(Bundle savedInstanceState) {
showLoadingDialog();
StatusBarUtil.setStatusBarColor(context, R.color.pink);//设置状态栏背景颜色
Back(toolbar);
String location = getIntent().getStringExtra("location");
mPresent.weatherData(context, location);
}
@Override
public int getLayoutId() {
return R.layout.activity_hot_city_weather;
}
@Override
protected HotCityWeatherContract.HotCityPresenter createPresent() {
return new HotCityWeatherContract.HotCityPresenter();
}
//天气信息数据返回
@Override
public void getWeatherDataResult(Response<WeatherResponse> response) {
if (("ok").equals(response.body().getHeWeather6().get(0).getStatus())) {
if (response.body().getHeWeather6().get(0).getBasic() != null) {//得到数据不为空则进行数据显示
//基本天气信息
WeatherResponse.HeWeather6Bean.NowBean nowBean = response.body().getHeWeather6().get(0).getNow();
tvTitle.setText(response.body().getHeWeather6().get(0).getBasic().getLocation());
Typeface typeface = Typeface.createFromAsset(getAssets(), "fonts/Roboto-Light.ttf");
tvTemperature.setText(nowBean.getTmp());
tvTemperature.setTypeface(typeface);//使用字体
int code = Integer.parseInt(nowBean.getCond_code());//获取天气状态码,根据状态码来显示图标
WeatherUtil.changeIcon(ivWeatherState, code);//调用工具类中写好的方法
//最低温和最高温
tvTemMax.setText(response.body().getHeWeather6().get(0).getDaily_forecast().get(0).getTmp_max());
tvTemMin.setText(" / "+response.body().getHeWeather6().get(0).getDaily_forecast().get(0).getTmp_min()+ " ℃");
dismissLoadingDialog();
} else {
ToastUtils.showShortToast(context, CodeToStringUtils.WeatherCode(response.body().getHeWeather6().get(0).getStatus()));
}
}
}
//异常返回
@Override
public void getDataFailed() {
dismissLoadingDialog();
ToastUtils.showShortToast(context, "请求超时");
}
在getWeatherDataResult里面用到了字体样式,这里放上链接可以直接下载,点击文件提取链接,文件提取码 kn72。下载之后赋值到assets下
然后要进入热门城市的天气页面还得在列表页面做item的点击事件处理才行,打开HotCityActivity
Intent intent = new Intent(context,HotCityWeatherActivity.class);
intent.putExtra("location",mList.get(position).getLocation());
startActivity(intent);
运行一下
⑥ TabLayout+ViewPager+Fragment使用
然后要加入TabLayout和ViewPager,通过viewPager来切换不同的Fragment,这里我添加了一个第三方的库,效果比较好,微博的顶部TabLayout切换的效果和这个是一样的。
在mvplibrary下的build.gradle的dependencies中加入如下图的依赖
//蠕虫蠕动动画TabLayout
api 'com.ogaclejapan.smarttablayout:library:2.0.0@aar'
//Optional: see how to use the utility.
api 'com.ogaclejapan.smarttablayout:utils-v4:2.0.0@aar'
然后Sync同步一下,
接下来就是修改activity_hot_city_weather.xml布局
<!--动画TabLayout-->
<com.ogaclejapan.smarttablayout.SmartTabLayout
android:id="@+id/tab"
android:layout_width="match_parent"
android:layout_height="48dp"
app:stl_indicatorAlwaysInCenter="false"
app:stl_indicatorWithoutPadding="false"
app:stl_indicatorInFront="false"
app:stl_indicatorInterpolation="smart"
app:stl_indicatorGravity="bottom"
app:stl_indicatorColor="@color/pink"
app:stl_indicatorThickness="4dp"
app:stl_indicatorWidth="auto"
app:stl_dividerColor="@color/transparent"
app:stl_indicatorCornerRadius="2dp"
app:stl_overlineColor="@color/transparent"
app:stl_overlineThickness="0dp"
app:stl_underlineColor="@color/transparent"
app:stl_underlineThickness="1dp"
app:stl_defaultTabBackground="@color/transparent"
app:stl_defaultTabTextAllCaps="false"
app:stl_defaultTabTextColor="#FC000000"
app:stl_defaultTabTextSize="12sp"
app:stl_defaultTabTextHorizontalPadding="16dp"
app:stl_defaultTabTextMinWidth="0dp"
app:stl_distributeEvenly="false"
app:stl_clickable="true"
app:stl_titleOffset="24dp"
app:stl_drawDecorationAfterTab="false"
/>
<!--ViewPager-->
<androidx.viewpager.widget.ViewPager
android:id="@+id/vp"
android:layout_width="match_parent"
android:layout_height="match_parent" />
然后创建两个Fragment和两个布局文件
我特别新建了一个fragment包来存放ForecastFragment和TodayFragment。然后是对应的布局文件
fragment_forecast.xml和fragment_today.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"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>
这个时候进入ForecastFragment修改代码,继承BaseFragment,然后设置视图
public class ForecastFragment extends BaseFragment {
@Override
public void initData(Bundle savedInstanceState) {
}
@Override
public int getLayoutId() {
return R.layout.fragment_forecast;
}
}
然后是TodayFragment
public class TodayFragment extends BaseFragment {
@Override
public void initData(Bundle savedInstanceState) {
}
@Override
public int getLayoutId() {
return R.layout.fragment_today;
}
}
Fragment都准备好了,然后就是在HotCityWeatherActivity使用了,之前已经在布局中添加过视图了,现在就是绑定视图
@BindView(R.id.tab)
SmartTabLayout tab;//标签视图
@BindView(R.id.vp)
ViewPager vp;
然后创建一个方法用来装载两个Fragment
private void initView() {
FragmentPagerItemAdapter adapter = new FragmentPagerItemAdapter(
getSupportFragmentManager(), FragmentPagerItems.with(this)
.add("Today", TodayFragment.class)
.add("Forecast", ForecastFragment.class)
.create());
vp.setAdapter(adapter);
tab.setViewPager(vp);
}
最后就是在InitData中调用initView了
运行一下
这个时候你就可以左右滑动或者是点击tab来切换不同的Fragment
⑦ Activity和Fragment通讯
这里我们使用EventBus来进行通讯,主要原因就是代码量少,页面简洁
在eventbus包下创建ForecastEvent和TodayHourlyEvent,代码如下:
TodayHourlyEvent.java
package com.llw.goodweather.eventbus;
import com.llw.goodweather.bean.WeatherResponse;
import java.util.List;
/**
* 热门城市当天逐三小时天气信息事件
*/
public class TodayHourlyEvent {
public List<WeatherResponse.HeWeather6Bean.HourlyBean> mHourlyBean;
public TodayHourlyEvent(List<WeatherResponse.HeWeather6Bean.HourlyBean> hourlyBean) {
this.mHourlyBean = hourlyBean;
}
}
ForecastEvent.java
package com.llw.goodweather.eventbus;
import com.llw.goodweather.bean.WeatherResponse;
import java.util.List;
/**
* 热门城市天气预报事件
*/
public class ForecastEvent {
public List<WeatherResponse.HeWeather6Bean.DailyForecastBean> mForecastBean;
public ForecastEvent(List<WeatherResponse.HeWeather6Bean.DailyForecastBean> forecastBean) {
this.mForecastBean = forecastBean;
}
}
事件写好了,接下来就是使用,首先在HotCityWeatherActivity页面中的getWeatherDataResult方法中传递这两个事件
//传递数据到TodayFragment和ForcastFragment
EventBus.getDefault().post(new TodayHourlyEvent(response.body().getHeWeather6().get(0).getHourly()));
EventBus.getDefault().post(new ForecastEvent(response.body().getHeWeather6().get(0).getDaily_forecast()));
然后到两个Fragment里面去接收
使用之前要先注册,如果不是第一次使用的话,就要先判断是否注册过,判断的业务逻辑写在initData里面,同时销毁的时候也要判断
如下所示
TodayFragment
@Override
public void initData(Bundle savedInstanceState) {
if (!EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().register(this);
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
if (EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().unregister(this);
}
}
然后才是接收传递过来的消息
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(TodayHourlyEvent event) {//接收
ToastUtils.showShortToast(context, "有" + event.mHourlyBean.size() + "条数据");
}
同样的代码在ForecastFragment也只是接收的代码不一样而已,我这里贴一下
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(ForecastEvent event) {//接收
ToastUtils.showShortToast(context, "有" + event.mForecastBean.size() + "条数据");
}
你可以再运行一下,当你的页面出现两个Toast的时候就说明通讯成功了,这里图我就不贴了。
⑧ 温度折线图
温度折线图,这个地方需要使用Android的图表,我从网络上找了一个自定义的控件,然后再加以改动就符合自己使用的业务场景了,同样这个自定义VIew也是放在mvplibrary下面的。第一步当然是创建样式了。
这应该一目了然吧。
样式代码:
<!--折线图-->
<declare-styleable name="WeatherChartView">
<attr name="textColor" format="color"/>
<attr name="dayTextColor" format="color"/>
<attr name="nightTextColor" format="color"/>
<attr name="dayColor" format="color"/>
<attr name="nightColor" format="color"/>
<attr name="textSize" format="dimension"/>
</declare-styleable>
这里创建了两个View,用于应对不同的业务需求
WeatherChartView.java
package com.llw.mvplibrary.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import com.llw.mvplibrary.R;
/**
* 温度折线
*/
public class WeatherChartView extends View {
/**
* x轴集合
*/
private float mXAxis[] = new float[8];
/**
* 白天y轴集合
*/
private float mYAxisDay[] = new float[8];
/**
* 夜间y轴集合
*/
private float mYAxisNight[] = new float[8];
/**
* x,y轴集合数
*/
private static final int LENGTH = 8;
/**
* 白天温度集合
*/
private int mTempDay[] = new int[8];
/**
* 夜间温度集合
*/
private int mTempNight[] = new int[8];
/**
* 控件高
*/
private int mHeight;
/**
* 字体大小
*/
private float mTextSize;
/**
* 圓半径
*/
private float mRadius;
/**
* 圓半径今天
*/
private float mRadiusToday;
/**
* 文字移动位置距离
*/
private float mTextSpace;
/**
* 白天折线颜色
*/
private int mColorDay;
/**
* 夜间折线颜色
*/
private int mColorNight;
/**
* 屏幕密度
*/
private float mDensity;
/**
* 控件边的空白空间
*/
private float mSpace;
/**
* 线画笔
*/
private Paint mLinePaint;
/**
* 点画笔
*/
private Paint mPointPaint;
/**
* 字体画笔
*/
private Paint mTextPaint;
public WeatherChartView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
@SuppressWarnings("deprecation")
private void init(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WeatherChartView);
float densityText = getResources().getDisplayMetrics().scaledDensity;
mTextSize = a.getDimensionPixelSize(R.styleable.WeatherChartView_textSize,
(int) (14 * densityText));
mColorDay = a.getColor(R.styleable.WeatherChartView_dayColor,
getResources().getColor(R.color.pink));
mColorNight = a.getColor(R.styleable.WeatherChartView_nightColor,
getResources().getColor(R.color.blue_one));
int textColor = a.getColor(R.styleable.WeatherChartView_textColor, Color.WHITE);
a.recycle();
mDensity = getResources().getDisplayMetrics().density;
mRadius = 3 * mDensity;
mRadiusToday = 5 * mDensity;
mSpace = 3 * mDensity;
mTextSpace = 10 * mDensity;
float stokeWidth = 2 * mDensity;
mLinePaint = new Paint();
mLinePaint.setAntiAlias(true);
mLinePaint.setStrokeWidth(stokeWidth);
mLinePaint.setStyle(Paint.Style.STROKE);
mPointPaint = new Paint();
mPointPaint.setAntiAlias(true);
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setColor(textColor);
mTextPaint.setTextSize(mTextSize);
mTextPaint.setTextAlign(Paint.Align.CENTER);
}
public WeatherChartView(Context context) {
super(context);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mHeight == 0) {
// 设置控件高度,x轴集合
setHeightAndXAxis();
}
// 计算y轴集合数值
computeYAxisValues();
if (mTempDay[0] != 0 && mTempDay[7] != 0) {
// 画白天折线图
drawChart(canvas, mColorDay, mTempDay, mYAxisDay, 0);
}
if (mTempNight[0] != 0 && mTempNight[7] != 0) {
// 画夜间折线图
drawChart(canvas, mColorNight, mTempNight, mYAxisNight, 1);
}
}
/**
* 计算y轴集合数值
*/
private void computeYAxisValues() {
// 存放白天最低温度
int minTempDay = mTempDay[0];
// 存放白天最高温度
int maxTempDay = mTempDay[0];
for (int item : mTempDay) {
if (item < minTempDay) {
minTempDay = item;
}
if (item > maxTempDay) {
maxTempDay = item;
}
}
// 存放夜间最低温度
int minTempNight = mTempNight[0];
// 存放夜间最高温度
int maxTempNight = mTempNight[0];
for (int item : mTempNight) {
if (item < minTempNight) {
minTempNight = item;
}
if (item > maxTempNight) {
maxTempNight = item;
}
}
// 白天,夜间中的最低温度
int minTemp = minTempNight < minTempDay ? minTempNight : minTempDay;
// 白天,夜间中的最高温度
int maxTemp = maxTempDay > maxTempNight ? maxTempDay : maxTempNight;
// 份数(白天,夜间综合温差)
float parts = maxTemp - minTemp;
// y轴一端到控件一端的距离
float length = mSpace + mTextSize + mTextSpace + mRadius;
// y轴高度
float yAxisHeight = mHeight - length * 2;
// 当温度都相同时(被除数不能为0)
if (parts == 0) {
for (int i = 0; i < LENGTH; i++) {
mYAxisDay[i] = yAxisHeight / 2 + length;
mYAxisNight[i] = yAxisHeight / 2 + length;
}
} else {
float partValue = yAxisHeight / parts;
for (int i = 0; i < LENGTH; i++) {
mYAxisDay[i] = mHeight - partValue * (mTempDay[i] - minTemp) - length;
mYAxisNight[i] = mHeight - partValue * (mTempNight[i] - minTemp) - length;
}
}
}
/**
* 画折线图
*
* @param canvas 画布
* @param color 画图颜色
* @param temp 温度集合
* @param yAxis y轴集合
* @param type 折线种类:0,白天;1,夜间
*/
private void drawChart(Canvas canvas, int color, int temp[], float[] yAxis, int type) {
mLinePaint.setColor(color);
mPointPaint.setColor(color);
int alpha1 = 102;
int alpha2 = 255;
for (int i = 0; i < LENGTH; i++) {
// 画线
if (i < LENGTH - 1) {
mLinePaint.setAlpha(alpha2);
mLinePaint.setPathEffect(null);
canvas.drawLine(mXAxis[i], yAxis[i], mXAxis[i + 1], yAxis[i + 1], mLinePaint);
}
// 画点
mPointPaint.setAlpha(alpha2);
canvas.drawCircle(mXAxis[i], yAxis[i], mRadiusToday, mPointPaint);
// 画字
mTextPaint.setAlpha(alpha2);
drawText(canvas, mTextPaint, i, temp, yAxis, type);
}
}
/**
* 绘制文字
*
* @param canvas 画布
* @param textPaint 画笔
* @param i 索引
* @param temp 温度集合
* @param yAxis y轴集合
* @param type 折线种类:0,白天;1,夜间
*/
private void drawText(Canvas canvas, Paint textPaint, int i, int[] temp, float[] yAxis, int type) {
switch (type) {
case 0:
// 显示白天气温
canvas.drawText(temp[i] + "°", mXAxis[i], yAxis[i] - mRadius - mTextSpace, textPaint);
break;
case 1:
// 显示夜间气温
canvas.drawText(temp[i] + "°", mXAxis[i], yAxis[i] + mTextSpace + mTextSize, textPaint);
break;
}
}
/**
* 设置高度,x轴集合
*/
private void setHeightAndXAxis() {
mHeight = getHeight();
// 控件宽
int width = getWidth();
// 每一份宽
float w = width / 16;
mXAxis[0] = w;
mXAxis[1] = w * 3;
mXAxis[2] = w * 5;
mXAxis[3] = w * 7;
mXAxis[4] = w * 9;
mXAxis[5] = w * 11;
mXAxis[6] = w * 13;
mXAxis[7] = w * 15;
}
/**
* 设置白天温度
*
* @param tempDay 温度数组集合
*/
public void setTempDay(int[] tempDay) {
mTempDay = tempDay;
}
/**
* 设置夜间温度
*
* @param tempNight 温度数组集合
*/
public void setTempNight(int[] tempNight) {
mTempNight = tempNight;
}
}
WeatherChartViewForecast.java
package com.llw.mvplibrary.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import com.llw.mvplibrary.R;
/**
* 折线温度双曲线
*/
public class WeatherChartViewForecast extends View {
/**
* x轴集合
*/
private float mXAxis[] = new float[7];
/**
* 白天y轴集合
*/
private float mYAxisDay[] = new float[7];
/**
* 夜间y轴集合
*/
private float mYAxisNight[] = new float[7];
/**
* x,y轴集合数
*/
private static final int LENGTH = 7;
/**
* 白天温度集合
*/
private int mTempMax[] = new int[7];
/**
* 夜间温度集合
*/
private int mTempMin[] = new int[7];
/**
* 控件高
*/
private int mHeight;
/**
* 字体大小
*/
private float mTextSize;
/**
* 圓半径
*/
private float mRadius;
/**
* 圓半径今天
*/
private float mRadiusToday;
/**
* 文字移动位置距离
*/
private float mTextSpace;
/**
* 白天折线颜色
*/
private int mColorDay;
/**
* 白天文字颜色
*/
private int mColorTextDay;
/**
* 夜间折线颜色
*/
private int mColorNight;
/**
* 夜间文字颜色
*/
private int mColorTextNight;
/**
* 屏幕密度
*/
private float mDensity;
/**
* 控件边的空白空间
*/
private float mSpace;
/**
* 线画笔
*/
private Paint mLinePaint;
/**
* 点画笔
*/
private Paint mPointPaint;
/**
* 字体画笔
*/
private Paint mTextPaint;
public WeatherChartViewForecast(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
@SuppressWarnings("deprecation")
private void init(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WeatherChartView);
float densityText = getResources().getDisplayMetrics().scaledDensity;
mTextSize = a.getDimensionPixelSize(R.styleable.WeatherChartView_textSize,
(int) (14 * densityText));
mColorDay = a.getColor(R.styleable.WeatherChartView_dayColor,
getResources().getColor(R.color.pink));
mColorNight = a.getColor(R.styleable.WeatherChartView_nightColor,
getResources().getColor(R.color.blue_one));
mColorTextDay = a.getColor(R.styleable.WeatherChartView_dayTextColor,
getResources().getColor(R.color.pink));
mColorTextNight = a.getColor(R.styleable.WeatherChartView_nightTextColor,
getResources().getColor(R.color.blue_one));
mDensity = getResources().getDisplayMetrics().density;
mRadius = 3 * mDensity;
mRadiusToday = 5 * mDensity;
mSpace = 3 * mDensity;
mTextSpace = 10 * mDensity;
float stokeWidth = 2 * mDensity;
mLinePaint = new Paint();
mLinePaint.setAntiAlias(true);
mLinePaint.setStrokeWidth(stokeWidth);
mLinePaint.setStyle(Paint.Style.STROKE);
mPointPaint = new Paint();
mPointPaint.setAntiAlias(true);
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(mTextSize);
mTextPaint.setTextAlign(Paint.Align.CENTER);
}
public WeatherChartViewForecast(Context context) {
super(context);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mHeight == 0) {
// 设置控件高度,x轴集合
setHeightAndXAxis();
}
// 计算y轴集合数值
computeYAxisValues();
if (mTempMax[0] != 0 && mTempMax[6] != 0) {
// 画最高温折线图
drawChart(canvas, mColorDay, mTempMax, mYAxisDay, 0);
}
if (mTempMin[0] != 0 && mTempMin[6] != 0) {
// 画最低温折线图
drawChart(canvas, mColorNight, mTempMin, mYAxisNight, 1);
}
}
/**
* 计算y轴集合数值
*/
private void computeYAxisValues() {
// 存放最高温中的最低温度
int minTempDay = mTempMax[0];
// 存放最高温中的最高温度
int maxTempDay = mTempMax[0];
for (int item : mTempMax) {
if (item < minTempDay) {
minTempDay = item;
}
if (item > maxTempDay) {
maxTempDay = item;
}
}
// 最低温中的最低温度
int minTempNight = mTempMin[0];
// 最低温中的最高温度
int maxTempNight = mTempMin[0];
for (int item : mTempMin) {
if (item < minTempNight) {
minTempNight = item;
}
if (item > maxTempNight) {
maxTempNight = item;
}
}
// 白天,夜间中的最低温度
int minTemp = minTempNight < minTempDay ? minTempNight : minTempDay;
// 白天,夜间中的最高温度
int maxTemp = maxTempDay > maxTempNight ? maxTempDay : maxTempNight;
// 份数(白天,夜间综合温差)
float parts = maxTemp - minTemp;
// y轴一端到控件一端的距离
float length = mSpace + mTextSize + mTextSpace + mRadius;
// y轴高度
float yAxisHeight = mHeight - length * 2;
// 当温度都相同时(被除数不能为0)
if (parts == 0) {
for (int i = 0; i < LENGTH; i++) {
mYAxisDay[i] = yAxisHeight / 2 + length;
mYAxisNight[i] = yAxisHeight / 2 + length;
}
} else {
float partValue = yAxisHeight / parts;
for (int i = 0; i < LENGTH; i++) {
mYAxisDay[i] = mHeight - partValue * (mTempMax[i] - minTemp) - length;
mYAxisNight[i] = mHeight - partValue * (mTempMin[i] - minTemp) - length;
}
}
}
/**
* 画折线图
*
* @param canvas 画布
* @param color 画图颜色
* @param temp 温度集合
* @param yAxis y轴集合
* @param type 折线种类:0,最高温;1,最低温
*/
private void drawChart(Canvas canvas, int color, int temp[], float[] yAxis, int type) {
mLinePaint.setColor(color);
mPointPaint.setColor(color);
int alpha1 = 102;
int alpha2 = 255;
for (int i = 0; i < LENGTH; i++) {
// 画线
if (i < LENGTH - 1) {
mLinePaint.setAlpha(alpha2);
mLinePaint.setPathEffect(null);
canvas.drawLine(mXAxis[i], yAxis[i], mXAxis[i + 1], yAxis[i + 1], mLinePaint);
}
// 画点
mPointPaint.setAlpha(alpha2);
canvas.drawCircle(mXAxis[i], yAxis[i], mRadiusToday, mPointPaint);
// 画字
mTextPaint.setAlpha(alpha2);
drawText(canvas, mTextPaint, i, temp, yAxis, type);
}
}
/**
* 绘制文字
*
* @param canvas 画布
* @param textPaint 画笔
* @param i 索引
* @param temp 温度集合
* @param yAxis y轴集合
* @param type 折线种类:0,白天;1,夜间
*/
private void drawText(Canvas canvas, Paint textPaint, int i, int[] temp, float[] yAxis, int type) {
switch (type) {
case 0:
// 显示白天气温
textPaint.setColor(mColorTextDay);
canvas.drawText(temp[i] + "°", mXAxis[i], yAxis[i] - mRadius - mTextSpace, textPaint);
break;
case 1:
// 显示夜间气温
textPaint.setColor(mColorTextNight);
canvas.drawText(temp[i] + "°", mXAxis[i], yAxis[i] + mTextSpace + mTextSize, textPaint);
break;
}
}
/**
* 设置高度,x轴集合
*/
private void setHeightAndXAxis() {
mHeight = getHeight();
// 控件宽
int width = getWidth();
// 每一份宽
float w = width / 14;
mXAxis[0] = w;
mXAxis[1] = w * 3;
mXAxis[2] = w * 5;
mXAxis[3] = w * 7;
mXAxis[4] = w * 9;
mXAxis[5] = w * 11;
mXAxis[6] = w * 13;
}
/**
* 设置最高温度
* @param tempMax
*/
public void setTempMax(int[] tempMax) {
mTempMax = tempMax;
}
/**
* 设置最低温度
*/
public void setTempMin(int[] tempMin) {
mTempMin = tempMin;
}
}
⑨ 自定义数据列表
自定义View创建好了,页面可不能只有一个折线图,那样太空了,所以还需要一个列表来展示,两个Fragment对应不同的列表样式。
首先创建一个圆角背景
shape_pink_8.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="8dp" />
<solid android:color="@color/pink" />
</shape>
接下里创建两个不同item布局文件
为了区别于之前创建的item,我特意在中间加了一个hot,表示这是热门城市数据列表的中item布局。
item_weather_forecast_hot_list.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/item_forecast"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/dp_12"
android:background="@drawable/shape_pink_8"
android:foreground="@drawable/bg_white"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="vertical"
android:padding="@dimen/dp_12">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_day"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="今天"
android:textColor="@color/white" />
<TextView
android:id="@+id/tv_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="日期"
android:textColor="@color/white" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_tem_max_min"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="最高温/最低温"
android:textColor="@color/white"
android:textSize="@dimen/sp_40" />
<TextView
android:text="°"
android:textColor="@color/white"
android:textSize="@dimen/sp_40"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
<TextView
android:id="@+id/tv_wind_dir_and_sc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_12"
android:text="风向风力"
android:textColor="@color/white"
android:textSize="@dimen/sp_14" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="@dimen/dp_6"
android:text="紫外线"
android:textColor="@color/pink_one"
android:textSize="@dimen/sp_14" />
<TextView
android:id="@+id/tv_uv_index"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="紫外线"
android:textColor="@color/white"
android:textSize="@dimen/sp_14" />
</LinearLayout>
<LinearLayout
android:layout_marginLeft="@dimen/dp_6"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="@dimen/dp_6"
android:text="相对湿度"
android:textColor="@color/pink_one"
android:textSize="@dimen/sp_14" />
<TextView
android:id="@+id/tv_hum"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="相对湿度"
android:textColor="@color/white"
android:textSize="@dimen/sp_14" />
</LinearLayout>
<LinearLayout
android:layout_marginLeft="@dimen/dp_6"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="@dimen/dp_6"
android:text="大气压强"
android:textColor="@color/pink_one"
android:textSize="@dimen/sp_14" />
<TextView
android:id="@+id/tv_pres"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="大气压强"
android:textColor="@color/white"
android:textSize="@dimen/sp_14" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
item_weather_hourly_hot_list.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:paddingLeft="@dimen/dp_16"
android:paddingRight="@dimen/dp_16"
android:paddingTop="@dimen/dp_8"
android:paddingBottom="@dimen/dp_8"
android:background="@drawable/shape_pink_8"
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!--时间-->
<TextView
android:id="@+id/tv_time"
android:textColor="#FFF"
android:text="上午10:00"
android:textSize="14sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<!--气候图标-->
<LinearLayout
android:gravity="center"
android:layout_width="@dimen/dp_0"
android:layout_height="wrap_content"
android:layout_weight="1">
<ImageView
android:id="@+id/iv_weather_state"
android:background="@mipmap/icon_100"
android:layout_width="30dp"
android:layout_height="30dp"/>
</LinearLayout>
<!--温度-->
<TextView
android:textSize="20sp"
android:id="@+id/tv_temperature"
android:textColor="#FFF"
android:text="25℃"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
两个item布局都写好了,接下来就是修改两个Fragment中绑定的布局了,之前他们里面都是空的。
fragment_today.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"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.llw.mvplibrary.view.WeatherChartView
android:layout_marginTop="@dimen/dp_20"
android:id="@+id/line_char"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_100"
android:layout_centerInParent="true"
app:dayColor="@color/pink"
app:nightColor="@color/blue_one"
app:textColor="@color/pink"
app:textSize="14sp"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_hourly"
android:layout_margin="@dimen/dp_16"
android:background="@drawable/shape_pink_8"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</LinearLayout>
fragment_forecast.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:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.llw.mvplibrary.view.WeatherChartViewForecast
android:layout_marginTop="@dimen/dp_20"
android:id="@+id/line_char"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_160"
android:layout_centerInParent="true"
app:dayColor="@color/pink"
app:nightColor="@color/blue_one"
app:dayTextColor="@color/pink"
app:nightTextColor="@color/blue_one"
app:textSize="14sp"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_forecast"
android:layout_margin="@dimen/dp_16"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</LinearLayout>
现在布局写好了,可以写代码了,这里会用到两个列表适配器,其中WeatherHourlyAdapter可以复用,不需要改动,但是另一个是需要重新写的。
WeatherForecastHotAdapter.java
package com.llw.goodweather.adapter;
import android.graphics.Typeface;
import android.widget.ImageView;
import android.widget.TextView;
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.WeatherResponse;
import com.llw.goodweather.utils.DateUtils;
import com.llw.goodweather.utils.WeatherUtil;
import java.util.List;
/**
* 热门城市天气预报列表展示适配器
*/
public class WeatherForecastHotAdapter extends BaseQuickAdapter<WeatherResponse.HeWeather6Bean.DailyForecastBean, BaseViewHolder> {
public WeatherForecastHotAdapter(int layoutResId, @Nullable List<WeatherResponse.HeWeather6Bean.DailyForecastBean> data) {
super(layoutResId, data);
}
@Override
protected void convert(BaseViewHolder helper, WeatherResponse.HeWeather6Bean.DailyForecastBean item) {
helper.setText(R.id.tv_date, item.getDate())//日期
.setText(R.id.tv_day, DateUtils.Week(item.getDate()))
.setText(R.id.tv_wind_dir_and_sc, item.getWind_dir() + item.getWind_sc() + "级")
.setText(R.id.tv_uv_index, uvIndex(Integer.parseInt(item.getUv_index())))//紫外线强度
.setText(R.id.tv_hum, item.getHum() + "%")//相对湿度
.setText(R.id.tv_pres, item.getPres() + "hPa")//大气压强
;
//最低温和最高温
TextView minAndMax = helper.getView(R.id.tv_tem_max_min);
minAndMax.setText(item.getTmp_min() + "/" + item.getTmp_max());
Typeface typeface = Typeface.createFromAsset(mContext.getAssets(), "fonts/Roboto-Light.ttf");
minAndMax.setTypeface(typeface);
helper.addOnClickListener(R.id.item_forecast);//绑定点击事件的id
}
//紫外线等级
private String uvIndex(int level) {//最弱、弱、中等、强、很强
String result = null;
if (level <= 2) {
result = "最弱";
} else if (level > 2 && level < 5) {
result = "弱";
} else if (level > 4 && level < 7) {
result = "中等";
} else if (level > 6 && level < 10) {
result = "强";
} else {
result = "很强";
}
return result;
}
}
那么现在可以在Fragment里面做数据的渲染展示了。
首先是TodayFragment
@BindView(R.id.line_char)
WeatherChartView lineChar;//折线图
@BindView(R.id.rv_hourly)
RecyclerView rvHourly;//逐三小时天气列表
List<WeatherResponse.HeWeather6Bean.HourlyBean> mList = new ArrayList<>();
WeatherHourlyAdapter mAdapter;
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(TodayHourlyEvent event) {//接收
List<WeatherResponse.HeWeather6Bean.HourlyBean> data = new ArrayList<>();
data.addAll(event.mHourlyBean);
Log.i("dayArray", data.get(0).getCond_txt());
int[] dayArray = new int[data.size()];
for (int i = 0; i < data.size(); i++) {
dayArray[i] = Integer.parseInt(event.mHourlyBean.get(i).getTmp());
}
Log.i("dayArray", dayArray.toString());
lineChar.setTempDay(dayArray);
lineChar.invalidate();
//列表
initList(data);
}
private void initList(List<WeatherResponse.HeWeather6Bean.HourlyBean> data) {
mAdapter = new WeatherHourlyAdapter(R.layout.item_weather_hourly_hot_list, mList);
rvHourly.setLayoutManager(new LinearLayoutManager(context));
rvHourly.setAdapter(mAdapter);
mList.clear();
mList.addAll(data);
mAdapter.notifyDataSetChanged();
}
将里面的Toast去掉,进行业务处理即可
然后是ForecastFragment
@BindView(R.id.line_char)
WeatherChartViewForecast lineChar;//折线图
@BindView(R.id.rv_forecast)
RecyclerView rvForecast;//未来七天天气预报
List<WeatherResponse.HeWeather6Bean.DailyForecastBean> mList = new ArrayList<>();
WeatherForecastHotAdapter mAdapter;
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(ForecastEvent event) {//接收
List<WeatherResponse.HeWeather6Bean.DailyForecastBean> data = new ArrayList<>();
data.addAll(event.mForecastBean);
int[] maxArray = new int[data.size()];
int[] minArray = new int[data.size()];
for (int i = 0; i < data.size(); i++) {
maxArray[i] = Integer.parseInt(event.mForecastBean.get(i).getTmp_max());
minArray[i] = Integer.parseInt(event.mForecastBean.get(i).getTmp_min());
}
lineChar.setTempMax(maxArray);
lineChar.setTempMin(minArray);
lineChar.invalidate();
//列表数据
initList(data);
}
private void initList(List<WeatherResponse.HeWeather6Bean.DailyForecastBean> data) {
mAdapter = new WeatherForecastHotAdapter(R.layout.item_weather_forecast_hot_list,mList);
rvForecast.setLayoutManager(new LinearLayoutManager(context));
rvForecast.setAdapter(mAdapter);
mList.clear();
mList.addAll(data);
mAdapter.notifyDataSetChanged();
}
OK,再运行一下
源码地址:GoodWeather
欢迎 Star 和 Fork