废话不多说,直接贴实现效果图和代码。。。。。。

效果图如下:

首先说一下我的实现思路,外部一个HorizontalScrollView,为scrollView添加一个LinearLayout子控件容器,遍历循环往LinearLayout中添加每天天气的layout,并为view设置每一天的数据。数据来源于https://www.nowapi.com/api/weather.future

下边贴代码:
首先是每天天气的layout布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical" android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center_horizontal"><TextView
        android:id="@+id/tv_horizontal_item_date"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="星期一\n2016-01-11"android:gravity="center_horizontal"/><ImageView
        android:id="@+id/iv_horizontal_item_day_pic"android:layout_width="30dp"android:layout_height="30dp"android:src="@mipmap/ic_launcher"/><TextView
        android:id="@+id/tv_horizontal_item_day_weather"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="晴"android:layout_marginBottom="6dp"/><com.example.cyy.weather.widget.FutureWeatherTrendView
        android:id="@+id/view_horizontal_item_trend"android:layout_width="120dp"android:layout_height="120dp" /><ImageView
        android:id="@+id/iv_horizontal_item_night_pic"android:layout_width="30dp"android:layout_height="30dp"android:src="@mipmap/ic_launcher"android:layout_marginTop="6dp"/><TextView
        android:id="@+id/tv_horizontal_item_night_weather"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="晴"/><TextView
        android:id="@+id/tv_horizontal_item_wind"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="西南风\n3级"android:layout_marginTop="9dp"android:gravity="center_horizontal"/></LinearLayout>

预览图是这样的

中间那块儿是一个自定义的view,下边看看这个自定义view是如何实现的,为了让这个自定义view适合任何数据。大致思路是:
1.算出每一度所占的高度,就是这几天中有一个最高的温度和一个最低的温度,让这两个温度相减得到最大温度差,然后用这个view的高度(除去上下文本的高度)除以这个温度差即得到这个值;
2.为了与前一天后一天的温度相连起来,有必要知道与前一天温度的中间值和与后一天温度的中间值,这个值根据相似三角形原理算出分别是取当天与前一天温度中间值以及当天与后一天温度中间值
如图:

如果是第一天如下图(不必知道与上一天的中间值)

如果是最后一天如下图(不必知道与下一天的中间值)

3.画点,写文本,连线,根据与最低温度的差值算出来温度点和两边的Y坐标,根据坐标值画点,写文本,最后连线

