}
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
这里我们使用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);
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"?>接下里创建两个不同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
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
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
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
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
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
Android高级架构师
由于篇幅问题,我呢也将自己当前所在技术领域的各项知识点、工具、框架等汇总成一份技术路线图,还有一些架构进阶视频、全套学习PDF文件、面试文档、源码笔记。
- 330页PDF Android学习核心笔记(内含上面8大板块)
-
Android学习的系统对应视频
-
Android进阶的系统对应学习资料
- Android BAT部分大厂面试题(有解析)
好了,以上便是今天的分享,希望为各位朋友后续的学习提供方便。觉得内容不错,也欢迎多多分享给身边的朋友哈。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
ut_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
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
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
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-7AJ54a1f-1712350852298)]
[外链图片转存中…(img-8kwg9AOh-1712350852299)]
[外链图片转存中…(img-W2OUqamt-1712350852299)]
[外链图片转存中…(img-bMuPAOqu-1712350852299)]
[外链图片转存中…(img-0D2cXdYS-1712350852300)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
Android高级架构师
由于篇幅问题,我呢也将自己当前所在技术领域的各项知识点、工具、框架等汇总成一份技术路线图,还有一些架构进阶视频、全套学习PDF文件、面试文档、源码笔记。
- 330页PDF Android学习核心笔记(内含上面8大板块)
[外链图片转存中…(img-7hAk6cwN-1712350852300)]
[外链图片转存中…(img-a6iUpsiZ-1712350852300)]
-
Android学习的系统对应视频
-
Android进阶的系统对应学习资料
[外链图片转存中…(img-3y4OYR9p-1712350852300)]
- Android BAT部分大厂面试题(有解析)
[外链图片转存中…(img-qDx5J22J-1712350852301)]
好了,以上便是今天的分享,希望为各位朋友后续的学习提供方便。觉得内容不错,也欢迎多多分享给身边的朋友哈。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!