效果目标:

1.现在到未来24小时温度点连成贝塞尔曲线

2.指示器跟随选中每小时空气质量移动

3.合并相同风力与天气icon

4.显示区域的icon在屏幕可视范围内左右居中

5.显示区域的风力等级在屏幕可视范围内左右居中


最终效果:


实现过程:

1.首先绘制贝塞尔曲线

 //绘制温度贝塞尔曲线private void drawCurve(Canvas canvas, List<Point> points) {Path curvePath = new Path();//绘制点的 x,y轴 集合points_x.clear();points_y.clear();//遍历 把points的x,y点添加到集合里for (int i = 0; i < points.size(); i++) {if (points.get(i) == null) {break;}if (i == points.size() - 1) {points_x.add(points.get(i).x);points_x.add(points.get(i).x + ITEM_WIDTH);points_y.add(points.get(i).y);} else {points_x.add(points.get(i).x);}points_y.add(points.get(i).y);}//把点转换 实现圆润的效果List<Cubic> calculate_x = calculate(points_x);List<Cubic> calculate_y = calculate(points_y);//从 x,y的0点绘制curvePath.moveTo(calculate_x.get(0).eval(0), calculate_y.get(0).eval(0));//循环绘制  STEPS越大 绘制的点越密for (int i = 0; i < calculate_x.size(); i++) {for (int j = 1; j <= STEPS; j++) {float u = j / (float) STEPS;curvePath.lineTo(calculate_x.get(i).eval(u), calculate_y.get(i).eval(u));}}canvas.drawPath(curvePath, curveLinePaint);}

点转换的方法

 /*** 计算 贝塞尔曲线的 点** @param x* @return*/private List<Cubic> calculate(List<Integer> x) {int n = x.size() - 1;float[] gamma = new float[n + 1];float[] delta = new float[n + 1];float[] D = new float[n + 1];int i;gamma[0] = 1.0f / 2.0f;for (i = 1; i < n; i++) {gamma[i] = 1 / (4 - gamma[i - 1]);}gamma[n] = 1 / (2 - gamma[n - 1]);delta[0] = 3 * (x.get(1) - x.get(0)) * gamma[0];for (i = 1; i < n; i++) {delta[i] = (3 * (x.get(i + 1) - x.get(i - 1)) - delta[i - 1])* gamma[i];}delta[n] = (3 * (x.get(n) - x.get(n - 1)) - delta[n - 1]) * gamma[n];D[n] = delta[n];for (i = n - 1; i >= 0; i--) {D[i] = delta[i] - gamma[i] * D[i + 1];}/* now compute the coefficients of the cubics */List<Cubic> cubics = new LinkedList<Cubic>();for (i = 0; i < n; i++) {Cubic c = new Cubic(x.get(i), D[i], 3 * (x.get(i + 1) - x.get(i))- 2 * D[i] - D[i + 1], 2 * (x.get(i) - x.get(i + 1)) + D[i]+ D[i + 1]);cubics.add(c);}return cubics;}

然后绘制曲线到底部的背景

