Android开发之天气趋势折线图

 更新时间:2016年08月05日 11:14:44   投稿:daisy  
在开发天气APP的时候会要显示多天的信息,所以加一个折线图来显示一下天气变化趋势是很不错的效果,本文详细介绍了开发过程,下面一起来看看。

先来看下效果:

控件内容比较简单,就是一个普通的折线图,上下分别带有数字,点击的时候显示当天温度的差值。 

创建一个类继承自View,并添加两个构造方法:

public class TrendGraph extends View {
  public TrendGraph(Context context) { // 在java代码中创建调用
    super(context);
  }

  public TrendGraph(Context context, AttributeSet attrs) { // 在xml中创建调用
    super(context, attrs);
  }
} 

因为这里不需要考虑wrap_content的情况,所以onMeasure方法不需重写,关键的是onDraw,而onDraw方法其实也不困难,只需要确定好每个点的具体位置就好,因为连线也是需要点的坐标,代码比较啰嗦,可以略过:

   @Override
   protected void onDraw(Canvas canvas) {
     super.onDraw(canvas);
     if (mElements == null || mElements.size() == 0) {
       return;
     }
     double max_up = getMaxUp();
     double min_down = getMinDown();
     canvas.setDrawFilter(mDrawFilter);
     mPaint.setStrokeWidth(lineWeith);
     float width = getWidth();
     float grap = width / mElements.size();
     float textSize = mTextPaint.getTextSize();
     int textMargin = circleRadius * 2;
     float margin_top = textSize + 2 * textMargin;
     Log.d(TAG, "onDraw: " + margin_top + "|" + textSize);
     float height = getHeight() - 2 * margin_top;
 
     for (int i = 0; i < mElements.size() - 1; i++) {
       float startX = i * grap + grap / 2;
       float stopX = (i + 1) * grap + grap / 2;
       float startY = (float) (max_up - mElements.get(i).getUp()) / (float) (max_up -
           min_down) * height + margin_top;
       float stopY = (float) (max_up - mElements.get(i + 1).getUp()) / (float) (max_up -
           min_down) * height + margin_top;
 
       canvas.drawText((int) mElements.get(i).getUp() + "℃", startX - textSize, startY -
           textMargin, mTextPaint);
       canvas.drawCircle(startX, startY, circleRadius, mPaint);
       canvas.drawLine(startX, startY, stopX, stopY, mPaint);
       if (i == mElements.size() - 2) {
         canvas.drawText((int) mElements.get(i + 1).getUp() + "℃", stopX - textSize, stopY
             - textMargin, mTextPaint);
         canvas.drawCircle(stopX, stopY, circleRadius, mPaint);
       }
 
       startY = (float) (max_up - mElements.get(i).getDown()) / (float) (max_up - min_down) *
           height + margin_top;
       stopY = (float) (max_up - mElements.get(i + 1).getDown()) / (float) (max_up -
           min_down) * height + margin_top;
       canvas.drawText((int) mElements.get(i).getDown() + "℃", startX - textSize, startY +
           textSize + textMargin, mTextPaint);
       canvas.drawCircle(startX, startY, circleRadius, mPaint);
       canvas.drawLine(startX, startY, stopX, stopY, mPaint);
       if (i == mElements.size() - 2) {
         canvas.drawText((int) mElements.get(i + 1).getDown() + "℃", stopX - textSize,
             stopY + textSize + textMargin, mTextPaint);
         canvas.drawCircle(stopX, stopY, circleRadius, mPaint);
       }
     }
   }

考虑到需要允许用户进行简单的设置,例如点的大小,文字大小等等,所以定义一些自定义属性(res/values/attr.xml):

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <declare-styleable name="TrendGraph">
    <attr name="lineWidth" format="dimension"/>
    <attr name="circleRadius" format="dimension" />
    <attr name="textSize" format="dimension" />
    <attr name="textColor" format="reference" />
  </declare-styleable>
</resources>

format指该属性的格式,指定为dimension则是尺寸,取值单位是dp、sp或px等等,而reference则是引用,即一般在xml中引用其他资源的写法,如@string/app_name。还有其他类型,可以自行查找文档。 

对自定义属性进行解析得到,这个解析需要在上面定义的第二个构造方法中进行,代码如下:

  public TrendGraph(Context context, AttributeSet attrs) {
    super(context, attrs);
    TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.TrendGraph);
    circleRadius = array.getDimensionPixelSize(R.styleable.TrendGraph_circleRadius, 5);
    lineWeith = array.getDimensionPixelSize(R.styleable.TrendGraph_lineWidth, 3);
    mTextPaint.setTextSize(array.getDimensionPixelSize(R.styleable.TrendGraph_textSize, 35));
    mTextPaint.setColor(array.getColor(R.styleable.TrendGraph_textColor, Color.BLACK));
    array.recycle();
  }

getDimensionPixelSize方法则是通过传入的值,转换为具体的像素(px)值,也就免去我们手动转换的麻烦。但是要注意,其中的defaultValue依然是px。

接着,就可以通过xml指定这些属性,在布局中加入命名空间:

