Android 天气APP(二十七)增加地图天气的逐小时天气、太阳和月亮数据

import android.graphics.Paint;

import android.graphics.Path;

import android.graphics.Point;

import android.graphics.Rect;

import android.graphics.Shader;

import android.graphics.drawable.BitmapDrawable;

import android.os.Build;

import android.text.TextPaint;

import android.util.AttributeSet;

import android.util.DisplayMetrics;

import android.view.View;

import androidx.annotation.Nullable;

import androidx.annotation.RequiresApi;

import com.llw.goodweather.R;

import com.llw.goodweather.bean.HourlyResponse;

import com.llw.goodweather.utils.DisplayUtil;

import com.llw.goodweather.utils.IconUtils;

import java.util.ArrayList;

import java.util.Iterator;

import java.util.List;

/**

  • 24小时天气预报自定义View

*/

public class HourlyForecastView extends View implements ScrollWatcher {

private Context mContext;

//折线

private Paint foldLinePaint;

private Paint backPaint;

//底线

private Paint baseLinePaint;

//虚线

private Paint dashPaint;

//文字

private Paint textPaint;

//图片

private Paint bitmapPaint;

//文本的大小

private int textSize;

//数据

private List<HourlyResponse.HourlyBean> hourlyWeatherList;

//画虚线的点的index

private List dashLineList;

private int screenWidth;

//每个item的宽度

private int itemWidth;

//温度基准高度

private int lowestTempHeight;

//温度基准高度

private int highestTempHeight;

//最低温

private int lowestTemp;

//最高温

private int highestTemp;

//默认图片绘制位置

float bitmapHeight;

//默认图片宽高

float bitmapXY;

//View宽高

private int mWidth;

private int mHeight;

//默认高

private int defHeightPixel = 0;

private int defWidthPixel = 0;

private int paddingL = 0;

private int paddingT = 0;

private int paddingR = 0;

private int paddingB = 0;

private int mScrollX = 0;

private float baseLineHeight;

private Paint paint1;

private boolean isDark = false;

public HourlyForecastView(Context context) {

this(context, null);

}

public HourlyForecastView(Context context, @Nullable AttributeSet attrs) {

this(context, attrs, -1);

}

public HourlyForecastView(final Context context, @Nullable AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

init(context);

}

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)

public HourlyForecastView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {

super(context, attrs, defStyleAttr, defStyleRes);

init(context);

}

private void init(Context context) {

mContext = context;

// if (ContentUtil.APP_SETTING_THEME.equals(“深色”)) {

// isDark = true;

// } else {

// isDark = false;

// }

isDark = false;

initDefValue();

initPaint();

}

private static int ITEM_SIZE = 24;

public void initData(List<HourlyResponse.HourlyBean> weatherData) {

hourlyWeatherList = weatherData;

int size = weatherData.size();

ITEM_SIZE = size;

dashLineList = new ArrayList<>();

Iterator iterator = weatherData.iterator();

HourlyResponse.HourlyBean hourlyBase;

String lastText = “”;

int idx = 0;

while (iterator.hasNext()) {

hourlyBase = (HourlyResponse.HourlyBean) iterator.next();

if (!hourlyBase.getIcon().equals(lastText)) {

if (idx != size - 1) {

dashLineList.add(idx);//从0开始添加虚线位置的索引值idx

lastText = hourlyBase.getIcon();

}

}

idx++;

}

dashLineList.add(size - 1);//添加最后一条虚线位置的索引值idx

invalidate();

}

private void initDefValue() {

DisplayMetrics dm = getResources().getDisplayMetrics();

screenWidth = dm.widthPixels;

itemWidth = DisplayUtil.dp2px(mContext, 30);

defWidthPixel = itemWidth * (ITEM_SIZE - 1);

defHeightPixel = DisplayUtil.dp2px(mContext, 80);

lowestTempHeight = DisplayUtil.dp2px(mContext, 40);//长度 非y轴值

highestTempHeight = DisplayUtil.dp2px(mContext, 70);

//defPadding

paddingT = DisplayUtil.dp2px(mContext, 20);

paddingL = DisplayUtil.dp2px(mContext, 10);

paddingR = DisplayUtil.dp2px(mContext, 15);

textSize = DisplayUtil.sp2px(mContext, 12);

bitmapHeight = 1 / 2f * (2 * defHeightPixel - lowestTempHeight) + DisplayUtil.dp2px(mContext, 2);//- 给文字留地方

bitmapXY = 18;

}

private TextPaint textLinePaint;

private void initPaint() {

// setLayerType(View.LAYER_TYPE_SOFTWARE, null);//关闭硬件加速

paint1 = new Paint(Paint.ANTI_ALIAS_FLAG);

paint1.setColor(mContext.getResources().getColor(R.color.line_back_dark));

paint1.setStyle(Paint.Style.FILL);

foldLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);

foldLinePaint.setStyle(Paint.Style.STROKE);

foldLinePaint.setStrokeWidth(5);

//折线颜色

foldLinePaint.setColor(mContext.getResources().getColor(R.color.line_color_2));

backPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

backPaint.setStrokeWidth(2);

backPaint.setAntiAlias(true);

dashPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

dashPaint.setColor(mContext.getResources().getColor(R.color.back_white));

DashPathEffect pathEffect = new DashPathEffect(new float[]{8, 8, 8, 8}, 1);

dashPaint.setPathEffect(pathEffect);

dashPaint.setStrokeWidth(3);

dashPaint.setAntiAlias(true);

dashPaint.setStyle(Paint.Style.STROKE);

textPaint = new Paint();

textPaint.setTextAlign(Paint.Align.CENTER);

textPaint.setTextSize(textSize);

textLinePaint = new TextPaint();

textLinePaint.setTextSize(DisplayUtil.sp2px(getContext(), 12));

textLinePaint.setAntiAlias(true);

textLinePaint.setColor(mContext.getResources().getColor(R.color.black));

//底部时间文字颜色

textPaint.setColor(mContext.getResources().getColor(R.color.search_light_un_color));

baseLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);

baseLinePaint.setStrokeWidth(3);

baseLinePaint.setStyle(Paint.Style.STROKE);

baseLinePaint.setColor(mContext.getResources().getColor(R.color.slategray));

bitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

bitmapPaint.setFilterBitmap(true);//图像滤波处理

bitmapPaint.setDither(true);//防抖动

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

//当设置的padding值小于默认值是设置为默认值

paddingT = DisplayUtil.dp2px(mContext, 20);

paddingL = DisplayUtil.dp2px(mContext, 10);

paddingR = DisplayUtil.dp2px(mContext, 15);

paddingB = Math.max(paddingB, getPaddingBottom());

//获取测量模式

//注意 HorizontalScrollView的子View 在没有明确指定dp值的情况下 widthMode总是MeasureSpec.UNSPECIFIED

//同理 ScrollView的子View的heightMode

int widthMode = MeasureSpec.getMode(widthMeasureSpec);

int heightMode = MeasureSpec.getMode(heightMeasureSpec);

//获取测量大小

int widthSize = MeasureSpec.getSize(widthMeasureSpec);

int heightSize = MeasureSpec.getSize(heightMeasureSpec);

if (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) {

mWidth = widthSize + paddingL + paddingR;

mHeight = heightSize;

}

//如果为wrap_content 那么View大小为默认值

if (widthMode == MeasureSpec.UNSPECIFIED && heightMode == MeasureSpec.AT_MOST) {

mWidth = defWidthPixel + paddingL + paddingR;

mHeight = defHeightPixel + paddingT + paddingB;

}

//设置视图的大小

setMeasuredDimension(mWidth, mHeight);

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

initDefValue();

initPaint();

if (hourlyWeatherList != null && hourlyWeatherList.size() != 0) {

drawLines(canvas);

drawBitmaps(canvas);

drawTemp(canvas);

}

}

private void drawTemp(Canvas canvas) {

for (int i = 0; i < hourlyWeatherList.size(); i++) {

if (currentItemIndex == i) {

//计算提示文字的运动轨迹

// int Y = getTempBarY(i);

String tmp = hourlyWeatherList.get(i).getTemp();

float temp = Integer.parseInt(tmp);

int Y = (int) (tempHeightPixel(temp) + paddingT);

//画出温度提示

int offset = itemWidth / 4;

Rect targetRect = new Rect(getScrollBarX(), Y - DisplayUtil.dip2px(getContext(), 24)

, getScrollBarX() + offset, Y - DisplayUtil.dip2px(getContext(), 4));

Paint.FontMetricsInt fontMetrics = textLinePaint.getFontMetricsInt();

int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;

textLinePaint.setTextAlign(Paint.Align.LEFT);

// if (ContentUtil.APP_SETTING_UNIT.equals(“hua”)) {

// tmp = String.valueOf(TransUnitUtil.getF(tmp));

// }

canvas.drawText(tmp + “°”, targetRect.centerX(), baseline, textLinePaint);

}

}

}

private void drawBitmaps(Canvas canvas) {

int scrollX = mScrollX;

boolean leftHide;

boolean rightHide;

for (int i = 0; i < dashLineList.size() - 1; i++) {

leftHide = true;

rightHide = true;

int left = itemWidth * dashLineList.get(i) + paddingL;

int right = itemWidth * dashLineList.get(i + 1) + paddingL;

//图的中间位置 drawBitmap是左边开始画

float drawPoint = 0;

if (left > scrollX && left < scrollX + screenWidth) {

leftHide = false;//左边缘显示

}

if (right > scrollX && right < scrollX + screenWidth) {

rightHide = false;

}

if (!leftHide && !rightHide) {//左右边缘都显示

drawPoint = (left + right) / 2f;

} else if (leftHide && !rightHide) {//右边缘与屏幕左边

drawPoint = (scrollX + right) / 2f;

} else if (!leftHide) {//左边缘与屏幕右边

//rightHide is True when reach this statement

drawPoint = (left + screenWidth + scrollX) / 2f;

} else {//左右边缘都不显示

if (right < scrollX + screenWidth) { //左右边缘都在屏幕左边

continue;

} else if (left > scrollX + screenWidth) {//左右边缘都在屏幕右边

continue;

} else {

drawPoint = (screenWidth) / 2f + scrollX;

}

}

String code = hourlyWeatherList.get(dashLineList.get(i)).getIcon();

BitmapDrawable bd;

if (code.contains(“d”)) {

bd = (BitmapDrawable) mContext.getResources().getDrawable(IconUtils.getDayIconDark(code.replace(“d”, “”)));

} else {

bd = (BitmapDrawable) mContext.getResources().getDrawable(IconUtils.getNightIconDark(code.replace(“n”, “”)));

}

assert bd != null;

Bitmap bitmap = DisplayUtil.bitmapResize(bd.getBitmap(),

DisplayUtil.dp2px(mContext, bitmapXY), DisplayUtil.dp2px(mContext, bitmapXY));

//越界判断

if (drawPoint >= right - bitmap.getWidth() / 2f) {

drawPoint = right - bitmap.getWidth() / 2f;

}

if (drawPoint <= left + bitmap.getWidth() / 2f) {

drawPoint = left + bitmap.getWidth() / 2f;

}

drawBitmap(canvas, bitmap, drawPoint, bitmapHeight);

// String text = hourlyWeatherList.get(dashLineList.get(i)).getCond_txt();

// textPaint.setTextSize(DisplayUtil.sp2px(mContext, 8));

// canvas.drawText(text, drawPoint, bitmapHeight + bitmap.getHeight() + 100 / 3f, textPaint);

}

}

private void drawBitmap(Canvas canvas, Bitmap bitmap, float left, float top) {

canvas.save();

canvas.drawBitmap(bitmap, left - bitmap.getWidth() / 2, top, bitmapPaint);

canvas.restore();

}

