Android 天气APP(十六)热门城市 - 海外城市

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 {

/**

  • 热门城市城市 - 海外

  • @param context

*/

public void hotCity(final Context context) {

ApiService service = ServiceGenerator.createService(ApiService.class, 2);//指明访问的地址

service.hotCity().enqueue(new NetCallBack() {

@Override

public void onSuccess(Call call, Response response) {

if(getView() != null){

getView().getHotCityResult(response);

}

}

@Override

public void onFailed() {

if(getView() != null){

getView().getDataFailed();

}

}

});

}

}

public interface IHotCityView extends BaseView {

//热门城市返回数据

void getHotCityResult(Response response);

//错误返回

void getDataFailed();

}

}

接下来就要创建一个新的Activity,用于展示热门城市的数据

③ 创建Activity并设计布局


在这里插入图片描述

在这里插入图片描述

接下来就可以设计布局,再此之前,要放一些需要的东西,

首先在mvplibrary里增加一些颜色

在这里插入图片描述

#F38A50

#FFEFD5

#454545

#BABABA

#FFBCB3

#FDEBE8

然后是图标,一个小飞机图片

下面是图片

在这里插入图片描述

上面是图片

因为是白色,你看不见很正常,保存到本地就可以了。

然后是样式,一个渐变的圆角背景

在这里插入图片描述

在app的drawable下创建shape_orange_8.xml

<?xml version="1.0" encoding="utf-8"?>

<gradient

android:startColor=“@color/orange”

android:centerColor=“@color/orange”

android:endColor=“#FDC03D”

android:angle=“225” />

现在可以创建布局了

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” />

<ImageView

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_marginRight=“@dimen/dp_12”

android:src=“@mipmap/icon_open_orange” />

</androidx.cardview.widget.CardView>

然后修改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”/>

④ 创建热门城市列表适配及数据渲染


在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 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” />

<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”/>

然后创建一个订阅器

在这里插入图片描述

代码如下:

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 {

/**

  • 天气所有数据

  • @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() {

@Override

public void onSuccess(Call call, Response response) {

if(getView() != null){

getView().getWeatherDataResult(response);

}

}

@Override

public void onFailed() {

if(getView() != null){

getView().getDataFailed();

}

}

});

}

}

public interface IHotCityView extends BaseView {

//查询天气所有数据

void getWeatherDataResult(Response 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 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布局

在这里插入图片描述

<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”

/>

<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”>

这个时候进入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下面的。第一步当然是创建样式了。

在这里插入图片描述

这应该一目了然吧。

样式代码:

在这里插入图片描述

这里创建了两个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);

最后

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司21年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
aint.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);

最后

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司21年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

[外链图片转存中…(img-ybo8euBK-1714680493231)]

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 22
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值