This commit is contained in:
coco
2026-07-03 15:56:07 +08:00
commit caef23209c
5767 changed files with 1004268 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
/build
+30
View File
@@ -0,0 +1,30 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "25.0.3"
defaultConfig {
applicationId "com.jeanboy.linechart"
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
testCompile 'junit:junit:4.12'
}
+25
View File
@@ -0,0 +1,25 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in D:\Develop\Android\SDK/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
@@ -0,0 +1,26 @@
package com.jeanboy.linechart;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumentation test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.jeanboy.linechart", appContext.getPackageName());
}
}
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jeanboy.linechart">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
@@ -0,0 +1,577 @@
package com.jeanboy.linechart;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Created by jeanboy on 2017/6/12.
*/
public class LineChartView extends View {
private Paint linePaint;//曲线画笔
private Paint pointPaint;//曲线上锚点画笔
private Paint tablePaint;//表格画笔
private Paint textRulerPaint;//标尺文本画笔
private Paint textPointPaint;//曲线上锚点文本画笔
private Path linePath;//曲线路径
private Path tablePath;//表格路径
private int mWidth, mHeight;
private List<Data> dataList = new ArrayList<>();
private Point[] linePoints;
private int stepStart;
private int stepEnd;
private int stepSpace;
private int stepSpaceDefault = 10;
private int stepSpaceDP = stepSpaceDefault;//item宽度默认dp
private int topSpace, bottomSpace;
private int tablePadding;
private int tablePaddingDP = 20;//view四周padding默认dp
private int maxValue, minValue;
private int rulerValueDefault = 10;
private int rulerValue = rulerValueDefault;//刻度单位跨度
private int rulerValuePadding;//刻度单位与轴的间距
private int rulerValuePaddingDP = 8;//刻度单位与轴的间距默认dp
private float heightPercent = 0.618f;
private int lineColor = Color.parseColor("#286DD4");//曲线颜色
private float lineWidthDP = 2f;//曲线宽度dp
private int pointColor = Color.parseColor("#FF4081");//锚点颜色
private float pointWidthDefault = 8f;
private float pointWidthDP = pointWidthDefault;//锚点宽度dp
private int tableColor = Color.parseColor("#BBBBBB");//表格线颜色
private float tableWidthDP = 0.5f;//表格线宽度dp
private int rulerTextColor = tableColor;//表格标尺文本颜色
private float rulerTextSizeSP = 10f;//表格标尺文本大小
private int pointTextColor = Color.parseColor("#009688");//锚点文本颜色
private float pointTextSizeSP = 10f;//锚点文本大小
private boolean isShowTable = false;
private boolean isBezierLine = false;
private boolean isCubePoint = false;
private boolean isInitialized = false;
private boolean isPlayAnim = false;
private ValueAnimator valueAnimator;
private float currentValue = 0f;
private boolean isAnimating = false;
public LineChartView(Context context) {
this(context, null);
}
public LineChartView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LineChartView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setupView();
}
private void setupView() {
linePaint = new Paint();
linePaint.setAntiAlias(true);//抗锯齿
linePaint.setStyle(Paint.Style.STROKE);//STROKE描边FILL填充
linePaint.setColor(lineColor);
linePaint.setStrokeWidth(dip2px(lineWidthDP));//边框宽度
pointPaint = new Paint();
pointPaint.setAntiAlias(true);
pointPaint.setStyle(Paint.Style.FILL);
pointPaint.setColor(pointColor);
pointPaint.setStrokeWidth(dip2px(pointWidthDP));
tablePaint = new Paint();
tablePaint.setAntiAlias(true);
tablePaint.setStyle(Paint.Style.STROKE);
tablePaint.setColor(tableColor);
tablePaint.setStrokeWidth(dip2px(tableWidthDP));
textRulerPaint = new Paint();
textRulerPaint.setAntiAlias(true);
textRulerPaint.setStyle(Paint.Style.FILL);
textRulerPaint.setTextAlign(Paint.Align.CENTER);
textRulerPaint.setColor(rulerTextColor);//文本颜色
textRulerPaint.setTextSize(sp2px(rulerTextSizeSP));//字体大小
textPointPaint = new Paint();
textPointPaint.setAntiAlias(true);
textPointPaint.setStyle(Paint.Style.FILL);
textPointPaint.setTextAlign(Paint.Align.CENTER);
textPointPaint.setColor(pointTextColor);//文本颜色
textPointPaint.setTextSize(sp2px(pointTextSizeSP));//字体大小
linePath = new Path();
tablePath = new Path();
resetParam();
}
private void initAnim() {
valueAnimator = ValueAnimator.ofFloat(0f, 1f).setDuration(dataList.size() * 150);
valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentValue = (float) animation.getAnimatedValue();
postInvalidate();
}
});
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
currentValue = 0f;
isAnimating = true;
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
currentValue = 1f;
isAnimating = false;
isPlayAnim = false;
}
});
valueAnimator.setStartDelay(500);
}
private void resetParam() {
linePath.reset();
tablePath.reset();
stepSpace = dip2px(stepSpaceDP);
tablePadding = dip2px(tablePaddingDP);
rulerValuePadding = dip2px(rulerValuePaddingDP);
stepStart = tablePadding * (isShowTable ? 2 : 1);
stepEnd = stepStart + stepSpace * (dataList.size() - 1);
topSpace = bottomSpace = tablePadding;
linePoints = new Point[dataList.size()];
initAnim();
isInitialized = false;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = tablePadding + getTableEnd() + getPaddingLeft() + getPaddingRight();//计算自己的宽度
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);//父类期望的高度
if (MeasureSpec.EXACTLY == heightMode) {
height = getPaddingTop() + getPaddingBottom() + height;
}
setMeasuredDimension(width, height);//设置自己的宽度和高度
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.TRANSPARENT);//绘制背景颜色
canvas.translate(0f, mHeight / 2f + (getViewDrawHeight() + topSpace + bottomSpace) / 2f);//设置画布中心点垂直居中
if (!isInitialized) {
setupLine();
}
if (isShowTable) {
drawTable(canvas);//绘制表格
}
drawLine(canvas);//绘制曲线
drawLinePoints(canvas);//绘制曲线上的点
}
private void drawText(Canvas canvas, Paint textPaint, String text, float x, float y) {
canvas.drawText(text, x, y, textPaint);
}
/**
* 绘制标尺y轴文本
*
* @param canvas
* @param text
* @param x
* @param y
*/
private void drawRulerYText(Canvas canvas, String text, float x, float y) {
textRulerPaint.setTextAlign(Paint.Align.RIGHT);
Paint.FontMetrics fontMetrics = textRulerPaint.getFontMetrics();
float fontTotalHeight = fontMetrics.bottom - fontMetrics.top;
float offsetY = fontTotalHeight / 2 - fontMetrics.bottom;
float newY = y + offsetY;
float newX = x - rulerValuePadding;
drawText(canvas, textRulerPaint, text, newX, newY);
}
/**
* 绘制标尺x轴文本
*
* @param canvas
* @param text
* @param x
* @param y
*/
private void drawRulerXText(Canvas canvas, String text, float x, float y) {
textRulerPaint.setTextAlign(Paint.Align.CENTER);
Paint.FontMetrics fontMetrics = textRulerPaint.getFontMetrics();
float fontTotalHeight = fontMetrics.bottom - fontMetrics.top;
float offsetY = fontTotalHeight / 2 - fontMetrics.bottom;
float newY = y + offsetY + rulerValuePadding;
drawText(canvas, textRulerPaint, text, x, newY);
}
/**
* 绘制曲线上锚点文本
*
* @param canvas
* @param text
* @param x
* @param y
*/
private void drawLinePointText(Canvas canvas, String text, float x, float y) {
textPointPaint.setTextAlign(Paint.Align.CENTER);
float newY = y - rulerValuePadding;
drawText(canvas, textPointPaint, text, x, newY);
}
private int getTableStart() {
return isShowTable ? stepStart + tablePadding : stepStart;
}
private int getTableEnd() {
return isShowTable ? stepEnd + tablePadding : stepEnd;
}
/**
* 绘制背景表格
*
* @param canvas
*/
private void drawTable(Canvas canvas) {
int tableEnd = getTableEnd();
int rulerCount = maxValue / rulerValue;
int rulerMaxCount = maxValue % rulerValue > 0 ? rulerCount + 1 : rulerCount;
int rulerMax = rulerValue * rulerMaxCount + rulerValueDefault;
tablePath.moveTo(stepStart, -getValueHeight(rulerMax));//加上顶部的间隔
tablePath.lineTo(stepStart, 0);//标尺y轴
tablePath.lineTo(tableEnd, 0);//标尺x轴
int startValue = minValue - (minValue > 0 ? 0 : minValue % rulerValue);
int endValue = (maxValue + rulerValue);
//标尺y轴连接线
do {
int startHeight = -getValueHeight(startValue);
tablePath.moveTo(stepStart, startHeight);
tablePath.lineTo(tableEnd, startHeight);
//绘制y轴刻度单位
drawRulerYText(canvas, String.valueOf(startValue), stepStart, startHeight);
startValue += rulerValue;
} while (startValue < endValue);
canvas.drawPath(tablePath, tablePaint);
//绘制x轴刻度单位
drawRulerXValue(canvas);
}
/**
* 绘制标尺x轴上所有文本
*
* @param canvas
*/
private void drawRulerXValue(Canvas canvas) {
if (linePoints == null) return;
for (int i = 0; i < linePoints.length; i++) {
Point point = linePoints[i];
if (point == null) break;
drawRulerXText(canvas, String.valueOf(i), linePoints[i].x, 0);
}
}
/**
* 绘制曲线
*
* @param canvas
*/
private void drawLine(Canvas canvas) {
if (isPlayAnim) {
Path dst = new Path();
PathMeasure measure = new PathMeasure(linePath, false);
measure.getSegment(0, currentValue * measure.getLength(), dst, true);
canvas.drawPath(dst, linePaint);
} else {
canvas.drawPath(linePath, linePaint);
}
}
/**
* 绘制曲线上的锚点
*
* @param canvas
*/
private void drawLinePoints(Canvas canvas) {
if (linePoints == null) return;
float pointWidth = dip2px(pointWidthDP) / 2;
int pointCount = linePoints.length;
if (isPlayAnim) {
pointCount = Math.round(currentValue * linePoints.length);
}
for (int i = 0; i < pointCount; i++) {
Point point = linePoints[i];
if (point == null) break;
if (isCubePoint) {
canvas.drawPoint(point.x, point.y, pointPaint);
} else {
canvas.drawCircle(point.x, point.y, pointWidth, pointPaint);
}
//绘制点的文本
drawLinePointText(canvas, String.valueOf(dataList.get(i).getValue()), point.x, point.y);
}
}
/**
* 获取value值所占的view高度
*
* @param value
* @return
*/
private int getValueHeight(int value) {
float valuePercent = Math.abs(value - minValue) * 100f / (Math.abs(maxValue - minValue) * 100f);//计算value所占百分比
return (int) (getViewDrawHeight() * valuePercent + bottomSpace + 0.5f);//底部加上间隔
}
/**
* 获取绘制区域高度
*
* @return
*/
private float getViewDrawHeight() {
return getMeasuredHeight() * heightPercent;
}
/**
* 初始化曲线数据
*/
private void setupLine() {
if (dataList.isEmpty()) return;
int stepTemp = getTableStart();
Point pre = new Point();
pre.set(stepTemp, -getValueHeight(dataList.get(0).getValue()));//坐标系从0,0默认在第四象限绘制
linePoints[0] = pre;
linePath.moveTo(pre.x, pre.y);
if (dataList.size() == 1) {
isInitialized = true;
return;
}
for (int i = 1; i < dataList.size(); i++) {
Data data = dataList.get(i);
Point next = new Point();
next.set(stepTemp += stepSpace, -getValueHeight(data.getValue()));
if (isBezierLine) {
int cW = pre.x + stepSpace / 2;
Point p1 = new Point();//控制点1
p1.set(cW, pre.y);
Point p2 = new Point();//控制点2
p2.set(cW, next.y);
linePath.cubicTo(p1.x, p1.y, p2.x, p2.y, next.x, next.y);//创建三阶贝塞尔曲线
} else {
linePath.lineTo(next.x, next.y);
}
pre = next;
linePoints[i] = next;
}
isInitialized = true;
}
private int dip2px(float dipValue) {
final float scale = getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
}
private int sp2px(float spValue) {
final float fontScale = getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
private void refreshLayout() {
resetParam();
requestLayout();
postInvalidate();
}
/*-------------可操作方法---------------*/
/**
* 设置数据
*
* @param dataList
*/
public void setData(List<Data> dataList) {
if (dataList == null) {
throw new RuntimeException("dataList cannot is null!");
}
if (dataList.isEmpty()) return;
this.dataList.clear();
this.dataList.addAll(dataList);
maxValue = Collections.max(this.dataList, new Comparator<Data>() {
@Override
public int compare(Data o1, Data o2) {
return o1.getValue() - o2.getValue();
}
}).getValue();
minValue = Collections.min(this.dataList, new Comparator<Data>() {
@Override
public int compare(Data o1, Data o2) {
return o1.getValue() - o2.getValue();
}
}).getValue();
refreshLayout();
}
/**
* 设置是否显示标尺表格
*
* @param showTable
*/
public void setShowTable(boolean showTable) {
isShowTable = showTable;
refreshLayout();
}
/**
* 设置是否是贝塞尔曲线
*
* @param isBezier
*/
public void setBezierLine(boolean isBezier) {
isBezierLine = isBezier;
refreshLayout();
}
/**
* 设置锚点形状
*
* @param isCube
*/
public void setCubePoint(boolean isCube) {
isCubePoint = isCube;
refreshLayout();
}
/**
* 设置标尺y轴间距
*
* @param space
*/
public void setRulerYSpace(int space) {
if (space <= 0) {
space = rulerValueDefault;
}
this.rulerValue = space;
refreshLayout();
}
/**
* 设置曲线点的间距,标尺x轴间距
*
* @param dp
*/
public void setStepSpace(int dp) {
if (dp < stepSpaceDefault) {
dp = stepSpaceDefault;
}
this.stepSpaceDP = dp;
refreshLayout();
}
/**
* 设置锚点尺寸
*
* @param dp
*/
public void setPointWidth(float dp) {
if (dp <= 0) {
dp = pointWidthDefault;
}
this.pointWidthDP = dp;
refreshLayout();
}
/**
* 播放动画
*/
public void playAnim() {
this.isPlayAnim = true;
if (isAnimating) return;
if (valueAnimator != null) {
valueAnimator.start();
}
}
public static class Data {
int value;
public Data(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
}
@@ -0,0 +1,129 @@
package com.jeanboy.linechart;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.SeekBar;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
TextView tv_ruler_y;
SeekBar sb_ruler_space;
TextView tv_step_space;
SeekBar sb_step_space;
LineChartView lineChartView;
private int[] dataArr = new int[]{200, 100, 300, -20, 50, -80, 200, 100, 300, 50, 200, 150, 160, 100, 300, 50, 200, 150,
300, 50, 200, 100, 150, 150};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lineChartView = (LineChartView) findViewById(R.id.line_chart_view);
sb_ruler_space = (SeekBar) findViewById(R.id.sb_ruler_space);
tv_ruler_y = (TextView) findViewById(R.id.tv_ruler_y);
sb_step_space = (SeekBar) findViewById(R.id.sb_step_space);
tv_step_space = (TextView) findViewById(R.id.tv_step_space);
List<LineChartView.Data> datas = new ArrayList<>();
for (int value : dataArr) {
LineChartView.Data data = new LineChartView.Data(value);
datas.add(data);
}
lineChartView.setData(datas);
sb_ruler_space.setMax(70);
sb_ruler_space.setProgress(20);
if (lineChartView != null) {
lineChartView.setRulerYSpace(20);
tv_ruler_y.setText(String.valueOf(20));
}
sb_ruler_space.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (lineChartView != null) {
lineChartView.setRulerYSpace(progress);
tv_ruler_y.setText(String.valueOf(progress));
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
sb_step_space.setMax(70);
sb_step_space.setProgress(15);
if (lineChartView != null) {
lineChartView.setStepSpace(15);
tv_step_space.setText(String.valueOf(15));
}
sb_step_space.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (lineChartView != null) {
lineChartView.setStepSpace(progress);
tv_step_space.setText(String.valueOf(progress));
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
private boolean isShowTable = false;
public void tableToggle(View view) {
if (lineChartView != null) {
isShowTable = !isShowTable;
lineChartView.setShowTable(isShowTable);
}
}
private boolean isBezier = false;
public void bezierModelToggle(View view) {
if (lineChartView != null) {
isBezier = !isBezier;
lineChartView.setBezierLine(isBezier);
}
}
private boolean isCube = false;
public void pointModelToggle(View view) {
if (lineChartView != null) {
isCube = !isCube;
lineChartView.setCubePoint(isCube);
}
}
public void doAnimation(View view) {
if (lineChartView != null) {
lineChartView.playAnim();
}
}
}
@@ -0,0 +1,130 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.jeanboy.linechart.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:overScrollMode="never"
android:scrollbars="none">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.jeanboy.linechart.LineChartView
android:id="@+id/line_chart_view"
android:layout_width="wrap_content"
android:layout_height="300dp"/>
</LinearLayout>
</HorizontalScrollView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="doAnimation"
android:text="animation"/>
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="bezierModelToggle"
android:text="bezier Toggle"/>
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="pointModelToggle"
android:text="point Toggle"/>
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="tableToggle"
android:text="table Toggle"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="20dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="@dimen/activity_horizontal_margin">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Y轴标尺间距:"/>
<TextView
android:id="@+id/tv_ruler_y"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="20"/>
</LinearLayout>
<SeekBar
android:id="@+id/sb_ruler_space"
android:layout_width="match_parent"
android:layout_height="40dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="20dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="@dimen/activity_horizontal_margin">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="X轴标尺间距:"/>
<TextView
android:id="@+id/tv_step_space"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="15"/>
</LinearLayout>
<SeekBar
android:id="@+id/sb_step_space"
android:layout_width="match_parent"
android:layout_height="40dp"/>
</LinearLayout>
</FrameLayout>
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>
@@ -0,0 +1,5 @@
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>
@@ -0,0 +1,3 @@
<resources>
<string name="app_name">Android-LineChart</string>
</resources>
@@ -0,0 +1,11 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>
@@ -0,0 +1,17 @@
package com.jeanboy.linechart;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}