private void drawLines(Canvas canvas) {

//底部的线的高度 高度为控件高度减去text高度的1.5倍

baseLineHeight = mHeight - 1.5f * textSize;

Path path = new Path();

List dashWidth = new ArrayList<>();

List dashHeight = new ArrayList<>();

List mPointList = new ArrayList<>();

for (int i = 0; i < hourlyWeatherList.size(); i++) {

float temp = Integer.parseInt(hourlyWeatherList.get(i).getTemp());

float w = itemWidth * i + paddingL;

float h = tempHeightPixel(temp) + paddingT;

Point point = new Point((int) w, (int) h);

mPointList.add(point);

//画虚线

if (dashLineList.contains(i)) {

dashWidth.add(w);

dashHeight.add(h);

}

}

float prePreviousPointX = Float.NaN;

float prePreviousPointY = Float.NaN;

float previousPointX = Float.NaN;

float previousPointY = Float.NaN;

float currentPointX = Float.NaN;

float currentPointY = Float.NaN;

float nextPointX;

float nextPointY;

for (int valueIndex = 0; valueIndex < hourlyWeatherList.size(); ++valueIndex) {

if (Float.isNaN(currentPointX)) {

Point point = mPointList.get(valueIndex);

currentPointX = point.x;

currentPointY = point.y;

}

if (Float.isNaN(previousPointX)) {

//是否是第一个点

if (valueIndex > 0) {

Point point = mPointList.get(valueIndex - 1);

previousPointX = point.x;

previousPointY = point.y;

} else {

//是的话就用当前点表示上一个点

previousPointX = currentPointX;

previousPointY = currentPointY;

}

}

if (Float.isNaN(prePreviousPointX)) {

//是否是前两个点

if (valueIndex > 1) {

Point point = mPointList.get(valueIndex - 2);

prePreviousPointX = point.x;

prePreviousPointY = point.y;

} else {

//是的话就用当前点表示上上个点

prePreviousPointX = previousPointX;

prePreviousPointY = previousPointY;

}

}

// 判断是不是最后一个点了

if (valueIndex < hourlyWeatherList.size() - 1) {

Point point = mPointList.get(valueIndex + 1);

nextPointX = point.x;

nextPointY = point.y;

} else {

//是的话就用当前点表示下一个点

nextPointX = currentPointX;

nextPointY = currentPointY;

}

if (valueIndex == 0) {

// 将Path移动到开始点

path.moveTo(currentPointX, currentPointY);

} else {

// 求出控制点坐标

final float firstDiffX = (currentPointX - prePreviousPointX);

final float firstDiffY = (currentPointY - prePreviousPointY);

final float secondDiffX = (nextPointX - previousPointX);

final float secondDiffY = (nextPointY - previousPointY);

final float firstControlPointX = previousPointX + (0.2F * firstDiffX);

final float firstControlPointY = previousPointY + (0.2F * firstDiffY);

final float secondControlPointX = currentPointX - (0.2F * secondDiffX);

final float secondControlPointY = currentPointY - (0.2F * secondDiffY);

//画出曲线

path.cubicTo(firstControlPointX, firstControlPointY, secondControlPointX, secondControlPointY,

currentPointX, currentPointY);

}

// 更新值,

prePreviousPointX = previousPointX;

prePreviousPointY = previousPointY;

previousPointX = currentPointX;

previousPointY = currentPointY;

currentPointX = nextPointX;

currentPointY = nextPointY;

}

//画折线

canvas.drawPath(path, foldLinePaint);

path.lineTo(mWidth - paddingR, baseLineHeight);

path.lineTo(paddingL, baseLineHeight);

//画阴影

int[] shadeColors = new int[]{

Color.argb(100, 60, 174, 242),

Color.argb(30, 60, 174, 242),

Color.argb(18, 237, 238, 240)};

Shader mShader = new LinearGradient(0, 0, 0, getHeight(), shadeColors, null, Shader.TileMode.CLAMP);

backPaint.setShader(mShader);

canvas.drawPath(path, backPaint);

//画虚线

drawDashLine(dashWidth, dashHeight, canvas);

for (int i = 0; i < hourlyWeatherList.size(); i++) {

float temp = Integer.parseInt(hourlyWeatherList.get(i).getTemp());

float w = itemWidth * i + paddingL;

float h = tempHeightPixel(temp) + paddingT;

//画时间

String time = hourlyWeatherList.get(i).getFxTime();

//画时间

if (ITEM_SIZE > 8) {

if (i % 2 == 0) {

if (i == 0) {

textPaint.setTextAlign(Paint.Align.LEFT);

} else {

textPaint.setTextAlign(Paint.Align.CENTER);

}

canvas.drawText(time.substring(time.length() - 11, time.length() - 6), w, baseLineHeight + textSize + DisplayUtil.dip2px(mContext, 3), textPaint);

}

} else {

textPaint.setTextAlign(Paint.Align.CENTER);

if (i == 0) {

canvas.drawText(“. 现在”, w, baseLineHeight + textSize + DisplayUtil.dip2px(mContext, 3), textPaint);

} else {

canvas.drawText(time.substring(time.length() - 11, time.length() - 6), w, baseLineHeight + textSize + DisplayUtil.dip2px(mContext, 3), textPaint);

}

}

}

}

//画虚线

private void drawDashLine(List dashWidth, List dashHeight, Canvas canvas) {

if (dashHeight != null && dashHeight.size() > 1) {

for (int i = 1; i < dashHeight.size() - 1; i++) {

canvas.drawLine(dashWidth.get(i), dashHeight.get(i) + 3, dashWidth.get(i), baseLineHeight, dashPaint);

}

}

}

public float tempHeightPixel(float tmp) {

float res = ((tmp - lowestTemp) / (highestTemp - lowestTemp)) * (highestTempHeight - lowestTempHeight) + lowestTempHeight;

return defHeightPixel - res;//y从上到下

}

@Override

public void update(int scrollX) {

mScrollX = scrollX;

}

public void setLowestTemp(int lowestTemp) {

this.lowestTemp = lowestTemp;

}

public void setHighestTemp(int highestTemp) {

this.highestTemp = highestTemp;

}

private int maxScrollOffset = 0;//滚动条最长滚动距离

