Android 天气APP(十六)热门城市 - 海外城市,2024年Android社招面试题

}

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);

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移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合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)]

好了,以上便是今天的分享,希望为各位朋友后续的学习提供方便。觉得内容不错,也欢迎多多分享给身边的朋友哈。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值