├── .gitignore ├── GraphView.iml ├── README.md ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── voyager │ │ └── graphview │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── voyager │ │ └── graphview │ │ ├── DensityUtils.java │ │ ├── GraphItem.java │ │ ├── GraphView.java │ │ └── MainActivity.java │ └── res │ ├── layout │ └── activity_main.xml │ ├── menu │ └── menu_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshots ├── QQ截图20150726150847.jpg └── QQ截图20150726150955.jpg └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | .DS_Store 6 | /build 7 | /captures 8 | -------------------------------------------------------------------------------- /GraphView.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GraphView 2 | 自定义控件——天气温度折线图 3 | >> 4 | # 效果图 5 | ![image](https://github.com/w19961009/GraphView/raw/master/screenshots/QQ截图20150726150847.jpg "效果图") 6 | ![image](https://github.com/w19961009/GraphView/raw/master/screenshots/QQ截图20150726150955.jpg "效果图") 7 | # 使用 8 | ### 布局文件 9 | 14 | ### JAVA代码 15 | ArrayList items = new ArrayList(); 16 | items.add(new GraphItem("昨天", 21.0f)); 17 | items.add(new GraphItem("今天", 30.0f)); 18 | items.add(new GraphItem("明天", 33.0f)); 19 | items.add(new GraphItem("后天", 26.0f)); 20 | GraphView my_gv_main = (GraphView) findViewById(R.id.my_gv_main); 21 | my_gv_main.setItems(items); 22 | # 备注 23 | Created by wuhaojie on 2015/7/25. -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 21 5 | buildToolsVersion "22.0.1" 6 | 7 | defaultConfig { 8 | applicationId "com.voyager.graphview" 9 | minSdkVersion 15 10 | targetSdkVersion 21 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile 'com.android.support:appcompat-v7:22.2.1' 25 | } 26 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in E:\Android_SDK\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/voyager/graphview/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.voyager.graphview; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/voyager/graphview/DensityUtils.java: -------------------------------------------------------------------------------- 1 | package com.voyager.graphview; 2 | 3 | import android.content.Context; 4 | 5 | /** 6 | * Created by wuhaojie on 2015/7/25. 7 | */ 8 | public class DensityUtils { 9 | 10 | /** 11 | * 根据手机的分辨率从 dip 的单位 转成为 px(像素) 12 | */ 13 | public static int dip2px(Context context, float dpValue) { 14 | final float scale = context.getResources().getDisplayMetrics().density; 15 | return (int) (dpValue * scale + 0.5f); 16 | } 17 | 18 | /** 19 | * 根据手机的分辨率从 px(像素) 的单位 转成为 dp 20 | */ 21 | public static int px2dip(Context context, float pxValue) { 22 | final float scale = context.getResources().getDisplayMetrics().density; 23 | return (int) (pxValue / scale + 0.5f); 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/voyager/graphview/GraphItem.java: -------------------------------------------------------------------------------- 1 | package com.voyager.graphview; 2 | 3 | /** 4 | * 坐标点类 5 | * Created by wuhaojie on 2015/7/25. 6 | */ 7 | public class GraphItem { 8 | private String xValue; 9 | private float yValue; 10 | 11 | public String getxValue() { 12 | return xValue; 13 | } 14 | 15 | public void setxValue(String xValue) { 16 | this.xValue = xValue; 17 | } 18 | 19 | public float getyValue() { 20 | return yValue; 21 | } 22 | 23 | public void setyValue(float yValue) { 24 | this.yValue = yValue; 25 | } 26 | 27 | public GraphItem(String xValue, float yValue) { 28 | 29 | this.xValue = xValue; 30 | this.yValue = yValue; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/voyager/graphview/GraphView.java: -------------------------------------------------------------------------------- 1 | package com.voyager.graphview; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.graphics.Point; 8 | import android.util.AttributeSet; 9 | import android.view.View; 10 | 11 | import java.util.ArrayList; 12 | 13 | /** 14 | * 自定义控件——曲线视图 15 | * Created by wuhaojie on 2015/7/25. 16 | */ 17 | public class GraphView extends View { 18 | 19 | /** 20 | * 竖直方向上与 View边界的间距,单位:dip 21 | */ 22 | private static final int VER_MARGIN = 25; 23 | /** 24 | * 水平方向上与 View边界的间距,单位:dip 25 | */ 26 | private static final int HOS_MARGIN = 8; 27 | /** 28 | * 坐标轴颜色 29 | */ 30 | private static final String AXIS_LINE_COLOR = "#7fffffff"; 31 | /** 32 | * 坐标轴宽度 33 | */ 34 | private static final int AXIS_LINE_WIDTH = 2; 35 | /** 36 | * 自变量显示文字大小 37 | */ 38 | private static final int X_VALUE_TEXTSIZE = 12; 39 | /** 40 | * 因变量极差均分的份数 41 | */ 42 | private static final float COPYS = 6.0f; 43 | /** 44 | * 线条宽度 45 | */ 46 | private static final int GRAPH_LINE_WIDTH = 3; 47 | /** 48 | * 线条颜色 49 | */ 50 | private static final String GRAPH_LINE_COLOR = "#7fffffff"; 51 | /** 52 | * 坐标点半径 53 | */ 54 | private static final int POINT_RADIUS = 3; 55 | /** 56 | * 坐标点文字颜色 57 | */ 58 | private static final String POINT_COLOR = "#28bbff"; 59 | /** 60 | * 坐标点文字偏移量 61 | */ 62 | private static final int POINT_TEXT_OFFSET = 10; 63 | /** 64 | * 上下文 65 | */ 66 | private Context context; 67 | /** 68 | * 坐标点集合 69 | */ 70 | private ArrayList items = null; 71 | /** 72 | * View的高度 73 | */ 74 | private int height = 0; 75 | /** 76 | * View的宽度 77 | */ 78 | private int width = 0; 79 | /** 80 | * 水平间距,单位:px 81 | */ 82 | private int hosMargin; 83 | /** 84 | * 竖直间距,单位:px 85 | */ 86 | private int verMargin; 87 | /** 88 | * 画笔 89 | */ 90 | private Paint paint; 91 | /** 92 | * 自变量坐标集合 93 | */ 94 | private ArrayList xPoints; 95 | /** 96 | * 坐标点集合 97 | */ 98 | private Point[] points; 99 | 100 | /** 101 | * 构造函数 102 | */ 103 | public GraphView(Context context, AttributeSet attrs) { 104 | super(context, attrs); 105 | this.context = context; 106 | } 107 | 108 | /** 109 | * 初始化视图 110 | */ 111 | private void initView() { 112 | height = getHeight(); 113 | width = getWidth(); 114 | hosMargin = DensityUtils.dip2px(context, HOS_MARGIN); 115 | verMargin = DensityUtils.dip2px(context, VER_MARGIN); 116 | paint = new Paint(); 117 | paint.setAntiAlias(true); 118 | } 119 | 120 | /** 121 | * 返回坐标点集合 122 | */ 123 | public ArrayList getItems() { 124 | return items; 125 | } 126 | 127 | /** 128 | * 设置坐标点集合 129 | */ 130 | public void setItems(ArrayList items) { 131 | this.items = items; 132 | } 133 | 134 | /** 135 | * 绘制 View 136 | */ 137 | @Override 138 | protected void onDraw(Canvas canvas) { 139 | if (items == null) { 140 | return; 141 | } 142 | initView(); 143 | drawGraphAxis(canvas); 144 | drawXValue(canvas); 145 | drawYValue(canvas); 146 | drawPoint(canvas); 147 | } 148 | 149 | /** 150 | * 绘制坐标点 151 | */ 152 | private void drawPoint(Canvas canvas) { 153 | paint.setStyle(Paint.Style.FILL); 154 | int yOffset = DensityUtils.dip2px(context, POINT_TEXT_OFFSET); 155 | for (int i = 0; i < points.length; i++) { 156 | paint.setColor(Color.parseColor(POINT_COLOR)); 157 | canvas.drawCircle(points[i].x, points[i].y, POINT_RADIUS, paint); 158 | paint.setColor(Color.WHITE); 159 | canvas.drawText(String.valueOf(items.get(i).getyValue()), points[i].x, points[i].y - yOffset, paint); 160 | } 161 | } 162 | 163 | /** 164 | * 绘制 y轴因变量 165 | */ 166 | private void drawYValue(Canvas canvas) { 167 | float yValue = 0; 168 | float max = items.get(0).getyValue(), min = items.get(0).getyValue(); //因变量的最值 169 | for (GraphItem item : items) { 170 | yValue = item.getyValue(); 171 | max = yValue > max ? yValue : max; 172 | min = yValue < min ? yValue : min; 173 | } 174 | float range = (max - min) > 0 ? (max - min) : COPYS; //极差 175 | max = max + range / COPYS; 176 | min = min - range / COPYS; 177 | points = getAllPoints(xPoints, max, min, height - 2 * verMargin - hosMargin, verMargin); 178 | paint.setStrokeWidth(GRAPH_LINE_WIDTH); 179 | paint.setColor(Color.parseColor(GRAPH_LINE_COLOR)); 180 | paint.setStyle(Paint.Style.STROKE); 181 | for (int i = 0; i < points.length - 1; i++) { 182 | canvas.drawLine(points[i].x, points[i].y, points[i + 1].x, points[i + 1].y, paint); 183 | } 184 | } 185 | 186 | /** 187 | * 获取所有坐标点 188 | * 189 | * @param xPoints 自变量值集合 190 | * @param max 因变量最大值 191 | * @param min 因变量最小值 192 | * @param graphHeight 图像高度范围 193 | * @param verMargin 图像最高点 194 | * @return 返回点阵集合 195 | */ 196 | private Point[] getAllPoints(ArrayList xPoints, float max, float min, int graphHeight, int verMargin) { 197 | Point[] points = new Point[items.size()]; 198 | int yRealHeight; 199 | for (int i = 0; i < items.size(); i++) { 200 | yRealHeight = (int) (graphHeight + verMargin - graphHeight * (items.get(i).getyValue() - min) / (max - min)); 201 | points[i] = new Point(xPoints.get(i), yRealHeight); 202 | } 203 | return points; 204 | } 205 | 206 | /** 207 | * 绘制 x轴自变量 208 | */ 209 | private void drawXValue(Canvas canvas) { 210 | paint.setColor(Color.WHITE); 211 | paint.setStrokeWidth(0); 212 | paint.setTextSize(DensityUtils.dip2px(context, X_VALUE_TEXTSIZE)); 213 | paint.setTextAlign(Paint.Align.CENTER); 214 | int period = (width - 2 * hosMargin) / items.size(); //平均间隔 215 | int startX = hosMargin * 3; 216 | xPoints = new ArrayList<>(); 217 | for (GraphItem item : items) { 218 | xPoints.add(startX); 219 | canvas.drawText(item.getxValue(), startX, verMargin / 2, paint); 220 | startX += period; 221 | } 222 | } 223 | 224 | /** 225 | * 绘制坐标轴 226 | */ 227 | private void drawGraphAxis(Canvas canvas) { 228 | paint.setColor(Color.parseColor(AXIS_LINE_COLOR)); 229 | paint.setStrokeWidth(DensityUtils.dip2px(context, AXIS_LINE_WIDTH)); 230 | paint.setStyle(Paint.Style.STROKE); 231 | canvas.drawLine(hosMargin, verMargin, width - hosMargin, verMargin, paint); 232 | int verUpMargin = height - hosMargin - verMargin; //在竖直方向与View上端边界的间距 233 | canvas.drawLine(hosMargin, verUpMargin, width - hosMargin, verUpMargin, paint); 234 | } 235 | 236 | } 237 | -------------------------------------------------------------------------------- /app/src/main/java/com/voyager/graphview/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.voyager.graphview; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | import android.widget.Button; 7 | import android.widget.EditText; 8 | import android.widget.Toast; 9 | 10 | import java.util.ArrayList; 11 | 12 | /** 13 | * @author wuhaojie 14 | */ 15 | public class MainActivity extends Activity implements View.OnClickListener { 16 | 17 | private GraphView my_gv_main; 18 | private Button btn_add; 19 | private EditText et_data; 20 | private ArrayList items; 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_main); 26 | 27 | items = new ArrayList(); 28 | items.add(new GraphItem("昨天", 21.0f)); 29 | items.add(new GraphItem("今天", 30.0f)); 30 | items.add(new GraphItem("明天", 33.0f)); 31 | items.add(new GraphItem("后天", 26.0f)); 32 | 33 | my_gv_main = (GraphView) findViewById(R.id.my_gv_main); 34 | et_data = (EditText) findViewById(R.id.et_data); 35 | btn_add = (Button) findViewById(R.id.btn_add); 36 | btn_add.setOnClickListener(this); 37 | 38 | my_gv_main.setItems(items); 39 | } 40 | 41 | @Override 42 | public void onClick(View v) { 43 | String data = et_data.getText().toString().trim(); 44 | if (data.isEmpty()) { 45 | Toast.makeText(MainActivity.this, "请输入数据", Toast.LENGTH_SHORT).show(); 46 | return; 47 | } 48 | String[] points = data.split(";"); 49 | items.add(new GraphItem(points[0], Float.parseFloat(points[1]))); 50 | my_gv_main.invalidate(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 11 | 12 | 13 | 18 | 19 | 26 | 27 |