private int scrollOffset = 0; //滚动条偏移量

private int currentItemIndex = 0; //当前滚动的位置所对应的item下标

//设置scrollerView的滚动条的位置,通过位置计算当前的时段

public void setScrollOffset(int offset, int maxScrollOffset) {

this.maxScrollOffset = maxScrollOffset + DisplayUtil.dp2px(mContext, 50);

scrollOffset = offset;

currentItemIndex = calculateItemIndex();

invalidate();

}

//通过滚动条偏移量计算当前选择的时刻

private int calculateItemIndex() {

int x = getScrollBarX();

int sum = paddingL - itemWidth / 2;

for (int i = 0; i < ITEM_SIZE - 1; i++) {

sum += itemWidth;

if (x < sum)

return i;

}

return ITEM_SIZE - 1;

}

private int getScrollBarX() {

int x = (ITEM_SIZE - 1) * itemWidth * scrollOffset / maxScrollOffset;

x = x - DisplayUtil.dp2px(mContext, 3);

return x;

}

}

IndexHorizontalScrollView.java

package com.llw.goodweather.view.horizonview;

import android.content.Context;

import android.graphics.Canvas;

import android.util.AttributeSet;

import android.widget.HorizontalScrollView;

import com.llw.goodweather.utils.DisplayUtil;

/**

  • 横向滑动条

*/

public class IndexHorizontalScrollView extends HorizontalScrollView {

private HourlyForecastView hourlyForecastView;

public IndexHorizontalScrollView(Context context) {

this(context, null);

}

public IndexHorizontalScrollView(Context context, AttributeSet attrs) {

this(context, attrs, 0);

}

public IndexHorizontalScrollView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

}

/**

  • 绘制

  • @param canvas

*/

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

int offset = computeHorizontalScrollOffset();

int maxOffset = computeHorizontalScrollRange() - DisplayUtil.getScreenWidth(getContext());

if(hourlyForecastView != null){

hourlyForecastView.setScrollOffset(offset, maxOffset);

}

}

/**

  • 设置24小时的View

  • @param today24HourView

*/

public void setToday24HourView(HourlyForecastView today24HourView){

this.hourlyForecastView = today24HourView;

}

}

里面还有一些颜色值,在mvplibrary下的colors.xml,如下

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

#C6D7F4

#FBFEF7

#ffffff

#000000

#1E1E1A

#77d034

#9FC8E9

#00000000

#22000000

#22FFFFFF

#F38A50

#FFEFD5

#454545

#BABABA

#FFBCB3

#FDEBE8

#E7F3FC

#F2F2F2

#707070

#6D6D6D

#FF0A00

#E3E5E8

#E7C373

#243440

#C8DCFF

#F8F8F8

#44000000

#66000000

#70000000

#88000000

#FF7E45

#B3BCCA

#22FFFFFF

#44FFFFFF

#66FFFFFF

#88FFFFFF

#56004f

#F6F6F6

#626262

#141414

#DEDEE1

#D9D9D9

#818181

#f7f8fa

#E9EAEF

#B9C0CA

#3f8DA0BA

#919191

#666666

#708090

#50A0FF

#002C46

#597EF7

#33F2BE55

#33446FF2

#33AD9442

#334A5CC4

#A4A4A4

#3C91F2

#595959

然后在activity_map_weather.xml中

在这里插入图片描述

这一部分布局代码如下:

<TextView

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:layout_margin=“@dimen/dp_12”

android:text=“24小时预报”

android:textColor=“@color/black_4”

android:textSize=“@dimen/sp_16” />

<RelativeLayout

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_marginBottom=“@dimen/dp_12”>

<TextView

android:id=“@+id/tv_line_max_tmp”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_marginLeft=“@dimen/dp_12”

android:layout_marginTop=“@dimen/dp_16”

android:text=“21°”

android:textColor=“@color/black_4”

android:textSize=“@dimen/sp_16” />

<TextView

android:id=“@+id/tv_line_min_tmp”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_marginLeft=“@dimen/dp_12”

android:layout_marginTop=“@dimen/dp_66”

android:text=“11°”

android:textColor=“@color/black_4”

android:textSize=“@dimen/dp_16” />

<LinearLayout

android:layout_width=“match_parent”

android:layout_height=“@dimen/dp_98”

android:layout_marginLeft=“@dimen/dp_40”

android:orientation=“horizontal”

android:paddingRight=“@dimen/dp_12”>

<com.llw.goodweather.view.horizonview.IndexHorizontalScrollView

android:id=“@+id/hsv”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”>

<com.llw.goodweather.view.horizonview.HourlyForecastView

android:id=“@+id/hourly”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content” />

</com.llw.goodweather.view.horizonview.IndexHorizontalScrollView>

然后在MapWeatherActivity中

@BindView(R.id.tv_line_max_tmp)

TextView tvLineMaxTmp;//今日最高温

@BindView(R.id.tv_line_min_tmp)

TextView tvLineMinTmp;//今日最低温

@BindView(R.id.hourly)

HourlyForecastView hourly;//和风自定义逐小时天气渲染控件

@BindView(R.id.hsv)

IndexHorizontalScrollView hsv;//和风自定义滚动条

在搜索城市的返回中,新增一个24小时天气预报请求

在这里插入图片描述

然后在请求返回中做处理

然后实现里面的一个构造方法

/**

  • 24小时天气预报数据返回

  • @param response

*/

@Override