package com.example.cyy.weather.widget;import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import com.example.cyy.weather.utils.Tools;/*** Created by cyy on 2017/1/11.** //未来天气趋势的一个自定义view*/
public class FutureWeatherTrendView extends View {//为当前view指定一个默认最小宽度和默认最小高度private int mDefLeastWidth = 80, mDefLeastHeight = 150;//上下文对象private Context mContext;//分别代表画点的画笔,画文本的画笔和画线的画笔private Paint mCirclePaint, mTextPaint, mLinePaint;//字体大小private int mPaintTextSize = 16;//画的点的半径private int mCircleRadius = 3;//包含有字体度量信息的对象private Paint.FontMetrics mFontMetrics;//低温的左,中,右和高温的左,中,右,中间的温度为当天的温度,左边的温度是当天和上一天的中间值,// 右边的温度是当天和下一天的中间值private float mLowTemArray[], mHighTemArray[];//未来几天中温度的最低值和最高值private static float mLowestTem, mHighestTem;//未来几天中最低温度点的Y坐标private float mLowestTemY;//文本到点之间的间距private int mTextCircleDis = 3;//标记第一天和最后一天,如果type=0代表第一天,type=1代表最后一天,type=2代表第一天和最后一天之间的天private int mType;public FutureWeatherTrendView(Context context) {super(context);}public FutureWeatherTrendView(Context context, AttributeSet attrs) {super(context, attrs);this.mContext = context;initPaint();}/*** 初始化各种画笔*/private void initPaint(){mCirclePaint = new Paint();mCirclePaint.setColor(Color.YELLOW);mTextPaint = new Paint();mTextPaint.setTextSize(Tools.sp2px(mContext, mPaintTextSize));mTextPaint.setColor(Color.BLACK);mFontMetrics = mTextPaint.getFontMetrics();mLinePaint = new Paint();mLinePaint.setStrokeWidth(3);mLinePaint.setColor(Color.BLUE);//抗锯齿mLinePaint.setAntiAlias(true);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//view的宽度和高度int width, height;int widthMode = MeasureSpec.getMode(widthMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);width = measureWidthHeight(widthMode, widthSize, 0);height = measureWidthHeight(heightMode, heightSize, 0);setMeasuredDimension(width, height);}/*** 测量view的宽度和高度* @param mode* @param size* @param tag 0表示宽度,1表示高度* @return*/private int measureWidthHeight(int mode, int size, int tag) {int resultSize = 0;if(mode == MeasureSpec.EXACTLY){resultSize = size;}else{if(tag == 0){//view的宽度不能小于最小宽度resultSize = Tools.dip2px(mContext, mDefLeastWidth) + getPaddingLeft() + getPaddingRight();}else{//view的高度不能小于最小高度resultSize = Tools.dip2px(mContext, mDefLeastHeight) + getPaddingTop() + getPaddingBottom();}if(mode == MeasureSpec.AT_MOST){resultSize = Math.min(size, resultSize);}}return resultSize;}@Overrideprotected void onDraw(Canvas canvas) {//低温Y坐标float lowTemTextY = getTemY(mLowTemArray[1]) + getExtraHeight();drawLowOrHigh(canvas, mLowTemArray, lowTemTextY, mType);drawLowOrHigh(canvas, mHighTemArray, getTextHeight(), mType);}/*** 画低温和高温的点,温度文本值以及趋势* @param canvas 画布* @param temArray 低温或者高温左中右温度集合* @param textY 低温或者高温文本的Y坐标*/private void drawLowOrHigh(Canvas canvas, float temArray[], float textY, int type) {//第一步,画低温点,即中间的那个温度点(需要找到圆圈中心的坐标)//X坐标 中心int temX = getWidth() / 2;//Y坐标 中心float temY = getTemY(temArray[1]);//画点canvas.drawCircle(temX, temY, Tools.dip2px(mContext, mCircleRadius), mCirclePaint);//第二步,写低温下边的温度值(从文字左下角开始画,需要找到左下角的坐标)//X坐标float temTextX = getWidth() / 2 - mTextPaint.measureText(temArray[1] + "°") / 2;//Y坐标float temTextY = textY;//写文本canvas.drawText(temArray[1] + "°", temTextX, temTextY, mTextPaint);//第三步,得到低温左边和右边点的坐标int temLeftX = 0;float temLeftY = getTemY(temArray[0]);int temRightX = getWidth();float temRightY = getTemY(temArray[2]);//第四步,中间的温度点和左边以及右边的连线if(type == 0){canvas.drawLine(temX + Tools.dip2px(mContext, mCircleRadius), temY, temRightX, temRightY, mLinePaint);} else if(type == 1){canvas.drawLine(temLeftX, temLeftY, temX - Tools.dip2px(mContext, mCircleRadius), temY, mLinePaint);} else{canvas.drawLine(temLeftX, temLeftY, temX - Tools.dip2px(mContext, mCircleRadius), temY, mLinePaint);canvas.drawLine(temX + Tools.dip2px(mContext, mCircleRadius), temY, temRightX, temRightY, mLinePaint);}}/*** 获得文本所占的高度* @return*/private float getTextHeight(){//文本的高度float textHeight = mFontMetrics.bottom - mFontMetrics.top;return textHeight;}/*** 文本高度加上文本到温度点的间距*/private float getExtraHeight(){return getTextHeight() + Tools.dip2px(mContext, mTextCircleDis);}/*** 获得未来几天中最低温度所对应的点中心的Y坐标值*/private float getLowestTemY(){//最低点的Y坐标值=整个view的高度减去底下的文本的高度以及文本与温度点之间的间距mLowestTemY = getHeight() - getExtraHeight();return mLowestTemY;}/*** 平均一温度所占据的高度值* @return*/private float getPerTemHeight(){//最高温度和最低温度的高度差值float lowestHighestHeightDis = getHeight() - 2 * getExtraHeight();//最高温度和最低温度的差值float lowestHighestTemDis = mHighestTem - mLowestTem;return lowestHighestHeightDis / lowestHighestTemDis;}/*** 获得tem温度所在点的纵坐标* @param tem*/private float getTemY(float tem){float temY = getLowestTemY() - (tem - mLowestTem) * getPerTemHeight();return temY;}/*** 设置低温和低温左右的温度值* @param lowTemArray*/public FutureWeatherTrendView setLowTemArray(float lowTemArray[]){this.mLowTemArray = lowTemArray;return this;}/*** 设置高温和高温左右的温度值* @param highTemArray*/public FutureWeatherTrendView setHighTemArray(float highTemArray[]){this.mHighTemArray = highTemArray;return this;}public FutureWeatherTrendView setType(int type){this.mType = type;return this;}/*** 设置未来几天中最低的温度值* @param lowestTem*/public static void setLowestTem(int lowestTem){mLowestTem = lowestTem;}/*** 设置未来几天中最高的温度值* @param highestTem*/public static void setHighestTem(int highestTem){mHighestTem = highestTem;}}

然后是给view设置数据,向容器中添加view的代码,如下:

mTvPeriod.setText(weatherList.get(0).days + "---" + weatherList.get(weatherList.size() - 1).days);View itemView = null;FutureWeatherObj lastWeatherObj = null;FutureWeatherObj nextWeatherObj = null;int lowestTem = 0, highestTem = 0;LinearLayout ll = new LinearLayout(this);LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);ll.setLayoutParams(params);ll.setOrientation(LinearLayout.HORIZONTAL);ll.removeAllViews();ArrayList<Integer> lowTemList = new ArrayList<Integer>();//存放低温温度值的集合ArrayList<Integer> highTemList = new ArrayList<Integer>();//存放高温温度值的集合for (int i = 0; i < weatherList.size(); i++){FutureWeatherObj weatherObj = weatherList.get(i);lowTemList.add(Integer.parseInt(weatherObj.temp_low));highTemList.add(Integer.parseInt(weatherObj.temp_high));Collections.sort(lowTemList);//按照升序排列所有低温温度值Collections.sort(highTemList);//按照升序排列所有高温温度值}FutureWeatherTrendView.setLowestTem(lowTemList.get(0));FutureWeatherTrendView.setHighestTem(highTemList.get(weatherList.size() - 1));for(int i = 0; i < weatherList.size(); i++){float lowTemArray[] = new float[3];float highTemArray[] = new float[3];float lastNextTem[] = new float[4];FutureWeatherObj weatherObj = weatherList.get(i);if(i == 0){ //没有前一天只有后一天lastWeatherObj = null;nextWeatherObj = weatherList.get(i + 1);} else if(i > 0 && i < weatherList.size() - 1){ //有前一天和后一天lastWeatherObj = weatherList.get(i - 1);nextWeatherObj = weatherList.get(i + 1);} else if(i == weatherList.size() - 1){ //只有前一天没有后一天lastWeatherObj = weatherList.get(i - 1);nextWeatherObj = null;}if(lastWeatherObj != null) {lastNextTem[0] = Integer.parseInt(lastWeatherObj.temp_low);lastNextTem[1] = Integer.parseInt(lastWeatherObj.temp_high);}if(weatherObj != null){lowTemArray[1] = Integer.parseInt(weatherObj.temp_low);highTemArray[1] = Integer.parseInt(weatherObj.temp_high);}if(nextWeatherObj != null) {lastNextTem[2] = Integer.parseInt(nextWeatherObj.temp_low);lastNextTem[3] = Integer.parseInt(nextWeatherObj.temp_high);}if(i == 0){lowTemArray[2] = (lowTemArray[1] + lastNextTem[2]) / 2;highTemArray[2] = (highTemArray[1] + lastNextTem[3]) / 2;}else if(i > 0 && i < weatherList.size() - 1){lowTemArray[0] = (lastNextTem[0] + lowTemArray[1]) / 2;highTemArray[0] = (lastNextTem[1] + highTemArray[1]) / 2;lowTemArray[2] = (lowTemArray[1] + lastNextTem[2]) / 2;highTemArray[2] = (highTemArray[1] + lastNextTem[3]) / 2;}else if(i == weatherList.size() - 1){lowTemArray[0] = (lastNextTem[0] + lowTemArray[1]) / 2;highTemArray[0] = (lastNextTem[1] + highTemArray[1]) / 2;}itemView = LayoutInflater.from(this).inflate(R.layout.activity_future_weather_horizontal_item, null);TextView mTvDate = (TextView) itemView.findViewById(R.id.tv_horizontal_item_date);ImageView mIvDayPic = (ImageView) itemView.findViewById(R.id.iv_horizontal_item_day_pic);TextView mTvDayWeather = (TextView) itemView.findViewById(R.id.tv_horizontal_item_day_weather);FutureWeatherTrendView mViewTrend = (FutureWeatherTrendView) itemView.findViewById(R.id.view_horizontal_item_trend);ImageView mIvNightPic = (ImageView) itemView.findViewById(R.id.iv_horizontal_item_night_pic);TextView mTvNightWeather = (TextView) itemView.findViewById(R.id.tv_horizontal_item_night_weather);TextView mTvWind = (TextView) itemView.findViewById(R.id.tv_horizontal_item_wind);mTvDate.setText(weatherObj.week + "\n" + weatherObj.days);Picasso.with(this).load(weatherObj.weather_icon).into(mIvDayPic);Picasso.with(this).load(weatherObj.weather_icon1).into(mIvNightPic);if(!weatherObj.weather.contains("转")){mTvDayWeather.setText(weatherObj.weather);mTvNightWeather.setText(weatherObj.weather);}else{String weather[] = weatherObj.weather.split("转");mTvDayWeather.setText(weather[0]);mTvNightWeather.setText(weather[1]);}mTvWind.setText(weatherObj.wind + "\n" + weatherObj.winp);mViewTrend.setLowTemArray(lowTemArray).setHighTemArray(highTemArray).setType(i == 0 ? 0 : i == weatherList.size() - 1 ? 1 : 2);ll.addView(itemView);}mHsTrend.addView(ll);