Android Studio会自动引入,并且可以补全得到,具体使用:

  <com.fndroid.byweather.views.TrendGraph
    android:id="@+id/tg"
    android:layout_width="match_parent"
    app:textColor="@color/colorAccent"
    app:textSize="22sp"
    app:circleRadius="2dp"
    android:layout_height="200dp"/>

最后,添加一个事件监听,在点击View的时候进行回调:

① 定义接口:

  public interface onItemClickListener{
    void onItemClick(View view, Element element);
  }

② 在View中添加接口对象,并设置setter方法:

public class TrendGraph extends View {

  private onItemClickListener mOnItemClickListener;

  // 省略代码

  public void setOnItemClickListener(onItemClickListener onItemClickListener) {
    mOnItemClickListener = onItemClickListener;
  }

} 

③ 处理onTouchEvent,重写该方法,代码如下:

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    int viewWidth = getWidth();
    int itemWidth = viewWidth / mElements.size();
    int viewHeight = getHeight();
    boolean isMove = false; // 界面中最外层为一个NestedScrollView,所以为了避免滑动时也触发,加入变量处理

    switch (event.getAction()) {
      case MotionEvent.ACTION_MOVE:
        isMove = true;
        break;
      case MotionEvent.ACTION_UP:
        if (!isMove){ // 判断只有点击时进行回调
          int position = (int) (event.getX() / itemWidth); // 取得点击的位置
          mOnItemClickListener.onItemClick(this, mElements.get(position)); // 回调
        }
        break;
    }

    return true;
  }

④ 在Activity中,进行监听设置,并处理:

  historyGraph.setOnItemClickListener(this);
  @Override
  public void onItemClick(View view, TrendGraph.Element element) {
    int dt = (int) (element.getUp() - element.getDown());
    Snackbar.make(root, "当天温差为:" + dt + "℃", Snackbar.LENGTH_SHORT).show();
  }

总结

效果完成!如果有疑问欢迎大家交流讨论,希望本文对大家开发Android能有所帮助。

相关文章

  • Android控制闪光灯的方法(打开与关闭)

    Android控制闪光灯的方法(打开与关闭)

    这篇文章主要介绍了Android控制闪光灯的方法,可实现闪光灯打开与关闭的效果,涉及Android操作Camera拍照闪光灯的相关技巧,需要的朋友可以参考下
    2016-01-01
  • Android开发之ViewSwitcher用法实例

    Android开发之ViewSwitcher用法实例

    这篇文章主要介绍了Android开发之ViewSwitcher用法,结合实例形式分析了ViewSwitcher的功能、使用方法与相关注意事项,需要的朋友可以参考下
    2016-02-02
  • Android 超详细讲解fitsSystemWindows属性的使用

    Android 超详细讲解fitsSystemWindows属性的使用

    fitsSystemWindows属性可以让view根据系统窗口来调整自己的布局;简单点说就是我们在设置应用布局时是否考虑系统窗口布局,这里系统窗口包括系统状态栏、导航栏、输入法等,包括一些手机系统带有的底部虚拟按键
    2022-03-03
  • Android中EditText+Button组合导致输入板无法收起的原因分析及解决办法

    Android中EditText+Button组合导致输入板无法收起的原因分析及解决办法

    这篇文章主要介绍了Android中EditText+Button组合导致输入板无法收起的原因分析及解决办法的相关资料,需要的朋友可以参考下
    2016-01-01
  • android仿微信通讯录搜索示例(匹配拼音,字母,索引位置)

    android仿微信通讯录搜索示例(匹配拼音,字母,索引位置)

    本篇文章主要介绍了android仿微信通讯录搜索示例(匹配拼音,字母,索引位置),具有一定的参考价值,有兴趣的可以了解一下
    2017-09-09
  • Android微信签名知识的总结

    Android微信签名知识的总结

    这篇文章给大家详细总结了Android微信签名用到的知识,文中通过具体的实现过程给大家进行演示,相信对大家的理解很有帮助,下面来一起看看吧。
    2016-09-09
  • Android shape标签使用方法介绍

    Android shape标签使用方法介绍

    shape算是我们常用的一个标签,他可以生成线条,矩形, 圆形, 圆环,像我们圆角的按钮就可以通过shape来实现,最终Android会把这个带有shape标签的图片解析成一个Drawable对象,这个Drawable对象本质是GradientDrawable
    2022-09-09
  • 基于Android平台实现拼图小游戏

    基于Android平台实现拼图小游戏

    这篇文章主要为大家详细介绍了基于Android平台实现拼图小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-12-12
  • flutter PageView实现左右滑动切换视图

    flutter PageView实现左右滑动切换视图

    这篇文章主要为大家详细介绍了flutter PageView实现左右滑动切换视图,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-07-07
  • 超过百万的StackOverflow Flutter 20大问题(推荐)

    超过百万的StackOverflow Flutter 20大问题(推荐)

    这篇文章主要介绍了超过百万的StackOverflow Flutter 问题,有的问题在stackoverflow上有几十万的阅读量,说明很多人都遇到了这些问题,把这些问题整理分享给大家需要的朋友可以参考下
    2020-04-04

最新评论