public void getWeatherHourlyResult(Response response) {

if(response.body().getCode().equals(Constant.SUCCESS_CODE)){

List<HourlyResponse.HourlyBean> hourlyWeatherList = response.body().getHourly();

List<HourlyResponse.HourlyBean> data = new ArrayList<>();

if (hourlyWeatherList.size() > 23) {

for (int i = 0; i < 24; i++) {

data.add(hourlyWeatherList.get(i));

String condCode = data.get(i).getIcon();

String time = data.get(i).getFxTime();

time = time.substring(time.length() - 11, time.length() - 9);

int hourNow = Integer.parseInt(time);

if (hourNow >= 6 && hourNow <= 19) {

data.get(i).setIcon(condCode + “d”);

} else {

data.get(i).setIcon(condCode + “n”);

}

}

} else {

for (int i = 0; i < hourlyWeatherList.size(); i++) {

data.add(hourlyWeatherList.get(i));

String condCode = data.get(i).getIcon();

String time = data.get(i).getFxTime();

time = time.substring(time.length() - 11, time.length() - 9);

int hourNow = Integer.parseInt(time);

if (hourNow >= 6 && hourNow <= 19) {

data.get(i).setIcon(condCode + “d”);

} else {

data.get(i).setIcon(condCode + “n”);

}

}

}

int minTmp = Integer.parseInt(data.get(0).getTemp());

int maxTmp = minTmp;

for (int i = 0; i < data.size(); i++) {

int tmp = Integer.parseInt(data.get(i).getTemp());

minTmp = Math.min(tmp, minTmp);

maxTmp = Math.max(tmp, maxTmp);

}

//设置当天的最高最低温度

hourly.setHighestTemp(maxTmp);

hourly.setLowestTemp(minTmp);

if (maxTmp == minTmp) {

hourly.setLowestTemp(minTmp - 1);

}

hourly.initData(data);

tvLineMaxTmp.setText(maxTmp + “°”);

tvLineMinTmp.setText(minTmp + “°”);

}else {

ToastUtils.showShortToast(context, CodeToStringUtils.WeatherCode(response.body().getCode()));

}

}

OK,然后运行一下:

在这里插入图片描述

3.地图天气中增加太阳和月亮数据


首先定义样式,在styles.xml中增加

然后在view包下新增一个

在这里插入图片描述

SunView.java

package com.llw.goodweather.view.skyview;

import android.animation.ValueAnimator;

import android.annotation.SuppressLint;

import android.content.Context;

import android.content.res.TypedArray;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.graphics.Canvas;

import android.graphics.Paint;

import android.graphics.RectF;

import android.text.TextUtils;

import android.util.AttributeSet;

import android.view.View;

import android.view.WindowManager;

import androidx.annotation.Nullable;

import com.llw.goodweather.R;

import com.llw.goodweather.utils.DisplayUtil;

import java.text.DecimalFormat;

/**

  • 太阳、月亮自定义View

*/

public class SunView extends View {

private int mWidth; //屏幕宽度

private int marginTop = 50;//离顶部的高度

private int mCircleColor; //圆弧颜色

private int mFontColor; //字体颜色

private int mRadius; //圆的半径

private float mCurrentAngle; //当前旋转的角度

private float mTotalMinute; //总时间(日落时间减去日出时间的总分钟数)

private float mNeedMinute; //当前时间减去日出时间后的总分钟数

private float mPercentage; //根据所给的时间算出来的百分占比

private float positionX, positionY; //太阳图片的x、y坐标

private float mFontSize; //字体大小

private String mStartTime; //开始时间(日出时间)

private String mEndTime; //结束时间(日落时间)

private String mCurrentTime; //当前时间

private Paint mTextPaint; //画笔

private Paint mLinePaint; //画笔

private Paint mTimePaint; //画笔

private RectF mRectF; //半圆弧所在的矩形

private Bitmap mSunIcon; //太阳图片

private WindowManager wm;

private Paint mShadePaint;

private Paint mPathPaint;

private Context mContext;

private boolean isSun = true;

private float endHour;

private Paint shadePaint;

private Paint pathPaint;

public SunView(Context context) {

this(context, null);

}

public SunView(Context context, @Nullable AttributeSet attrs) {

this(context, attrs, 0);

}

public SunView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

initView(context, attrs);

}

private void initView(Context context, @Nullable AttributeSet attrs) {

mContext = context;

marginTop = DisplayUtil.dip2px(context, 30);

@SuppressLint(“CustomViewStyleable”) TypedArray type = context.obtainStyledAttributes(attrs, R.styleable.SunAnimationView);

mCircleColor = type.getColor(R.styleable.SunAnimationView_sun_circle_color, getContext().getResources().getColor(R.color.dark_text_color));

mFontColor = type.getColor(R.styleable.SunAnimationView_sun_font_color, getContext().getResources().getColor(R.color.colorAccent));

mRadius = type.getInteger(R.styleable.SunAnimationView_sun_circle_radius, DisplayUtil.dp2px(getContext(), 130));

mRadius = DisplayUtil.dp2px(getContext(), mRadius);

mFontSize = type.getDimension(R.styleable.SunAnimationView_sun_font_size, DisplayUtil.dp2px(getContext(), 10));

mFontSize = DisplayUtil.dp2px(getContext(), mFontSize);

isSun = type.getBoolean(R.styleable.SunAnimationView_type, true);

type.recycle();

mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);

mTimePaint = new Paint(Paint.ANTI_ALIAS_FLAG);

shadePaint = new Paint(Paint.ANTI_ALIAS_FLAG);

pathPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

}

public void setType(boolean isSun, int circleColor, int fontColor) {

this.isSun = isSun;

mCircleColor = circleColor;

mFontColor = fontColor;

}

public void setTimes(String startTime, String endTime, String currentTime) {

mStartTime = startTime;

mEndTime = endTime;

mCurrentTime = currentTime;

String currentTimes[] = currentTime.split(“:”);

String startTimes[] = startTime.split(“:”);

String endTimes[] = endTime.split(“:”);

float currentHour = Float.parseFloat(currentTimes[0]);

float currentMinute = Float.parseFloat(currentTimes[1]);

float startHour = Float.parseFloat(startTimes[0]);

endHour = Float.parseFloat(endTimes[0]);

if (!isSun && endHour < startHour) {

endHour += 24;

}

float endMinute = Float.parseFloat(endTimes[1]);

if (isSun) {

if (currentHour > endHour) {

mCurrentTime = endTime;

} else if (currentHour == endHour && currentMinute >= endMinute) {

mCurrentTime = endTime;

}

}

mTotalMinute = calculateTime(mStartTime, mEndTime, false);//计算总时间,单位:分钟

mNeedMinute = calculateTime(mStartTime, mCurrentTime, true);//计算当前所给的时间 单位:分钟

mPercentage = Float.parseFloat(formatTime(mTotalMinute, mNeedMinute));//当前时间的总分钟数占日出日落总分钟数的百分比

mCurrentAngle = 180 * mPercentage;

setAnimation(0, mCurrentAngle, 2000);

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);