/*** 绘制 曲线下背景和分割线* <p>* 1. 先把曲线找到 然后画曲线到右下 右下画左下 左下到 曲线0点* 2.闭合1画的路径 设置渐变* 3.把记录需要画分割线的下标遍历绘制*/private void onDrawLineBg(Canvas canvas, List<Point> points) {//背景的pathPath curvePath = new Path();//分割线的patnPath linePath = new Path();points_x.clear();points_y.clear();for (int i = 0; i < points.size(); i++) {if (points.get(i) == null) {break;}if (i == points.size() - 1) {points_x.add(points.get(i).x);points_x.add(points.get(i).x + ITEM_WIDTH);points_y.add(points.get(i).y);} else {points_x.add(points.get(i).x);}points_y.add(points.get(i).y);}List<Cubic> calculate_x = calculate(points_x);List<Cubic> calculate_y = calculate(points_y);//从 0 点开始画curvePath.moveTo(calculate_x.get(0).eval(0), calculate_y.get(0).eval(0));linePath.moveTo(calculate_x.get(0).eval(0), calculate_y.get(0).eval(0));//连接贝塞尔曲线for (int i = 0; i < calculate_x.size(); i++) {for (int j = 1; j <= STEPS; j++) {float u = j / (float) STEPS;curvePath.lineTo(calculate_x.get(i).eval(u), calculate_y.get(i).eval(u));linePath.lineTo(calculate_x.get(i).eval(u), calculate_y.get(i).eval(u));dashLinePaint.setShader(new LinearGradient(calculate_x.get(i).eval(u), calculate_y.get(i).eval(u), calculate_x.get(i).eval(u), (mHeight - bottomTextHeight - DisplayUtil.dip2px(getContext(), 20)), getResources().getColor(R.color.color_9945A0FD), getResources().getColor(R.color.color_00E0EFFF), Shader.TileMode.MIRROR));}//循环 需不需要画分割线eval(1) 是最大点 eval(0)是最小点for (Integer postion : postions) {if (postion == i && i != ITEM_SIZE - 1) {linePath.lineTo(calculate_x.get(i).eval(1), (mHeight - bottomTextHeight - DisplayUtil.dip2px(getContext(), 20)));linePath.lineTo(calculate_x.get(i).eval(1), calculate_y.get(i).eval(1));}}//            curvePath.lineTo(calculate_x.get(i).eval(0), calculate_y.get(i).eval(0));}//连接所有点curvePath.lineTo(calculate_x.get(calculate_x.size() - 1).eval(1), (mHeight - bottomTextHeight - DisplayUtil.dip2px(getContext(), 20)));curvePath.lineTo(calculate_x.get(0).eval(0), (mHeight - bottomTextHeight - DisplayUtil.dip2px(getContext(), 20)));curvePath.lineTo(calculate_x.get(0).eval(0), calculate_y.get(0).eval(0));//闭合路径curvePath.close();canvas.drawPath(curvePath, dashLinePaint);canvas.drawPath(linePath, linePaint);}

绘制选中时的背景

  /*** @param canvas* @param points 选中的背景*               1.先获取所有相同的数据最小的点*               2.找到当前选中的postion*               3.绘制后面相同的数据*/private void onDrawLineSelectBg(Canvas canvas, List<Point> points) {Path curvePath = new Path();points_x.clear();points_y.clear();for (int i = 0; i < points.size(); i++) {if (points.get(i) == null) {break;}if (i == points.size() - 1) {points_x.add(points.get(i).x);points_x.add(points.get(i).x + ITEM_WIDTH);points_y.add(points.get(i).y);} else {points_x.add(points.get(i).x);}points_y.add(points.get(i).y);}List<Cubic> calculate_x = calculate(points_x);List<Cubic> calculate_y = calculate(points_y);//定义 最大下标和最小下班int maxi = 0;int mini = 0;//遍历for (int i = 0; i < calculate_x.size(); i++) {//天气白天夜晚是否一致boolean weatherType = true;// 四种类型区分白天晚上 其他的不区分if (listItems.get(i).getWeatherCode().equals("00")|| listItems.get(i).getWeatherCode().equals("01")|| listItems.get(i).getWeatherCode().equals("03")|| listItems.get(i).getWeatherCode().equals("13")) {if (i <= listItems.size() - 2) {weatherType = listItems.get(i).getDayTime().equals(listItems.get(i + 1).getDayTime());} else {weatherType = listItems.get(i).getDayTime().equals(listItems.get(i - 1).getDayTime());}}if (i == currentItemIndex) {//获取最小下标  从左往右画for (int x = currentItemIndex; x >= 0; x--) {if (listItems.get(x).getWeatherCode().equals(listItems.get(i).getWeatherCode()) && weatherType) {mini = x;} else {break;}}//起始点curvePath.moveTo(calculate_x.get(mini).eval(0), calculate_y.get(mini).eval(0));//最小点 画到当前下标for (int x = mini; x < currentItemIndex; x++) {if (listItems.get(x).getWeatherCode().equals(listItems.get(i).getWeatherCode()) && weatherType) {for (int j = 1; j <= STEPS; j++) {float u = j / (float) STEPS;curvePath.lineTo(calculate_x.get(x).eval(u), calculate_y.get(x).eval(u));dashLinePaint.setShader(new LinearGradient(calculate_x.get(x).eval(u), calculate_y.get(x).eval(u), calculate_x.get(x).eval(u), (mHeight - bottomTextHeight - DisplayUtil.dip2px(getContext(), 20)), getResources().getColor(R.color.color_a7d1fc), getResources().getColor(R.color.color_00E0EFFF), Shader.TileMode.MIRROR));}} else {break;}}//接着画当前位置for (int j = 1; j <= STEPS; j++) {float u = j / (float) STEPS;curvePath.lineTo(calculate_x.get(i).eval(u), calculate_y.get(i).eval(u));dashLinePaint.setShader(new LinearGradient(calculate_x.get(i).eval(u), calculate_y.get(i).eval(u), calculate_x.get(i).eval(u), (mHeight - bottomTextHeight - DisplayUtil.dip2px(getContext(), 20)), getResources().getColor(R.color.color_a7d1fc), getResources().getColor(R.color.color_00E0EFFF), Shader.TileMode.MIRROR));}//画到最大下标 overfor (int x = currentItemIndex; x < calculate_x.size(); x++) {if (listItems.get(x).getWeatherCode().equals(listItems.get(i).getWeatherCode()) && weatherType) {maxi = x;for (int j = 1; j <= STEPS; j++) {float u = j / (float) STEPS;curvePath.lineTo(calculate_x.get(x).eval(u), calculate_y.get(x).eval(u));dashLinePaint.setShader(new LinearGradient(calculate_x.get(x).eval(u), calculate_y.get(x).eval(u), calculate_x.get(x).eval(u), (mHeight - bottomTextHeight - DisplayUtil.dip2px(getContext(), 20)), getResources().getColor(R.color.color_a7d1fc), getResources().getColor(R.color.color_00E0EFFF), Shader.TileMode.MIRROR));}} else {break;}}}}//闭合起来 填满渐变颜色curvePath.lineTo(calculate_x.get(maxi).eval(1), (mHeight - bottomTextHeight - DisplayUtil.dip2px(getContext(), 20)));curvePath.lineTo(calculate_x.get(mini).eval(0), (mHeight - bottomTextHeight - DisplayUtil.dip2px(getContext(), 20)));curvePath.lineTo(calculate_x.get(mini).eval(0), calculate_y.get(mini).eval(0));curvePath.close();canvas.drawPath(curvePath, dashLinePaint);}

画天气图标,合并 在可视范围居中

/*** @param canvas* @param i* @param pointLisss* 画 天气 icon* 1.相邻合并 并且居中 部分白天晚上icon不一样 也要区分* 2.控制显示位置 始终在屏幕可视范围内居中*/private void onDrawWeatherIcon(Canvas canvas, int i, List<Point> pointLisss) {//天气白天夜晚是否一致boolean weatherType = true;// 四种类型区分白天晚上if (listItems.get(i).getWeatherCode().equals("00")|| listItems.get(i).getWeatherCode().equals("01")|| listItems.get(i).getWeatherCode().equals("03")|| listItems.get(i).getWeatherCode().equals("13")) {if (i <= listItems.size() - 2) {weatherType = listItems.get(i).getDayTime().equals(listItems.get(i + 1).getDayTime());} else {weatherType = listItems.get(i).getDayTime().equals(listItems.get(i - 1).getDayTime());}} else {}//判断 如果相邻两个icon相同 数量++;if (i <= listItems.size() - 2 && listItems.get(i).getWeatherCode().equals(listItems.get(i + 1).getWeatherCode()) && weatherType) {weatherCount++;} else {if (i <= listItems.size() - 2 &&(!listItems.get(i).getWeatherCode().equals(listItems.get(i + 1).getWeatherCode()) ||!weatherType) ||i == listItems.size() - 1) {//计算左 右 上 下边位置int left = MARGIN_LEFT_ITEM + (i - (weatherCount - 1)) * ITEM_WIDTH;int right = left + weatherCount * ITEM_WIDTH;int top = tempBaseBottom + DisplayUtil.dip2px(getContext(), 5);int bottom = top + DisplayUtil.dip2px(getContext(), 16);Bitmap bitmap = getBitmap(ImageUtil.setWeatherIconByText(listItems.get(i).getWeatherCode() == null ? "125" : listItems.get(i).getWeatherCode(), getContext(), listItems.get(i).getDayTime()));Rect rect1 = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());RectF boxRect = new RectF(left, top, right, bottom);//相同数量大于 1if (weatherCount > 1) {//计算当前屏幕显示的x 起始点 如果大于当前icon显示区域的 left 并且 小于right(超出屏幕就不管他了) 把屏幕边缘的点 当作leftif (getScreenLeftX() > left && getScreenLeftX() < right) {left = (int) getScreenLeftX();if (right - left < ITEM_WIDTH) {left = right - ITEM_WIDTH;}}//计算当前屏幕显示的x 最终点 当前icon显示区域的 lright如果大于最终点 并且 left 在屏幕范围  把屏幕边缘的最终点 当作rightif (right > getScreenRightX() && left < getScreenRightX()) {right = (int) getScreenRightX();if (right - left < ITEM_WIDTH) {right = left + ITEM_WIDTH;}}//居中显示int iconleft = left + (right - left) / 2 - ICON_WIDTH / 2;int iconright = iconleft + ICON_WIDTH;boxRect = new RectF(iconleft, top, iconright, bottom);} else {//默认样式int iconleft = left + ITEM_WIDTH / 2 - ICON_WIDTH / 2;int iconright = iconleft + ICON_WIDTH;boxRect = new RectF(iconleft, top, iconright, bottom);}canvas.drawBitmap(bitmap, rect1, boxRect, bitmapPaint);//重新初始化数量weatherCount = 1;}}}

获取当前显示的x轴起始坐标

//获取在屏幕上第一个点x轴public float getScreenLeftX() {return scrollOffset + MARGIN_LEFT_ITEM - DisplayUtil.dip2px(getContext(), 9);}

获取当前显示的x轴最终坐标

 public float getScreenRightX() {float rightx = ShowWidth - MARGIN_LEFT_ITEM + getScreenLeftX() + DisplayUtil.dip2px(getContext(), 9);return rightx;}

绘制风力

 //画底部风力的BOXprivate void onDrawBox(Canvas canvas, int i, Point firstPoint, Point endPoint) {WeatherHourDetail item = listItems.get(i);if (i <= listItems.size() - 2 && item.getWindLevel() == listItems.get(i + 1).getWindLevel()) {windyCount = windyCount + 1;} else {//先画 然后再置为 1//与下一个  风级 内容不同时候 才 画  或者最后一个if (i <= listItems.size() - 2 && item.getWindLevel() != listItems.get(i + 1).getWindLevel() || i == listItems.size() - 1) {int left = /*MARGIN_LEFT_ITEM +*/ firstPoint.x + (i - (windyCount - 1)) * ITEM_WIDTH;//处理最右侧int right = left + windyCount * ITEM_WIDTH - 1;//等比计算底部高度
//                int top = mHeight - bottomTextHeight - 80;int top = (mHeight - bottomTextHeight) - DisplayUtil.dip2px(getContext(), 15);int bottom = mHeight - bottomTextHeight;Rect boxRect = new Rect(left, top, right, bottom);// 新建一个矩形Drawable drawable = ContextCompat.getDrawable(getContext(), R.drawable.shape_rangle_24_eeeeee);drawable.setBounds(boxRect);drawable.draw(canvas);//画 字//垂直居中Paint.FontMetricsInt fontMetrics = textPaint.getFontMetricsInt();float baselines = (boxRect.bottom + boxRect.top - fontMetrics.bottom - fontMetrics.top) / 2;textPaint.setTextAlign(Paint.Align.CENTER);textPaint.setColor(getResources().getColor(R.color.color_666666));String windlevel = "";if (item.getWindLevel() == 0) {windlevel = "微风";} else {windlevel = item.getWindLevel() + "级";}if (windyCount > 1) {//判断与icon一样if (getScreenLeftX() > left && getScreenLeftX() < right) {left = (int) getScreenLeftX();if (right - left < ITEM_WIDTH) {left = right - ITEM_WIDTH;}}if (right > getScreenRightX() && left < getScreenRightX()) {right = (int) getScreenRightX();if (right - left < ITEM_WIDTH) {right = left + ITEM_WIDTH;}}int iconleft = left + (right - left) / 2 - ICON_WIDTH / 2;int iconright = iconleft + ICON_WIDTH;textPaint.setTextSize(DisplayUtil.sp2px(getContext(), 11));canvas.drawText(windlevel, iconleft + (iconright - iconleft) / 2, baselines, textPaint);} else {//默认canvas.drawText(windlevel, boxRect.centerX(), baselines, textPaint);}}windyCount = 1;}}

绘制空气质量的box

//绘制空气质量  没啥好说的 就是判断 绘制 不合并private void onDrawWindyText(Canvas canvas, Rect rect, int i) {int bottom = (mHeight - bottomTextHeight) - DisplayUtil.dip2px(getContext(), 19);Rect boxRect = new Rect(rect.left, rect.top - DisplayUtil.dip2px(getContext(), 19), rect.right - 1, bottom);WeatherHourDetail WeatherHourDetail = listItems.get(i);Drawable drawable = ContextCompat.getDrawable(getContext(), R.drawable.shape_rangle_24_eeeeee);switch (WeatherHourDetail.getAirQuality().getQualityCode()) {case 0:drawable = ContextCompat.getDrawable(getContext(), R.drawable.shape_rangle_windlevel_1_default);break;case 1:drawable = ContextCompat.getDrawable(getContext(), R.drawable.shape_rangle_windlevel_2_default);break;case 2:drawable = ContextCompat.getDrawable(getContext(), R.drawable.shape_rangle_windlevel_3_default);break;case 3:drawable = ContextCompat.getDrawable(getContext(), R.drawable.shape_rangle_windlevel_4_default);break;case 4:drawable = ContextCompat.getDrawable(getContext(), R.drawable.shape_rangle_windlevel_5_default);break;case 5:drawable = ContextCompat.getDrawable(getContext(), R.drawable.shape_rangle_windlevel_6_default);break;}drawable.setBounds(boxRect);drawable.draw(canvas);
//        canvas.drawRoundRect(boxRect, 10, 10, bitmapPaint);}

绘制 指示器

/*** * @param canvas* @param rect* @param i* 画 指示器 与选中的风度*  就是获取x点 然后设置显示位置就行*/private void onDrawBox(Canvas canvas, Rect rect, int i) {if (i == currentItemIndex) {//获取y轴的方法 int Y = getTempBarY();//指示器图片Drawable drawable = ContextCompat.getDrawable(getContext(), R.drawable.indicator_24);//显示位置getScrollBarX 是 获取x轴点drawable.setBounds(getScrollBarX() - DisplayUtil.dip2px(getContext(), 9),Y - DisplayUtil.dip2px(getContext(), 9),getScrollBarX() + DisplayUtil.dip2px(getContext(), 9),Y + DisplayUtil.dip2px(getContext(), 9));drawable.draw(canvas);//选中的空气质量 就是判断就完事了Rect targetRect = new Rect(rect.left, rect.top - DisplayUtil.dip2px(getContext(), 46), rect.right, rect.top - DisplayUtil.dip2px(getContext(), 8));Paint.FontMetricsInt fontMetrics = textPaint.getFontMetricsInt();int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;textPaint.setTextAlign(Paint.Align.CENTER);WeatherHourDetail WeatherHourDetail = listItems.get(i);String text = "";switch (WeatherHourDetail.getAirQuality().getQualityCode()) {case 0:textPaint.setColor(getResources().getColor(R.color.color_50D29C));text = "优";break;case 1:textPaint.setColor(getResources().getColor(R.color.color_FFD012));text = "良";break;case 2:textPaint.setColor(getResources().getColor(R.color.color_F0B440));text = "轻度";break;case 3:textPaint.setColor(getResources().getColor(R.color.color_FFA531));text = "中度";break;case 4:textPaint.setColor(getResources().getColor(R.color.color_FF943F));text = "重度";break;case 5:textPaint.setColor(getResources().getColor(R.color.color_F65959));text = "严重";break;}canvas.drawText(text, targetRect.centerX(), baseline, textPaint);int bottom = (mHeight - bottomTextHeight) - DisplayUtil.dip2px(getContext(), 19);Rect boxRect = new Rect(rect.left, rect.top - DisplayUtil.dip2px(getContext(), 19), rect.right - 1, bottom);Drawable drawable2 = ContextCompat.getDrawable(getContext(), R.drawable.shape_rangle_24_eeeeee);switch (WeatherHourDetail.getAirQuality().getQualityCode()) {case 0:drawable2 = ContextCompat.getDrawable(getContext(), R.drawable.shape_rangle_windlevel_1);break;case 1:drawable2 = ContextCompat.getDrawable(getContext(), R.drawable.shape_rangle_windlevel_2);break;case 2:drawable2 = ContextCompat.getDrawable(getContext(), R.drawable.shape_rangle_windlevel_3);break;case 3:drawable2 = ContextCompat.getDrawable(getContext(), R.drawable.shape_rangle_windlevel_4);break;case 4:drawable2 = ContextCompat.getDrawable(getContext(), R.drawable.shape_rangle_windlevel_5);break;case 5:drawable2 = ContextCompat.getDrawable(getContext(), R.drawable.shape_rangle_windlevel_6);break;}drawable2.setBounds(boxRect);drawable2.draw(canvas);}}

绘制指示器描述的区域

/*** @param canvas* @param i      绘制指示器描述框*/private void onDrawTemp(Canvas canvas, int i) {WeatherHourDetail item = listItems.get(i);if (currentItemIndex == i) {//计算提示文字的运动轨迹int Y = getTempBarY();//画出背景图片 超过一半的话 改变背景方向if (i >= listItems.size() / 2) {Drawable drawable = ContextCompat.getDrawable(getContext(), R.drawable.shape_rangle_right_blue);drawable.setBounds(getScrollBarX() - DisplayUtil.dip2px(getContext(), 103),Y - DisplayUtil.dip2px(getContext(), 30),getScrollBarX() - DisplayUtil.dip2px(getContext(), 4),Y - DisplayUtil.dip2px(getContext(), 8));drawable.draw(canvas);} else {Drawable drawable = ContextCompat.getDrawable(getContext(), R.drawable.shape_rangle_left_blue);drawable.setBounds(getScrollBarX() + DisplayUtil.dip2px(getContext(), 4),Y - DisplayUtil.dip2px(getContext(), 30),getScrollBarX() + DisplayUtil.dip2px(getContext(), 99),Y - DisplayUtil.dip2px(getContext(), 8));drawable.draw(canvas);}//文字也是一样if (i >= listItems.size() / 2) {Rect targetRect = new Rect(getScrollBarX() - DisplayUtil.dip2px(getContext(), 105),Y - DisplayUtil.dip2px(getContext(), 30), getScrollBarX() + DisplayUtil.dip2px(getContext(), 5),Y - DisplayUtil.dip2px(getContext(), 8));Paint.FontMetricsInt fontMetrics = textPaint.getFontMetricsInt();int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;textPaint.setTextAlign(Paint.Align.CENTER);textPaint.setColor(getResources().getColor(R.color.white));canvas.drawText(item.getHourText() + " " + item.getWeatherText() + " " + item.getTemperature() + "°", targetRect.centerX(), baseline, textPaint);} else {Rect targetRect = new Rect(getScrollBarX() + DisplayUtil.dip2px(getContext(), 5), Y - DisplayUtil.dip2px(getContext(), 30), getScrollBarX() + DisplayUtil.dip2px(getContext(), 105), Y - DisplayUtil.dip2px(getContext(), 8));Paint.FontMetricsInt fontMetrics = textPaint.getFontMetricsInt();int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;textPaint.setTextAlign(Paint.Align.CENTER);textPaint.setColor(getResources().getColor(R.color.white));canvas.drawText(item.getHourText() + " " + item.getWeatherText() + " " + item.getTemperature() + "°", targetRect.centerX(), baseline, textPaint);}}}

x轴的点

   private int getScrollBarX() {int x = (int) (ITEM_SIZE * ITEM_WIDTH * scrollOffset / maxScrollOffset);x = x + MARGIN_LEFT_ITEM;return x;}

y轴

  //计算温度提示文字的运动轨迹private int getTempBarY() {int x = getScrollBarX();int sum = MARGIN_LEFT_ITEM;Point startPoint = null, endPoint;int i;for (i = 0; i < ITEM_SIZE; i++) {sum += ITEM_WIDTH;if (x < sum) {startPoint = listItems.get(i).tempPoint;break;}}if (i + 1 >= ITEM_SIZE || startPoint == null)return listItems.get(ITEM_SIZE - 1).tempPoint.y;endPoint = listItems.get(i + 1).tempPoint;Rect rect = listItems.get(i).windyBoxRect;int y = (int) (startPoint.y + (x - rect.left) * 1.0 / ITEM_WIDTH * (endPoint.y - startPoint.y));return y;}
通过滚动条偏移量计算当前选择的时刻
 private int calculateItemIndex() {int x = getScrollBarX();int sum = MARGIN_LEFT_ITEM + ITEM_WIDTH / 2;for (int i = 0; i < ITEM_SIZE; i++) {sum += ITEM_WIDTH;if (x + ITEM_WIDTH / 2 < sum) {return i;}}return ITEM_SIZE - 1;}
设置scrollerView的滚动条的位置,通过位置计算当前的时段
//设置scrollerView的滚动条的位置,通过位置计算当前的时段public void setScrollOffset(float offset, float maxScrollOffset) {this.maxScrollOffset = maxScrollOffset;scrollOffset = offset;int index = calculateItemIndex();currentItemIndex = index;invalidate();}

最后是控制横向滑动的IndexHorizontalScrollView

public class IndexHorizontalScrollView extends HorizontalScrollView {private static final String TAG = "IndexHorizontal";private Paint textPaint;private TwentyFourHourView today24HourView;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);init();}private void init() {textPaint = new Paint();textPaint.setTextSize(DisplayUtil.sp2px(getContext(), 12));textPaint.setAntiAlias(true);textPaint.setColor(new Color().WHITE);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);float offset = computeHorizontalScrollOffset();//最大值 减去 屏幕宽度 加上边距  控制内容可滑动范围float maxOffset = computeHorizontalScrollRange() - DisplayUtil.getScreenWidth(getContext()) + DisplayUtil.dip2px(getContext(), 36);if (today24HourView != null) {today24HourView.setScrollOffset(offset, maxOffset);}}public void setToday24HourView(TwentyFourHourView today24HourView) {this.today24HourView = today24HourView;}}

学习产出:

提示:这里统计学习计划的总量
例如:
1、 技术笔记 2 遍
2、CSDN 技术博客 3 篇
3、 学习的 vlog 视频 1 个

Android 仿墨迹天气24小时预报相关推荐

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

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

  2. android 墨迹天气背景,GitHub - xiangzhihong/android: 仿墨迹天气折线,以及背景滚动效果...

    ##实现效果 ##开源协议 Copyright (C) 2016 This program is free software; you can redistribute it and/or modif ...

  3. Android之高仿墨迹天气桌面组件(AppWidgetProvider) .

    Android之高仿墨迹天气桌面组件(AppWidgetProvider) . 点击:382 发布时间:2012-10-03 更多0 相信墨迹天气,大家都见过,他在时间显示和天气界面上,很吸引人,今天 ...

  4. 墨迹天气php,Android_仿墨迹天气在Android App中实现自定义zip皮肤更换,在这里谈一下墨迹天气的换肤 - phpStudy...

    仿墨迹天气在Android App中实现自定义zip皮肤更换 在这里谈一下墨迹天气的换肤实现方式,不过首先声明我只是通过反编译以及参考了一些网上其他资料的方式推测出的换肤原理, 在这里只供参考. 若大 ...

  5. (4.0.24.2)Android之桌面组件App Widget案例之高仿墨迹天气桌面组件

    相信墨迹天气,大家都见过,他在时间显示和天气界面上,很吸引人,今天我就来模仿一下墨迹天气的桌面组件,但是由于谷歌在天朝频频被墙的缘故,所以我在今天测试的时候,解析xml文件的网页打不开,所以天气显示出 ...

  6. android仿墨迹天气预报,手机天气预报 墨迹天气安卓版使用教程

    墨迹天气安卓版是一款免费的天气信息查询软件,目前它已经支持2488个城市,几乎覆盖国内所有的县级以上城市.作为一款拥有5000万用户的热门软件,很多用户都想深入了解墨迹天气的具体使用技巧.那么今天笔者 ...

  7. Android 打造自己的个性化应用(四):仿墨迹天气实现--自定义扩展名的zip格式的皮肤...

    在这里谈一下墨迹天气的换肤实现方式,不过首先声明我只是通过反编译以及参考了一些网上其他资料的方式推测出的换肤原理, 在这里只供参考. 若大家有更好的方式, 欢迎交流. 墨迹天气下载的皮肤就是一个zip ...

  8. android 自定义皮肤,仿墨迹天气在Android App中实现自定义zip皮肤更换

    在这里谈一下墨迹天气的换肤实现方式,不过首先声明我只是通过反编译以及参考了一些网上其他资料的方式推测出的换肤原理, 在这里只供参考. 若大家有更好的方式, 欢迎交流. 墨迹天气下载的皮肤就是一个zip ...

  9. 高仿墨迹天气 白天晴天

    简介 一直对墨迹天气的绚丽的场景蛮感兴趣的,趁有时间,自己就高仿了其中的一个场景,其他场景呢,也是类似的,主要是写对象的AI也就是逻辑了. 先看看效果吧,动态效果比较坑,太模糊 高清图 代码分析 来看 ...

最新文章

  1. Oracle 数据泵(IMPDP/EXPDP)导入导出总结
  2. mysql 获取昨天凌晨_MySQL慢日志体系建设
  3. 利用Linq2Sql+Juqery实现通用查询功能
  4. WCF分布式开发常见错误解决(1):添加服务引用出错
  5. 华为设备链路聚合配置命令
  6. 方立勋_30天掌握JavaWeb_回顾复习
  7. excel数据处理_如何用excel做仓库管理软件?这样做很好用,有权限和流程
  8. 按键精灵调用python文件_Python按键精灵自动化
  9. 计算机无法识别u盘,电脑无法识别U盘怎么办?简单搞定
  10. 提升测试效率都有哪些具体手段?
  11. 【转】ADW_Launcher
  12. 电池电压值转换为百分比
  13. LinuxShell作业题-1
  14. 怎样把m4a转换成mp3格式?
  15. Unity3D教程:触发器实现简单的场景跳转
  16. 模型驱动PDR、数据驱动PDR实验效果对比
  17. 解决Mac使用SVN命令出现xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools)的问题
  18. 小明的游戏(博弈论)
  19. 奥比中光网络深度摄像头——人脸活体检测
  20. 惠州学院c语言试卷,惠州学院高频试卷AB卷.doc

热门文章

  1. 机器学习 | 决策树原理剪枝连续值缺失值处理
  2. SAP对ERP系统的概述
  3. 合泰 HT66F2390 uart0与uart1 串口代码相互通信
  4. 伯克利的电气工程和计算机科学专业,Berkeley的Electrical Engineering Computer Sciences「加州大学伯克利分校电气工程与计算机科学系」...
  5. Beef-xss安装及使用
  6. 阿里云 mysql emoji_如何在 MySQL 中存储 emoji ?
  7. 星之匙,石之阶,火之行:我们能从EMUI 10.1读出什么
  8. 机器学习模型的集成方法总结:Bagging, Boosting, Stacking, Voting, Blending
  9. 普元eos使用svn_普元DevOps介绍
  10. 连连看游戏的核心算法