weatherList是未来天气数据集合

仿小米天气预报未来几天趋势预报折线图相关推荐

  1. 自定义View之仿小米MIUI天气24小时预报折线图

    本篇文章已授权微信公众号 hongyangAndroid(鸿洋)独家发布. 效果图 本控件是仿MIUI8天气24小时预报折线图,用小米手机的可以打开天气软件看一下.本文是对自定义View的练手作品,要 ...

  2. 小马哥----高仿小米4 tc01刷机拆机主板图与开机界面图 分联通版与移动版

    高仿米4 版本种类较多 m8209 m8219 T7219 T7220  T8221  T8213  H69 H71 K6  X77 TC01  TD01 x65等等机型版本 陆续有新版本出现市场 高 ...

  3. Java项目:仿小米电子产品售卖商城系统(java+SpringBoot+Vue+MySQL+Redis+ElementUI)

    源码获取:博客首页 "资源" 里下载! 项目描述:这是一个基于SpringBoot+Vue框架开发的仿小米电子产品售卖商城系统.首先,这是一个前后端分离的项目,代码简洁规范,注释说 ...

  4. 深度学习未来十大趋势

    深度学习未来十大趋势 [日期:2015-12-22] 来源: 作者:张巨岩 [字体:大 中 小] 本周,我在加拿大蒙特利尔参加了NIPS(Neural Information Processing S ...

  5. 未来CRM的趋势和预测

    未来CRM的趋势和预测 1.保守派失利 来自Gartner的最新数据显示,这一趋势得以延续--老的保守者失去了阵地.曾几何时,Siebel在CRM领域大名鼎鼎.甲骨文收购了该公司,不到十年前,在CRM ...

  6. 未來用工新趨勢_数字化商业浪潮来袭 未来用工新趋势成焦点

    原标题:数字化商业浪潮来袭 未来用工新趋势成焦点 智库时代网 最近几年,在共享经济的影响下,与互联网相伴成长的90.95后与上一辈人的追求和观念都发生了翻天覆地的变化,他们更注重的是自我实现与自由发展 ...

  7. 2016云栖大会马云畅谈未来五大创新趋势

    本文讲的是2016云栖大会马云畅谈未来五大创新趋势[IT168 云计算]今天,以"飞天·进化 Apsara Evolution"为主题的2016年云栖大会在杭州云栖小镇隆重召开.作 ...

  8. android 日历仿IOS,基于Android week view仿小米和iphone日历效果

    前言 最近由于项目需求,要做一个仿小米日历的功能,下面显示一天的日程,header以周为单位进行滑动,github上找了很久也没有找到合适的,但找到一相近的开源项目Android-week-view, ...

  9. 【报告分享】2020年母婴未来消费新趋势报告.pdf(附下载链接)

    大家好,我是文文(微信:sscbg2020),今天给大家分享尼尔森零售研究与CBME.时尚育儿消费者研究组与2020年10月份联合发布的报告<2020年母婴未来消费新趋势洞察报告.pdf> ...

最新文章

  1. Linux下 memcached安装以及启动
  2. 检查用户显示器的分辨率
  3. 生活娱乐 达尔优的键盘鼠标如何打开和关闭呼吸灯
  4. python tk下拉列表_如何从Tkinter中的列表创建下拉菜单?
  5. JAVA :RESTLET开发实例(一)基于JAX-RS的REST服务
  6. 面试突然问Java多线程底层原理,我哭了!
  7. 关于”要执行请求的操作,WordPress需要访问您网页服务器的权限”
  8. aix查看文件夹大小命令_AIX5.3系统文件大小的限制
  9. 吐血推荐几款优秀下载软件
  10. FlashBuild4序列号生成与使用方法
  11. 计算机科目三教学设计,驾校科目三教案
  12. 期货开户后需要银期转账绑定
  13. 下拉框 切换一个下拉框 另一个下拉框做相应的改变
  14. 为什么阿里 P7 都找不到工作了?
  15. 设计师都爱用的UI标注软件有哪些?
  16. java学习之面向对象和封装
  17. 后缀是lnk是什么文件_后缀是lnk文件怎么打开,lnk什么格式
  18. 生信基础(三)——统计分析工具R语言
  19. amd cpu 安卓模拟器_一款完美解决AMD兼容问题的安卓模拟器
  20. 【调剂】山东科技大学机械电子工程学院硕士招生

热门文章

  1. 深度学习系列笔记之统计基础
  2. xjoi 1542 玩玩拉格朗日四平方和定理
  3. 使用rufus制作Windows Server 2012 R2 U盘_wentfar·tsao
  4. 两个电阻的并联与串联
  5. 【2018NOIP普及组】T2:龙虎斗 试题解析
  6. 【C/C++】ifndef/define/endif的作用
  7. idea如何配置或者创建mybatis的xml文件 idea如何配置或者创建mybatis的配置文件
  8. 喜大普奔!GitHub App 终于支持中文了,可尝鲜下载体验!
  9. 通俗理解torch.distributed.barrier()工作原理
  10. 在echarts中圆环图中间 自定义图片引入