mWidth = wm.getDefaultDisplay().getWidth() / 2;

positionX = mWidth / 2 - mRadius - DisplayUtil.dip2px(mContext, 9); // 太阳图片的初始x坐标

positionY = mRadius; // 太阳图片的初始y坐标

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

}

@Override

protected void onLayout(boolean changed, int left, int top, int right, int bottom) {

super.onLayout(changed, mWidth / 2 - mRadius, marginTop, mWidth / 2 + mRadius, mRadius * 2 + marginTop);

}

@SuppressLint(“DrawAllocation”)

@Override

protected void onDraw(Canvas canvas) {

// 渐变遮罩的画笔

shadePaint.setColor(mContext.getResources().getColor(R.color.back_white));

shadePaint.setStyle(Paint.Style.FILL);

mShadePaint = shadePaint;

pathPaint.setColor(mContext.getResources().getColor(R.color.attention_text_light));

if (isSun) {

mSunIcon = BitmapFactory.decodeResource(getResources(), R.mipmap.icon_sun);

} else {

mSunIcon = BitmapFactory.decodeResource(getResources(), R.mipmap.icon_moon);

}

mSunIcon = DisplayUtil.bitmapResize(mSunIcon, DisplayUtil.dp2px(mContext, 18), DisplayUtil.dp2px(mContext, 18));

pathPaint.setStyle(Paint.Style.STROKE);

pathPaint.setStrokeWidth(2);

mPathPaint = pathPaint;

mLinePaint.setStyle(Paint.Style.STROKE);

mLinePaint.setDither(true);//防止抖动

mLinePaint.setStrokeWidth(2);

//第一步:画半圆

drawSemicircle(canvas);

canvas.save();

mLinePaint.setColor(mContext.getResources().getColor(R.color.attention_text_light));

canvas.drawLine(mWidth / 2 - mRadius - DisplayUtil.dip2px(mContext, 10), mRadius + marginTop, mWidth / 2 + mRadius + DisplayUtil.dip2px(mContext, 10), mRadius + marginTop, mLinePaint);

//第二步:绘制太阳的初始位置 以及 后面在动画中不断的更新太阳的X,Y坐标来改变太阳图片在视图中的显示

//第三部:绘制图上的文字

drawSunPosition(canvas);

drawFont(canvas);

super.onDraw(canvas);

}

/**

  • 绘制半圆

*/

private void drawSemicircle(Canvas canvas) {

mRectF = new RectF(mWidth / 2 - mRadius, marginTop, mWidth / 2 + mRadius, mRadius * 2 + marginTop);

mTextPaint.setStyle(Paint.Style.STROKE);

mTextPaint.setDither(true);//防止抖动

mTextPaint.setColor(mCircleColor);

canvas.drawArc(mRectF, 180, 180, true, mTextPaint);

}

/**

  • 绘制太阳的位置

*/

private void drawSunPosition(Canvas canvas) {

// canvas.drawRect(positionX + DisplayUtil.dp2px(mContext, 10), marginTop, mWidth / 2 + mRadius, mRadius * 2 + marginTop, mShadePaint);

canvas.drawArc(mRectF, 180, 180, true, mPathPaint);

canvas.drawBitmap(mSunIcon, positionX, positionY, mLinePaint);

}

/**

  • 绘制底部左右边的日出时间和日落时间

  • @param canvas

*/

private void drawFont(Canvas canvas) {

mFontSize = DisplayUtil.dp2px(getContext(), 12);

mTextPaint.setColor(mFontColor);

mTextPaint.setTextSize(mFontSize);

mTimePaint.setColor(getResources().getColor(R.color.black_4));

mTimePaint.setTextSize(mFontSize);

String startTime = TextUtils.isEmpty(mStartTime) ? “” : mStartTime;

String endTime = TextUtils.isEmpty(mEndTime) ? “” : mEndTime;

String sunrise = “日出”;

String sunset = “日落”;

if (!isSun) {

sunrise = “月出”;

sunset = “月落”;

}

mTimePaint.setTextAlign(Paint.Align.CENTER);

mTextPaint.setTextAlign(Paint.Align.CENTER);

canvas.drawText(sunrise, mWidth / 2 - mRadius + DisplayUtil.dip2px(mContext, 8), mRadius + DisplayUtil.dip2px(mContext, 16) + marginTop, mTextPaint);

canvas.drawText(startTime, mWidth / 2 - mRadius + DisplayUtil.dip2px(mContext, 8), mRadius + DisplayUtil.dip2px(mContext, 32) + marginTop, mTimePaint);

canvas.drawText(sunset, mWidth / 2 + mRadius - DisplayUtil.dip2px(mContext, 8), mRadius + DisplayUtil.dip2px(mContext, 16) + marginTop, mTextPaint);

canvas.drawText(endTime, mWidth / 2 + mRadius - DisplayUtil.dip2px(mContext, 8), mRadius + DisplayUtil.dip2px(mContext, 32) + marginTop, mTimePaint);

}

/**

  • 精确计算文字宽度

  • @param paint 画笔

  • @param str 字符串文本

*/

public static int getTextWidth(Paint paint, String str) {

int iRet = 0;

if (str != null && str.length() > 0) {

int len = str.length();

float[] widths = new float[len];

paint.getTextWidths(str, widths);

for (int j = 0; j < len; j++) {

iRet += (int) Math.ceil(widths[j]);

}

}

return iRet;

}

/**

  • 根据日出和日落时间计算出一天总共的时间:单位为分钟

  • @param startTime 日出时间

  • @param endTime 日落时间

  • @return

*/

private float calculateTime(String startTime, String endTime, boolean isCurrent) {

String startTimes[] = startTime.split(“:”);

String endTimes[] = endTime.split(“:”);

float startHour = Float.parseFloat(startTimes[0]);

float startMinute = Float.parseFloat(startTimes[1]);

float endHour = Float.parseFloat(endTimes[0]);

float endMinute = Float.parseFloat(endTimes[1]);

if (!isCurrent && !isSun && endHour < startHour) {

endHour += 24;

}

if (isSun) {

if (startHour > endHour) {

return 0;

} else if (startHour == endHour && startMinute >= endMinute) {

return 0;

}

} else {

if (isCurrent) {

if (startHour > endHour) {

return 0;

} else if (startHour == endHour && startMinute >= endMinute) {

return 0;

}

} else {

if (startHour >= endHour + 24) {

return 0;

}

}

}

if (checkTime(startTime, endTime)) {

return (endHour - startHour - 1) * 60 + (60 - startMinute) + endMinute;

}

return 0;

}

/**

  • 对所给的时间做一下简单的数据校验

  • @param startTime

  • @param endTime

  • @return

*/

private boolean checkTime(String startTime, String endTime) {

if (TextUtils.isEmpty(startTime) || TextUtils.isEmpty(endTime)

|| !startTime.contains(“:”) || !endTime.contains(“:”)) {

return false;

}

String startTimes[] = startTime.split(“:”);

String endTimes[] = endTime.split(“:”);

float startHour = Float.parseFloat(startTimes[0]);

float startMinute = Float.parseFloat(startTimes[1]);

float endHour = Float.parseFloat(endTimes[0]);

float endMinute = Float.parseFloat(endTimes[1]);

//如果所给的时间(hour)小于日出时间(hour)或者大于日落时间(hour)

if ((startHour < Float.parseFloat(mStartTime.split(“:”)[0]))

|| (endHour > this.endHour)) {

return false;

}

//如果所给时间与日出时间:hour相等,minute小于日出时间

if ((startHour == Float.parseFloat(mStartTime.split(“:”)[0]))

&& (startMinute < Float.parseFloat(mStartTime.split(“:”)[1]))) {

return false;

}

//如果所给时间与日落时间:hour相等,minute大于日落时间

if ((startHour == this.endHour)

&& (endMinute > Float.parseFloat(mEndTime.split(“:”)[1]))) {

return false;

}

if (startHour < 0 || endHour < 0

|| startHour > 23 || endHour > 23

|| startMinute < 0 || endMinute < 0

|| startMinute > 60 || endMinute > 60) {

return false;

}

return true;

}

/**

  • 根据具体的时间、日出日落的时间差值 计算出所给时间的百分占比

  • @param totalTime 日出日落的总时间差

  • @param needTime 当前时间与日出时间差

  • @return

*/

private String formatTime(float totalTime, float needTime) {

if (totalTime == 0)

return “0.00”;

DecimalFormat decimalFormat = new DecimalFormat(“0.00”);//保留2位小数,构造方法的字符格式这里如果小数不足2位,会以0补足.

return decimalFormat.format(needTime / totalTime);//format 返回的是字符串

}

private void setAnimation(float startAngle, float currentAngle, int duration) {

ValueAnimator sunAnimator = ValueAnimator.ofFloat(startAngle, currentAngle);

sunAnimator.setDuration(duration);

sunAnimator.setTarget(currentAngle);

sunAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

//每次要绘制的圆弧角度

mCurrentAngle = (float) animation.getAnimatedValue();

invalidateView();

}

});

sunAnimator.start();

}

private void invalidateView() {

//绘制太阳的x坐标和y坐标

positionX = mWidth / 2 - (float) (mRadius * Math.cos((mCurrentAngle) * Math.PI / 180)) - DisplayUtil.dp2px(mContext, 10);

positionY = mRadius - (float) (mRadius * Math.sin((mCurrentAngle) * Math.PI / 180)) + DisplayUtil.dip2px(mContext, 18);

invalidate();

}

}

然后进入到activity_map_weather.xml

在这里插入图片描述

<LinearLayout

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:gravity=“center_vertical”

android:orientation=“horizontal”>

<TextView

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_margin=“@dimen/dp_12”

android:text=“太阳和月亮”

android:textColor=“@color/black_4”

android:textSize=“@dimen/sp_16” />

<TextView

android:id=“@+id/tv_moon_state”

android:layout_width=“@dimen/dp_0”

android:layout_height=“wrap_content”

android:layout_weight=“1”

android:gravity=“right”

android:paddingRight=“@dimen/dp_12”

android:textColor=“@color/moon_tv_color”

android:textSize=“@dimen/sp_14” />

<LinearLayout

android:layout_width=“match_parent”

android:layout_height=“@dimen/dp_160”

android:layout_marginTop=“@dimen/dp_24”

android:orientation=“horizontal”>

<com.llw.goodweather.view.skyview.SunView

android:id=“@+id/sun_view”

android:layout_width=“0dp”

android:layout_height=“@dimen/dp_240”

android:layout_weight=“1”

app:sun_circle_color=“@color/sun_light_circle”

app:sun_circle_radius=“70”

app:sun_font_color=“@color/color_a4a4a4”

app:sun_font_size=“12px” />

<com.llw.goodweather.view.skyview.SunView

android:id=“@+id/moon_view”

android:layout_width=“0dp”

android:layout_height=“@dimen/dp_240”

android:layout_weight=“1”

app:sun_circle_color=“@color/moon_light_circle”

app:sun_circle_radius=“70”

app:sun_font_color=“@color/color_a4a4a4”

app:sun_font_size=“12px”

app:type=“false” />

然后进入到MapWeatherActivity

@BindView(R.id.sun_view)

SunView sunView;//太阳

@BindView(R.id.moon_view)

SunView moonView;//月亮

@BindView(R.id.tv_moon_state)

TextView tvMoonState;//月亮状态

然后就要新增接口了,在新增接口之前,有一个小插曲就是,如果你是在S6版本下创建的Key,那么你是访问不了V7版本下的太阳和月亮的接口的,会提示403,就是没有权限,所以需要你重新创建一个应用KEY,这是我自己新建的KEY,你最好也自己新建一个

在这里插入图片描述

打开Constant,新增如下:

在这里插入图片描述

再打开ApiService,新增并且修改之前的接口,这里我就不一一展示了

/**

  • 太阳和月亮 日出日落、月升月落

*/

@GET(“/v7/astronomy/sunmoon?key=”+ API_KEY)

Call getSunMoon(@Query(“location”) String location, @Query(“date”) String date);

SunMoonResponse的代码如下:

package com.llw.goodweather.bean;

import java.util.List;

/**

  • 太阳和月亮数据实体

*/

public class SunMoonResponse {

/**

  • code : 200

  • updateTime : 2020-09-02T18:00+08:00

  • fxLink : http://hfx.link/2ax1

  • sunrise : 2020-09-02T05:44+08:00

  • sunset : 2020-09-02T18:41+08:00

  • moonrise : 2020-09-02T19:10+08:00

  • moonset : 2020-09-03T05:19+08:00

  • moonPhase : [{“fxTime”:“2020-09-02T00:00+08:00”,“value”:“0.48”,“name”:“盈凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T01:00+08:00”,“value”:“0.48”,“name”:“盈凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T02:00+08:00”,“value”:“0.48”,“name”:“盈凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T03:00+08:00”,“value”:“0.48”,“name”:“盈凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T04:00+08:00”,“value”:“0.48”,“name”:“盈凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T05:00+08:00”,“value”:“0.49”,“name”:“盈凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T06:00+08:00”,“value”:“0.49”,“name”:“盈凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T07:00+08:00”,“value”:“0.51”,“name”:“亏凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T08:00+08:00”,“value”:“0.51”,“name”:“亏凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T09:00+08:00”,“value”:“0.51”,“name”:“亏凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T10:00+08:00”,“value”:“0.51”,“name”:“亏凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T11:00+08:00”,“value”:“0.51”,“name”:“亏凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T12:00+08:00”,“value”:“0.51”,“name”:“亏凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T13:00+08:00”,“value”:“0.51”,“name”:“亏凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T14:00+08:00”,“value”:“0.51”,“name”:“亏凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T15:00+08:00”,“value”:“0.51”,“name”:“亏凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T16:00+08:00”,“value”:“0.52”,“name”:“亏凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T17:00+08:00”,“value”:“0.52”,“name”:“亏凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T18:00+08:00”,“value”:“0.52”,“name”:“亏凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T19:00+08:00”,“value”:“0.52”,“name”:“亏凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T20:00+08:00”,“value”:“0.52”,“name”:“亏凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T21:00+08:00”,“value”:“0.52”,“name”:“亏凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T22:00+08:00”,“value”:“0.52”,“name”:“亏凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T23:00+08:00”,“value”:“0.52”,“name”:“亏凸月”,“illumination”:“100”}]

  • refer : {“sources”:[“heweather.com”],“license”:[“no commercial use”]}

*/

private String code;

private String updateTime;

private String fxLink;

private String sunrise;

private String sunset;

private String moonrise;

private String moonset;

private ReferBean refer;

private List moonPhase;

public String getCode() {

return code;

}

public void setCode(String code) {

this.code = code;

}

public String getUpdateTime() {

return updateTime;

}

public void setUpdateTime(String updateTime) {

this.updateTime = updateTime;

}

public String getFxLink() {

return fxLink;

}

public void setFxLink(String fxLink) {

this.fxLink = fxLink;

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

如何做好面试突击,规划学习方向?

面试题集可以帮助你查漏补缺,有方向有针对性的学习,为之后进大厂做准备。但是如果你仅仅是看一遍,而不去学习和深究。那么这份面试题对你的帮助会很有限。最终还是要靠资深技术水平说话。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。建议先制定学习计划,根据学习计划把知识点关联起来,形成一个系统化的知识体系。

学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。

我搜集整理过这几年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节

img

在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

020-09-02T19:00+08:00",“value”:“0.52”,“name”:“亏凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T20:00+08:00”,“value”:“0.52”,“name”:“亏凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T21:00+08:00”,“value”:“0.52”,“name”:“亏凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T22:00+08:00”,“value”:“0.52”,“name”:“亏凸月”,“illumination”:“100”},{“fxTime”:“2020-09-02T23:00+08:00”,“value”:“0.52”,“name”:“亏凸月”,“illumination”:“100”}]

  • refer : {“sources”:[“heweather.com”],“license”:[“no commercial use”]}

*/

private String code;

private String updateTime;

private String fxLink;

private String sunrise;

private String sunset;

private String moonrise;

private String moonset;

private ReferBean refer;

private List moonPhase;

public String getCode() {

return code;

}

public void setCode(String code) {

this.code = code;

}

public String getUpdateTime() {

return updateTime;

}

public void setUpdateTime(String updateTime) {

this.updateTime = updateTime;

}

public String getFxLink() {

return fxLink;

}

public void setFxLink(String fxLink) {

this.fxLink = fxLink;

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-4eZWHuGt-1712066398892)]
[外链图片转存中…(img-wMXyPgWu-1712066398893)]
[外链图片转存中…(img-T3mEocHY-1712066398893)]
[外链图片转存中…(img-L2zTsc8U-1712066398893)]
[外链图片转存中…(img-Rh5Kfyt8-1712066398894)]
[外链图片转存中…(img-DLITOskT-1712066398894)]
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-YjkAcVxp-1712066398894)]

如何做好面试突击,规划学习方向?

面试题集可以帮助你查漏补缺,有方向有针对性的学习,为之后进大厂做准备。但是如果你仅仅是看一遍,而不去学习和深究。那么这份面试题对你的帮助会很有限。最终还是要靠资深技术水平说话。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。建议先制定学习计划,根据学习计划把知识点关联起来,形成一个系统化的知识体系。

学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。

我搜集整理过这几年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节

img

在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

  • 17
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值