Commit 0d07084b by 王宇航

首頁、廚房單打印

parent 70fa4320
apply plugin: 'com.android.library'
//apply plugin: 'com.github.dcendents.android-maven'
group='com.github.huangyanbin'
android {
compileSdkVersion 25
buildToolsVersion '26.0.2'
defaultConfig {
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 {
}
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in E:\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
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.david.chart">
<application
android:allowBackup="true"
android:label="@string/app_name"
android:supportsRtl="true">
</application>
</manifest>
package com.daivd.chart.component;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import com.daivd.chart.component.base.IChartTitle;
import com.daivd.chart.component.base.PercentComponent;
import com.daivd.chart.data.style.FontStyle;
/**
* 绘制标题
* @author huangyanbin
*/
public class ChartTitle extends PercentComponent<String> implements IChartTitle {
/**
* 图表标题最大占比
*/
private static final float MAX_PERCENT =0.4f;
/**
* 标题字体样式
*/
private FontStyle fontStyle= new FontStyle();
private Path path = new Path();
/**
* 设置标题占比
* @param percent 百分比
*/
@Override
public void setPercent(float percent) {
if(percent > MAX_PERCENT){
percent = MAX_PERCENT;
}
super.setPercent(percent);
}
/**
* 绘制标题
* <p>通过设置标题方位绘制标题</p>
* @param canvas 画布
* @param chartName 图表标题
* @param paint 画笔
*/
@Override
public void draw(Canvas canvas, String chartName, Paint paint) {
fontStyle.fillPaint(paint);
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
paint.setTextAlign(Paint.Align.LEFT);
float textHeight = fontMetrics.descent - fontMetrics.ascent;
int textWidth = (int)paint.measureText(chartName);
Rect rect = getRect();
int startY = rect.centerY();
int startX = rect.centerX();
path.rewind();
switch (direction) {
case TOP:
case BOTTOM:
startY-= textHeight/2;
startX -=textWidth/2;
canvas.drawText(chartName, startX, startY, paint);
break;
case LEFT:
case RIGHT:
path.moveTo(startX,rect.top);
path.lineTo(startX,rect.bottom);
canvas.drawTextOnPath(chartName,path,(rect.height()-textWidth)/2,0,paint);
break;
}
}
/**
* 获取标题字体样式
* @return 标题字体样式
*/
public FontStyle getFontStyle() {
return fontStyle;
}
/**
* 设置标题字体样式
* @param fontStyle 标题字体样式
*/
public void setFontStyle(FontStyle fontStyle) {
this.fontStyle = fontStyle;
}
}
package com.daivd.chart.component;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import com.daivd.chart.component.base.IEmpty;
import com.daivd.chart.data.style.FontStyle;
/**
* 空白提示
* @author huangyanbin
*/
public class EmptyView implements IEmpty {
/**
* 空白提示字体样式
*/
private FontStyle fontStyle = new FontStyle();
/**
* 空白文字
*/
private String emptyTip = "No Data";
/**
* 空白范围
*/
private Rect rect;
/**
* 绘制空白
* @param canvas 画布
* @param paint 画笔
*/
@Override
public void draw(Canvas canvas, Paint paint) {
draw(canvas,emptyTip,paint);
}
/**
* 获取空白字体样式
* @return 空白字体样式
*/
public FontStyle getFontStyle() {
return fontStyle;
}
/**
* 设置空白字体样式
* @param fontStyle 空白字体样式
*/
public void setFontStyle(FontStyle fontStyle) {
this.fontStyle = fontStyle;
}
/**
* 设置空白提示
* @return 空白提示
*/
@Override
public String getEmptyTip() {
return emptyTip;
}
/**
* 获取空白提示
* @param emptyTip 空白提示
*/
@Override
public void setEmptyTip(String emptyTip) {
this.emptyTip = emptyTip;
}
/**
* 计算空白大小
* <p>因为空白提示是占整个图表不需要计算</>
* @param chartRect 图表范围
*/
@Override
public void computeRect(Rect chartRect) {
rect = chartRect;
}
/**
* 绘制空白
* @param canvas 画布
* @param emptyTip 空白提示
* @param paint 画笔
*/
@Override
public void draw(Canvas canvas, String emptyTip, Paint paint) {
fontStyle.fillPaint(paint);
paint.setTextAlign(Paint.Align.LEFT);
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
int textHeight = (int) (fontMetrics.descent - fontMetrics.ascent);
int textWidth = (int) paint.measureText(emptyTip);
canvas.drawText(emptyTip,rect.left +(rect.right-rect.left-textWidth)/2,rect.top+(rect.bottom - rect.top-textHeight)/2,paint);
}
}
package com.daivd.chart.component;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import com.daivd.chart.component.base.ILegend;
import com.daivd.chart.component.base.PercentComponent;
import com.daivd.chart.data.ChartData;
import com.daivd.chart.data.ColumnData;
import com.daivd.chart.data.style.FontStyle;
import com.daivd.chart.listener.OnClickLegendListener;
import com.daivd.chart.provider.component.point.ILegendPoint;
import com.daivd.chart.provider.component.point.IPoint;
import com.daivd.chart.provider.component.point.LegendPoint;
import com.daivd.chart.provider.component.point.Point;
import java.util.List;
/**
* 图表图例
* @author huangyanbin
*/
public class Legend<C extends ColumnData> extends PercentComponent<ChartData<C>> implements ILegend<C> {
private FontStyle fontStyle;
private ILegendPoint point;
private int padding = 10;
private PointF pointF;
private boolean isSelectColumn = true;
private OnClickLegendListener<C> onClickLegendListener;
private boolean isDisplay = true;
public Legend(){
LegendPoint p = new LegendPoint();
p.getPointStyle().setWidth(p.getPointStyle().getWidth()*2);
point = p;
fontStyle = new FontStyle();
}
@Override
public void computeRect(Rect chartRect) {
if(isDisplay) {
super.computeRect(chartRect);
}
}
/**
* 绘制
* @param canvas 画布
* @param chartData 数据
* @param paint 画笔
*/
@Override
public void draw(Canvas canvas, ChartData<C> chartData, Paint paint) {
if(isDisplay) {
paint.setTextAlign(Paint.Align.LEFT);
Rect legendRect = getRect();
List<C> columnDataList = chartData.getColumnDataList();
int maxLegendNameLength = 0;
int columnDataSize = columnDataList.size();
String maxLengthColumnName = null;
for (int i = 0; i < columnDataSize; i++) {
ColumnData columnData = columnDataList.get(i);
String name = columnData.getName();
if (maxLegendNameLength < name.length()) {
maxLengthColumnName = name;
maxLegendNameLength = name.length();
}
}
fontStyle.fillPaint(paint);
int textWidth = (int) paint.measureText(maxLengthColumnName);//文本长度
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
float textHeight = fontMetrics.descent - fontMetrics.ascent;
int maxLegendLength = (int) (point.getWidth() + padding * 3 + textWidth);
int columnSize = legendRect.width() / maxLegendLength; //列
columnSize = columnSize > 0 ? columnSize : 1;
int rowSize = columnDataSize / columnSize;
rowSize = rowSize > 0 ? rowSize : 1;
int perHeight = (int) (textHeight + padding);
int perWidth = legendRect.width() / columnSize;
int offsetY = (legendRect.height() - perHeight * rowSize) / 2;
offsetY = offsetY > 0 ? offsetY : 0;
int offsetX = columnDataSize < columnSize ? (columnSize - columnDataSize) * perWidth / 2 : 0;
for (int i = 0; i < columnDataList.size(); i++) {
int column = i % columnSize;
int row = i / columnSize;
int startX = offsetX + legendRect.left + column * perWidth + (perWidth - maxLegendLength) / 2;
int startY = legendRect.top + offsetY + row * perHeight;
C columnData = columnDataList.get(i);
String name = columnData.getName();
float pointWidth = point.getWidth();
float pointHeight = point.getHeight();
if (pointF != null && isClickRect(startX - pointWidth, startX + perWidth, startY - padding / 2, startY + perHeight + padding / 2)) {
if (isSelectColumn) {
columnData.setDraw(!columnData.isDraw());
}
pointF = null;
if (onClickLegendListener != null) {
onClickLegendListener.onClickLegend(columnData, this);
}
}
paint.setColor(columnData.getColor());
drawPoint(canvas, columnData.isDraw(), startX, (int) (startY - textHeight / 2 + pointHeight / 2), paint);
startX += pointWidth + padding;
drawText(canvas, startX, startY, name, paint);
}
}
}
@Override
public void setFontStyle(FontStyle fontStyle) {
this.fontStyle = fontStyle;
}
/**
* Determine whether to click or not rect
*/
private boolean isClickRect(float left, float right, float top, float bottom){
if(pointF != null) {
return pointF.x >= left && pointF.x <= right && pointF.y >= top && pointF.y <= bottom;
}
return false;
}
/**
* 绘制文字
*/
private void drawText(Canvas canvas, int startX, int startY, String content, Paint paint) {
fontStyle.fillPaint(paint);
canvas.drawText(content, startX, startY, paint);
}
private void drawPoint(Canvas canvas,boolean isDraw,int x, int y, Paint paint){
float w = point.getWidth();
x += w/2;
point.drawPoint(canvas,x,y,0,!isDraw,paint);
}
@Override
public FontStyle getFontStyle() {
return fontStyle;
}
@Override
public void onClickLegend(PointF pointF) {
this.pointF = pointF;
}
@Override
public void setOnClickLegendListener(OnClickLegendListener<C> onClickLegendListener) {
this.onClickLegendListener = onClickLegendListener;
}
@Override
public int getPadding() {
return padding;
}
@Override
public void setPadding(int padding) {
this.padding = padding;
}
public IPoint getPoint() {
return point;
}
public void setPoint(ILegendPoint point) {
this.point = point;
}
@Override
public void setSelectColumn(boolean selectColumn) {
isSelectColumn = selectColumn;
}
@Override
public void setDisplay(boolean isDisplay) {
this.isDisplay = isDisplay;
}
}
package com.daivd.chart.component;
import android.content.ContentValues;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Environment;
import android.provider.MediaStore;
import android.view.View;
import com.daivd.chart.data.PicOption;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* Created by huang on 2017/10/30.
* 图片生成
*/
public class PicGeneration<V extends View> {
public boolean save(V v,PicOption option){
getChartBitmap(v);
return saveToGallery(v,option);
}
private Bitmap getChartBitmap(V v) {
// 创建一个bitmap 根据我们自定义view的大小
Bitmap returnedBitmap = Bitmap.createBitmap(v.getWidth(),
v.getHeight(), Bitmap.Config.RGB_565);
// 绑定canvas
Canvas canvas = new Canvas(returnedBitmap);
// 获取视图的背景
Drawable bgDrawable = v.getBackground();
if (bgDrawable != null)
// 如果有就绘制
bgDrawable.draw(canvas);
else
// 没有就绘制白色
canvas.drawColor(Color.WHITE);
// 绘制
v.draw(canvas);
return returnedBitmap;
}
private boolean saveToGallery(V v, PicOption option) {
String mFilePath;
// 控制图片质量
if (option.getQuality() < 0 || option.getQuality() > 100)
option.setQuality(50);
long currentTime = System.currentTimeMillis();
File extBaseDir = Environment.getExternalStorageDirectory();
File file = new File(extBaseDir.getAbsolutePath() + "/DCIM/" + option.getSubFolderPath());
if (!file.exists()) {
if (!file.mkdirs()) {
return false;
}
}
String mimeType = "";
String fileName = option.getFileName();
switch (option.getFormat()) {
case PNG:
mimeType = "image/png";
if (!fileName.endsWith(".png"))
fileName += ".png";
break;
case WEBP:
mimeType = "image/webp";
if (!fileName.endsWith(".webp"))
fileName += ".webp";
break;
case JPEG:
default:
mimeType = "image/jpeg";
if (!(fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")))
fileName += ".jpg";
break;
}
mFilePath = file.getAbsolutePath() + "/" + fileName;
FileOutputStream out = null;
try {
out = new FileOutputStream(mFilePath);
Bitmap b = getChartBitmap(v);
b.compress(option.getFormat(), option.getQuality(), out);
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
long size = new File(mFilePath).length();
ContentValues values = new ContentValues(8);
// store the details
values.put(MediaStore.Images.Media.TITLE, fileName);
values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
values.put(MediaStore.Images.Media.DATE_ADDED, currentTime);
values.put(MediaStore.Images.Media.MIME_TYPE, mimeType);
values.put(MediaStore.Images.Media.DESCRIPTION, option.getFileDescription());
values.put(MediaStore.Images.Media.ORIENTATION, 0);
values.put(MediaStore.Images.Media.DATA, mFilePath);
values.put(MediaStore.Images.Media.SIZE, size);
return v.getContext().getContentResolver().
insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values) != null;
}
}
package com.daivd.chart.component.axis;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.view.Gravity;
import com.daivd.chart.component.base.IAxis;
import com.daivd.chart.data.ChartData;
import com.daivd.chart.data.format.IFormat;
import com.daivd.chart.data.BarData;
import com.daivd.chart.data.ScaleData;
import com.daivd.chart.data.style.FontStyle;
import com.daivd.chart.data.style.LineStyle;
import com.daivd.chart.matrix.MatrixHelper;
/**
* 基本轴 负责轴的计算方位和绘制
* @author huangyanbin
*/
public abstract class BaseAxis<V> implements IAxis<V> {
/**
* 轴线样式
*/
protected LineStyle axisStyle = new LineStyle();
/**
* 刻度样式
*/
protected FontStyle scaleStyle = new FontStyle();
/**
* 网格样式
*/
protected LineStyle gridStyle = new LineStyle(); //网格样式
/**
* 是否绘制网格
*/
protected boolean isDrawGrid; //是否绘制网格
/**
* 是否显示轴线
*/
protected boolean isShowAxisLine = true;
/**
* 轴文字位置
*/
protected int gravity = Gravity.CENTER;
/**
* 轴方位(上下左右,横向轴可以设置上下,竖轴可以设置左右)
*/
protected int direction;
/**
* 是否是线性图
* 因为线性图一般从0开始绘制轴文字,而柱状图从每个间隔中间开始绘制
*/
protected boolean isLine;
/**
* 文字格式化
*/
private IFormat<V> format;
/**
* 是否显示轴
*/
private boolean isDisplay = true;
/**
*设置是否绘制网格
* @param drawGrid 是否绘制
*/
public void setDrawGrid(boolean drawGrid) {
isDrawGrid = drawGrid;
}
/**
* 获取轴样式
* 默认赋予样式,可以直接get来使用
* @return 轴样式
*/
public LineStyle getAxisStyle() {
return axisStyle;
}
/**
* 设置轴样式
* 如果你对LineStyle提供的样式不满意,可以自定义新的LineStyle
* @param axisStyle 轴样式
*/
public void setAxisStyle(LineStyle axisStyle) {
this.axisStyle = axisStyle;
}
/**
* 获取刻度字体样式
* 默认赋予样式,可以直接get来使用
* @return 刻度字体样式
*/
public FontStyle getScaleStyle() {
return scaleStyle;
}
/**
* 设置刻度字体样式
* 如果你对FontStyle提供的样式不满意,可以自定义新的FontStyle
* @param scaleStyle 刻度字体样式
*/
public void setScaleStyle(FontStyle scaleStyle) {
this.scaleStyle = scaleStyle;
}
/**
* 获取网格样式
* @return 网格样式
*/
public LineStyle getGridStyle() {
return gridStyle;
}
/**
* 设置网格样式
* @param gridStyle 网格样式
*/
public void setGridStyle(LineStyle gridStyle) {
this.gridStyle = gridStyle;
}
/**
* 设置轴偏移方位
* @param gravity 偏移方位
*/
public void setGravity(int gravity) {
this.gravity = gravity;
}
/**
* 是否是线性图表
* 因为线性图一般从0开始绘制轴文字,而柱状图从每个间隔中间开始绘制
* @param isLine 是否是线性图表
*/
public void isLine(boolean isLine) {
this.isLine = isLine;
}
/**
* 是否显示轴
* @return 是否显示轴
*/
public boolean isDisplay() {
return isDisplay;
}
/**
* 绘制轴
* 判断是否需要绘制以及调用绘制轴刻度和绘制轴线
* @param canvas 画布
* @param rect 绘制范围
* @param helper 缩放移动辅助类
* @param paint 画笔
* @param chartData 图表数据
*/
@Override
public void draw(Canvas canvas, Rect rect, MatrixHelper helper, Paint paint, ChartData<? extends BarData> chartData) {
if(isDisplay) {
drawScale(canvas, rect, helper, paint, chartData);
drawAxis(canvas, rect, paint, chartData);
}
}
/**
* 绘制轴刻度
* @param canvas 画布
* @param rect 绘制范围
* @param helper 缩放移动辅助类
* @param paint 画笔
* @param chartData 图表数据
*/
protected void drawScale(Canvas canvas, Rect rect, MatrixHelper helper, Paint paint, ChartData<? extends BarData> chartData) {
ScaleData scaleData = chartData.getScaleData();
Rect clipRect = new Rect(rect);
Rect scaleRect = scaleData.scaleRect;
Rect zoomRect = helper.getZoomProviderRect(scaleData.getOffsetRect(new Rect(rect), scaleRect));
chartData.getScaleData().zoom = helper.getZoom();
if (direction == AxisDirection.BOTTOM || direction == AxisDirection.TOP) {
zoomRect.top = rect.top;
zoomRect.bottom = rect.bottom;
clipRect.left = rect.left + scaleRect.left;
clipRect.right = rect.right - scaleRect.right;
} else {
zoomRect.left = rect.left;
zoomRect.right = rect.right;
clipRect.top = rect.top + scaleRect.top;
clipRect.bottom = rect.bottom - scaleRect.bottom;
}
drawScale(canvas, zoomRect, clipRect, paint, chartData);
}
/**
* 绘制轴线
* @param canvas 画布
* @param rect 绘制范围
* @param paint 画笔
* @param chartData 图表数据
*/
protected void drawAxis(Canvas canvas, Rect rect, Paint paint, ChartData<? extends BarData> chartData) {
if(isShowAxisLine) {
Rect scaleRect = chartData.getScaleData().scaleRect;
axisStyle.fillPaint(paint);
int[] r = calculation(rect, scaleRect);
Path path = new Path();
path.moveTo(r[0],r[1]);
path.lineTo(r[2],r[3]);
canvas.drawPath(path, paint);
}
}
/**
* 计算轴范围
* @param rect 原始范围
* @param scaleRect 缩放范围
* @return 轴上下左右的大小
*/
protected abstract int[] calculation(Rect rect, Rect scaleRect);
/**
* 提供给子类绘制刻度抽象方法
* @param canvas 画布
* @param rect 原始范围
* @param clipRect 裁切轴之后范围
* @param paint 画笔
* @param chartData 图表数据
*/
protected abstract void drawScale(Canvas canvas, Rect rect, Rect clipRect, Paint paint,ChartData<? extends BarData> chartData);
/**
* 获取文字格式化
* @return 文字格式化
*/
public IFormat<V> getFormat() {
return format;
}
/**
* 设置文字格式化
* @param format 文字格式化
*/
@Override
public void setFormat(IFormat<V> format) {
this.format = format;
}
/**
* 是否显示轴线
* @return 是否显示轴线
*/
public boolean isShowAxisLine() {
return isShowAxisLine;
}
/**
* 设置是否显示轴线
* @param showAxisLine 是否显示轴线
*/
public void setShowAxisLine(boolean showAxisLine) {
isShowAxisLine = showAxisLine;
}
/**
* 设置是否显示轴
* @param isShow 是否显示轴
*/
@Override
public void setDisplay(boolean isShow) {
this.isDisplay = isShow;
}
}
package com.daivd.chart.component.axis;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.view.Gravity;
import com.daivd.chart.data.ChartData;
import com.daivd.chart.data.BarData;
import com.daivd.chart.data.ScaleData;
import com.daivd.chart.exception.ChartException;
import java.util.List;
/**
* 图表横轴
* @author huangyanbin
*/
public class HorizontalAxis extends BaseAxis<String> {
/**
* 横轴默认构造方法
* 默认方位在底部
*/
public HorizontalAxis() {
direction = AxisDirection.BOTTOM;
}
/**
* 文字旋转角度
*/
private int rotateAngle= 0;
/**
* 文字是否旋转
*/
private boolean isRotateAngle;
/**
* 文字宽度
*/
private int textWidth;
/**
* 文字高度
*/
private int textHeight;
/**
* 旋转文字高度
*/
private int rotateTextHeight;
/**
* 是否需要偏移来完整显示左右两边文字
*/
private boolean isShowFullValue; //是否显示全文字
/**
* 刻度数据
*/
private ScaleData scaleData;
/**
* 计算刻度大小
* <p>通过计算刻度的宽高得到轴的大小,然后保存到scaleData对象中,以便后面的计算</p>
* @param chartData 图表数据
* @param rect 图表原始范围
* @param paint 画笔
*/
@Override
public void computeScale(ChartData<? extends BarData> chartData, Rect rect, Paint paint) {
if(isDisplay()) {
scaleData = chartData.getScaleData();
scaleStyle.fillPaint(paint);
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
textHeight = (int) (fontMetrics.descent - fontMetrics.ascent);
int maxLength = 0;
String maxLengthXData = "1";
for (String xData : chartData.getCharXDataList()) {
String formatData = formatData(xData);
if (maxLength < formatData.length()) {
maxLengthXData = formatData;
maxLength = formatData.length();
}
}
textWidth = (int) paint.measureText(maxLengthXData);
//计算旋转的高宽
int dis = textHeight;
if (isRotateAngle) {
int tempHeight = (int) Math.abs(textWidth * Math.sin(rotateAngle * Math.PI / 180)
+ textHeight * Math.cos(rotateAngle * Math.PI / 180));
int tempWidth = (int) Math.abs(textWidth * Math.cos(rotateAngle * Math.PI / 180)
+ textHeight * Math.sin(rotateAngle * Math.PI / 180));
rotateTextHeight = tempHeight;
dis += rotateTextHeight;
textWidth = tempWidth;
}
dis += (int) (scaleStyle.getPadding() * 2 + axisStyle.getWidth());
if (direction == AxisDirection.BOTTOM) {
scaleData.scaleRect.bottom = dis;
} else {
scaleData.scaleRect.top = dis;
}
}
}
/**
* 绘制刻度
* <p>通过zoomRect计算出每个刻度的宽度,迭代绘制刻度</p>
* @param canvas 画布
* @param zoomRect 缩放之后范围
* @param rect 原始范围
* @param paint 画笔
* @param chartData 图表数据
*/
protected void drawScale(Canvas canvas, Rect zoomRect, Rect rect, Paint paint, ChartData<? extends BarData> chartData) {
ScaleData scaleData = chartData.getScaleData();
List<String> groupDataList = chartData.getCharXDataList();
int rowSize = scaleData.rowSize;
int groupSize = groupDataList.size();
if (groupSize != rowSize) {
throw new ChartException("Horizontal Vertical axis data inconsistency");
}
float startY;
if (direction == AxisDirection.BOTTOM) {
startY = zoomRect.bottom -scaleData.scaleRect.bottom/2;
} else {
startY = zoomRect.top + scaleData.scaleRect.top/2;
}
int left = zoomRect.left ;
int width = zoomRect.right - left;
double perWidth = ((double) width) / (isLine? groupSize -1 : groupSize);
int filterMultiple = (int) (textWidth / perWidth +1);
for (int i = 0; i < groupSize; i++) {
String content = groupDataList.get(i);
int startX = getGravityStartX(left, i, perWidth);
//留1px缓冲
if (startX >= rect.left-1 && startX<= rect.right+1) {
if( i % filterMultiple == 0) {
drawText(canvas, content,startX, startY,i, paint);
drawGrid(canvas, startX, rect, scaleData.scaleRect, paint);
}
}
}
}
/**
* 获取刻度起始X的位置
* <p>根据gravity来判断偏移的值</p>
* @param left 左边
* @param position 位置
* @param perWidth 每个刻度的宽度
* @return 刻度起始X的位置
*/
private int getGravityStartX(int left, int position, double perWidth) {
int startX = (int) (left + position * perWidth);
if (gravity == Gravity.CENTER) {
startX += perWidth / 2;
} else if (gravity == Gravity.RIGHT) {
startX += perWidth;
}
return startX;
}
/**
* 绘制文字
* <p>完成文字偏移,文字旋转,绘制</p>
* @param canvas 画布
* @param contentStr 文字内容
* @param startX 文字绘制起始X位置
* @param startY 文字绘制起始Y位置
* @param position 刻度序号
* @param paint 画笔
*/
private void drawText(Canvas canvas, String contentStr, int startX, float startY, int position, Paint paint) {
String content = formatData(contentStr);
scaleStyle.fillPaint(paint);
paint.setTextAlign(Paint.Align.CENTER);
if (isShowFullValue && position == 0) {
int width = (int) paint.measureText(content);
startX+= width/2;
} else if (isShowFullValue && position == scaleData.rowSize - 1) {
int width = (int) paint.measureText(content);
startX-= width/2;
}
if (isRotateAngle) {
canvas.save();
canvas.rotate(rotateAngle, startX, startY);
canvas.drawText(content, startX, startY + textHeight / 2, paint);
canvas.restore();
} else {
canvas.drawText(content, startX, startY + textHeight / 2, paint);
}
}
/**
* 格式化文字
* @param data 文字
* @return 格式化完成之后的文字
*/
private String formatData(String data){
return getFormat()!= null ? getFormat().format(data) :data;
}
/**
* 绘制竖向网格
* @param canvas 画布
* @param startX 网格起始X位置
* @param rect 原始范围
* @param scaleRect 缩放范围
* @param paint 画布
*/
public void drawGrid(Canvas canvas, float startX, Rect rect, Rect scaleRect, Paint paint) {
if (gridStyle != null && isDrawGrid) {
gridStyle.fillPaint(paint);
Path path = new Path();
path.moveTo(startX, rect.top + scaleRect.top);
path.lineTo(startX, rect.bottom - scaleRect.bottom);
canvas.drawPath(path, paint);
}
}
/**
* 计算出裁切轴之后的范围
* @param rect 原始范围
* @param scaleRect 缩放范围
* @return 上下左右的大小
*/
protected int[] calculation(Rect rect, Rect scaleRect) {
int startX = rect.left + scaleRect.left;
int endX = rect.right - scaleRect.right;
int startY, endY;
if (direction == AxisDirection.BOTTOM) {
startY = rect.bottom - scaleRect.bottom;
} else {
startY = rect.top + scaleRect.top;
}
endY = startY;
return new int[]{startX, startY, endX, endY};
}
/**
* 设置轴方位
* <p>横轴只能设置上下方位</p>
* @param axisDirection 轴方位
*
*/
@Override
public void setAxisDirection(int axisDirection) {
if (axisDirection == AxisDirection.BOTTOM || axisDirection == AxisDirection.TOP) {
this.direction = axisDirection;
} else throw new ChartException("Can only set BOTTOM, TOP direction");
}
/**
* 设置文字旋转角度
* @param rotateAngle
*/
public void setRotateAngle(int rotateAngle) {
isRotateAngle = true;
this.rotateAngle = rotateAngle;
}
/**
* 是否需要偏移来完整显示左右两边文字
*
* @return 是否需要偏移
*/
public boolean isShowFullValue() {
return isShowFullValue;
}
/**
* 设置是否需要偏移来完整显示左右两边文字
*
* @param showFullValue 设置是否需要偏移
*/
public void setShowFullValue(boolean showFullValue) {
isShowFullValue = showFullValue;
}
}
package com.daivd.chart.component.axis;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import com.daivd.chart.data.ChartData;
import com.daivd.chart.data.BarData;
import com.daivd.chart.data.ScaleData;
import com.daivd.chart.data.ScaleSetData;
import com.daivd.chart.exception.ChartException;
import java.util.List;
/**
* 图表竖轴
* @author huangyanbin
*/
public class VerticalAxis extends BaseAxis<Double> {
/**
* 图表刻度设置数据
* <p>用于保存用户设置最大,最小等数据</p>
*/
private ScaleSetData scaleSetData = new ScaleSetData();
/**
* 横轴构造方法
* <p>可以设置横轴方向 左右方位</p>
* @param direction 方位
*/
public VerticalAxis(int direction) {
this.direction = direction;
}
/**
* 小数点格式化
*/
private java.text.DecimalFormat df = new java.text.DecimalFormat("0");
/**
* 计算刻度大小
* <p>通过计算刻度的宽高得到轴的大小,然后保存到scaleData对象中,以便后面的计算</p>
* @param chartData 图表数据
* @param rect 图表原始范围
* @param paint 画笔
*/
@Override
public void computeScale(ChartData<? extends BarData> chartData, Rect rect, Paint paint) {
if(isDisplay()) {
ScaleData scaleData = chartData.getScaleData();
scaleData.resetScale(scaleSetData, direction);
scaleStyle.fillPaint(paint);
int length = Math.max(formatVerticalAxisData(scaleData.getMaxScaleValue(direction)).length(),
formatVerticalAxisData(scaleData.getMinScaleValue(direction)).length());
int textHeight = (int) (paint.measureText("1", 0, 1) * length);
int dis = (int) (textHeight + scaleStyle.getPadding() + axisStyle.getWidth());
if (direction == AxisDirection.LEFT) {
scaleData.scaleRect.left = dis;
} else {
scaleData.scaleRect.right = dis;
}
}
}
/**
* 绘制刻度
* <p>通过zoomRect计算出每个刻度的宽度,迭代绘制刻度</p>
* @param canvas 画布
* @param zoomRect 缩放之后范围
* @param clipRect 原始范围
* @param paint 画笔
* @param chartData 图表数据
*/
@Override
protected void drawScale(Canvas canvas, Rect zoomRect, Rect clipRect, Paint paint, ChartData<? extends BarData> chartData) {
ScaleData scaleData = chartData.getScaleData();
List<Double> scaleList = scaleData.getScaleList(direction);
float startX;
if (direction == AxisDirection.LEFT) {
startX = zoomRect.left + scaleData.scaleRect.right + scaleStyle.getPadding();
} else {
startX = zoomRect.right - scaleData.scaleRect.right + scaleStyle.getPadding();
}
int bottom = zoomRect.bottom;
int height = zoomRect.height();
float textHeight = paint.measureText("1", 0, 1);
int perHeight = height / (scaleList.size()-1);
for (int i = 0; i < scaleList.size(); i++) {
double value = scaleList.get(i);
float startY = bottom - i * perHeight;
if (clipRect.contains(clipRect.centerX(), (int) startY-1)) {
drawText(canvas, startX, startY + textHeight / 2, value, paint);
drawGrid(canvas, startY, zoomRect, scaleData.scaleRect, paint);
}
}
}
/**
* 绘制文字
*/
private void drawText(Canvas canvas, float startX, float startY, double value, Paint paint) {
scaleStyle.fillPaint(paint);
String content = formatVerticalAxisData(value);
paint.setTextAlign(Paint.Align.LEFT);
canvas.drawText(content, startX, startY, paint);
}
/**
* 绘制网格
*/
private void drawGrid(Canvas canvas, float startY, Rect rect, Rect scaleRect, Paint paint) {
if (gridStyle != null && isDrawGrid) {
gridStyle.fillPaint(paint);
Path path = new Path();
path.moveTo(rect.left + scaleRect.left, startY);
path.lineTo(rect.right - scaleRect.right, startY);
canvas.drawPath(path, paint);
}
}
/**
* 计算出裁切轴之后的范围
* @param rect 原始范围
* @param scaleRect 缩放范围
* @return 上下左右的大小
*/
protected int[] calculation(Rect rect, Rect scaleRect) {
int startY = rect.top + scaleRect.top;
int endY = rect.bottom - scaleRect.bottom;
int startX, endX;
if (direction == AxisDirection.LEFT) {
startX = rect.left + scaleRect.left;
} else {
startX = rect.right - scaleRect.right;
}
endX = startX;
return new int[]{startX, startY, endX, endY};
}
/**
* 设置轴方位
* <p>竖轴只能设置左右方位</p>
* @param axisDirection 轴方位
*
*/
@Override
public void setAxisDirection(int axisDirection) {
if (axisDirection == AxisDirection.LEFT || axisDirection == AxisDirection.RIGHT) {
this.direction = axisDirection;
} else throw new ChartException("Can only set LEFT, RIGHT direction");
}
/**
* 格式化竖轴数据
* @param value 数值
* @return 格式化竖轴之后数据
*/
private String formatVerticalAxisData(double value) {
if(getFormat() != null){
return getFormat().format(value);
}
return df.format(value);
}
/**
* 设置刻度是否从0开始
*/
public void setStartZero(boolean isStartZero){
this.scaleSetData.setStartZoom(isStartZero);
}
/**
* 设置刻度最大值
* @param maxValue 最大值
*/
public void setMaxValue(double maxValue) {
this.scaleSetData.setMaxValue(maxValue);
}
/**
* 设置刻度最小值
* @param minValue 最大值
*/
public void setMinValue(double minValue) {
this.scaleSetData.setMinValue(minValue);
}
}
package com.daivd.chart.component.base;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import com.daivd.chart.data.ChartData;
import com.daivd.chart.data.format.IFormat;
import com.daivd.chart.data.BarData;
import com.daivd.chart.matrix.MatrixHelper;
/**
* Created by huangYanBin on 2017/9/26.
* 轴接口
*/
public interface IAxis<V> {
void draw(Canvas canvas, Rect rect, MatrixHelper helper, Paint paint, ChartData<? extends BarData> chartData);
void computeScale(ChartData<? extends BarData> chartData, Rect rect, Paint paint);
void setAxisDirection(int axisDirection);
void setFormat(IFormat<V> format);
IFormat<V> getFormat();
void setDisplay(boolean isShow);
/**
* Created by huangYanBin on 2017/9/26.
* 轴方向
*/
interface AxisDirection {
int TOP = 1;
int BOTTOM=2;
int LEFT = 3;
int RIGHT = 4;
}
}
package com.daivd.chart.component.base;
import com.daivd.chart.data.style.FontStyle;
/**
* Created by huang on 2017/10/26.
*/
public interface IChartTitle extends IComponent<String> {
float getPercent();
void setPercent(float percent);
FontStyle getFontStyle();
int getDirection();
void setDirection(int direction);
void setFontStyle(FontStyle fontStyle);
}
package com.daivd.chart.component.base;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
/** 图表组件
* Created by huang on 2017/10/26.
*/
public interface IComponent<T> {
int LEFT = 0;
int TOP = 1;
int RIGHT =2;
int BOTTOM = 3;
/**
* 计算组件Rect
* @param chartRect
*/
void computeRect(Rect chartRect);
/**
* 绘制组件
* @param canvas 画布
* @param t 数据
* @param paint 画笔
*/
void draw(Canvas canvas, T t, Paint paint);
}
package com.daivd.chart.component.base;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import com.daivd.chart.data.style.FontStyle;
/**
* Created by huang on 2017/9/30.
*/
public interface IEmpty extends IComponent<String> {
void draw(Canvas canvas, Paint paint);
FontStyle getFontStyle();
void setFontStyle(FontStyle fontStyle) ;
String getEmptyTip();
void setEmptyTip(String emptyTip);
}
package com.daivd.chart.component.base;
import android.graphics.PointF;
import com.daivd.chart.data.ChartData;
import com.daivd.chart.data.ColumnData;
import com.daivd.chart.data.style.FontStyle;
import com.daivd.chart.provider.component.point.ILegendPoint;
import com.daivd.chart.provider.component.point.IPoint;
import com.daivd.chart.listener.OnClickLegendListener;
/**
* Created by huang on 2017/10/26.
*/
public interface ILegend<C extends ColumnData> extends IComponent<ChartData<C>> {
float getPercent();
void setPercent(float percent);
FontStyle getFontStyle();
int getDirection();
void setDirection(int direction);
void setFontStyle(FontStyle fontStyle);
void onClickLegend(PointF pointF);
void setOnClickLegendListener(OnClickLegendListener<C> onClickLegendListener);
int getPadding();
void setPadding(int padding);
IPoint getPoint();
void setPoint(ILegendPoint point);
void setSelectColumn(boolean selectColumn);
void setDisplay(boolean isDisplay);
}
\ No newline at end of file
package com.daivd.chart.component.base;
import android.graphics.Rect;
/**图表百分百组件
* Created by huang on 2017/10/26.
*/
public abstract class PercentComponent<T> implements IComponent<T> {
private static final float DEFAULT_PERCENT = 0.1f;
private float percent = DEFAULT_PERCENT;
private Rect rect = new Rect();
protected int direction;
@Override
public void computeRect(Rect chartRect) {
rect.left = chartRect.left;
rect.right = chartRect.right;
rect.top = chartRect.top;
rect.bottom = chartRect.bottom;
int h = (int) (chartRect.height()*percent);
int w = (int) (chartRect.width()*percent);
switch (direction){
case TOP:
rect.bottom = rect.top+h;
chartRect.top = chartRect.top+h;
break;
case LEFT:
rect.right = rect.left + w;
chartRect.left = chartRect.left + w;
break;
case RIGHT:
rect.left = rect.right -w;
chartRect.right = chartRect.right - w;
break;
case BOTTOM:
rect.top = rect.bottom -h;
chartRect.bottom = chartRect.bottom -h;
break;
}
}
public float getPercent() {
return percent;
}
public void setPercent(float percent) {
this.percent = percent;
}
public Rect getRect() {
return rect;
}
public void setRect(Rect rect) {
this.rect = rect;
}
public int getDirection() {
return direction;
}
public void setDirection(int direction) {
this.direction = direction;
}
}
package com.daivd.chart.core;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import com.daivd.chart.core.base.BaseBarLineChart;
import com.daivd.chart.data.BarData;
import com.daivd.chart.provider.barLine.Bar3DProvider;
/**
* Created by huang on 2017/9/26.
* 3DBar图表
*/
public class Bar3DChart extends BaseBarLineChart<Bar3DProvider,BarData> {
public Bar3DChart(Context context) {
super(context);
}
public Bar3DChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public Bar3DChart(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected Bar3DProvider initProvider() {
return new Bar3DProvider();
}
}
package com.daivd.chart.core;
import android.content.Context;
import android.util.AttributeSet;
import com.daivd.chart.core.base.BaseBarLineChart;
import com.daivd.chart.data.BarData;
import com.daivd.chart.provider.barLine.BarProvider;
/**
* Created by huang on 2017/9/26.
* 柱状图
*/
public class BarChart extends BaseBarLineChart<BarProvider<BarData>,BarData> {
public BarChart(Context context) {
super(context);
}
public BarChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public BarChart(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected BarProvider initProvider() {
return new BarProvider();
}
}
package com.daivd.chart.core;
import android.content.Context;
import android.util.AttributeSet;
import com.daivd.chart.core.base.BaseBarLineChart;
import com.daivd.chart.data.BarLineData;
import com.daivd.chart.provider.barLine.BarLineProvider;
/**
* Created by huang on 2017/9/26.
* 柱状图
*/
public class BarLineChart extends BaseBarLineChart<BarLineProvider,BarLineData> {
public BarLineChart(Context context) {
super(context);
}
public BarLineChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public BarLineChart(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected BarLineProvider initProvider() {
return new BarLineProvider();
}
}
package com.daivd.chart.core;
import android.content.Context;
import android.util.AttributeSet;
import android.view.Gravity;
import com.daivd.chart.core.base.BaseBarLineChart;
import com.daivd.chart.data.LineData;
import com.daivd.chart.exception.ChartException;
import com.daivd.chart.provider.barLine.LineProvider;
import com.daivd.chart.provider.component.line.CurveLineModel;
import com.daivd.chart.provider.component.line.BrokenLineModel;
/**
* Created by huang on 2017/9/26.
* 线性图
*/
public class LineChart extends BaseBarLineChart<LineProvider,LineData> {
public static final int LINE_MODEL = 0;
public static final int CURVE_MODEL = 1;
public LineChart(Context context) {
super(context);
}
public LineChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LineChart(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected LineProvider initProvider() {
horizontalAxis.setGravity(Gravity.LEFT);
horizontalAxis.isLine(true);
return new LineProvider();
}
public void setLineModel(int model){
if(model == LINE_MODEL){
provider.setLineModel(new BrokenLineModel());
}else if( model == CURVE_MODEL){
provider.setLineModel(new CurveLineModel());
}else {
throw new ChartException("Please set the correct Line model");
}
}
}
package com.daivd.chart.core;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import com.daivd.chart.core.base.BaseRotateChart;
import com.daivd.chart.data.PieData;
import com.daivd.chart.matrix.RotateHelper;
import com.daivd.chart.provider.pie.Pie3DProvider;
/**
* Created by huang on 2017/10/9.
* 饼图
*/
public class Pie3DChart extends BaseRotateChart<Pie3DProvider,PieData> {
public Pie3DChart(Context context) {
super(context);
}
public Pie3DChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public Pie3DChart(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected Pie3DProvider initProvider(RotateHelper rotateHelper) {
Pie3DProvider provider = new Pie3DProvider(getContext());
provider.setRotateHelper(rotateHelper);
return provider;
}
}
package com.daivd.chart.core;
import android.content.Context;
import android.util.AttributeSet;
import com.daivd.chart.core.base.BaseRotateChart;
import com.daivd.chart.data.PieData;
import com.daivd.chart.matrix.RotateHelper;
import com.daivd.chart.provider.pie.PieProvider;
/**
* Created by huang on 2017/10/9.
* 饼图
*/
public class PieChart extends BaseRotateChart<PieProvider,PieData> {
public PieChart(Context context) {
super(context);
}
public PieChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PieChart(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected PieProvider initProvider(RotateHelper rotateHelper) {
PieProvider provider = new PieProvider();
provider.setRotateHelper(rotateHelper);
return provider;
}
}
package com.daivd.chart.core;
import android.content.Context;
import android.util.AttributeSet;
import com.daivd.chart.core.base.BaseRotateChart;
import com.daivd.chart.data.RadarData;
import com.daivd.chart.matrix.RotateHelper;
import com.daivd.chart.provider.radar.RadarProvider;
/**
* Created by huang on 2017/10/9.
* 雷达图
*/
public class RadarChart extends BaseRotateChart<RadarProvider,RadarData> implements RotateHelper.OnRotateListener{
public RadarChart(Context context) {
super(context);
}
public RadarChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RadarChart(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected RadarProvider initProvider(RotateHelper rotateHelper) {
RadarProvider provider = new RadarProvider();
provider.setRotateHelper(rotateHelper);
return provider;
}
}
package com.daivd.chart.core;
import android.content.Context;
import android.util.AttributeSet;
import com.daivd.chart.core.base.BaseRotateChart;
import com.daivd.chart.data.RoseData;
import com.daivd.chart.matrix.RotateHelper;
import com.daivd.chart.provider.rose.RoseProvider;
/**
* Created by huang on 2017/10/9.
* 饼图
*/
public class RoseChart extends BaseRotateChart<RoseProvider,RoseData> {
public RoseChart(Context context) {
super(context);
}
public RoseChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RoseChart(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected RoseProvider initProvider(RotateHelper rotateHelper) {
RoseProvider provider = new RoseProvider();
provider.setRotateHelper(rotateHelper);
return provider;
}
}
package com.daivd.chart.core.base;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import com.daivd.chart.component.axis.HorizontalAxis;
import com.daivd.chart.component.base.IAxis;
import com.daivd.chart.component.axis.VerticalAxis;
import com.daivd.chart.data.BarData;
import com.daivd.chart.provider.IProvider;
/**
* Created by huang on 2017/9/26.
* 线性和柱状图
*/
public abstract class BaseBarLineChart<P extends IProvider<C>,C extends BarData> extends BaseChart<P,C> {
protected HorizontalAxis horizontalAxis; //横轴
protected VerticalAxis leftVerticalAxis;//竖轴
protected VerticalAxis rightVerticalAxis;//竖轴
public BaseBarLineChart(Context context) {
super(context);
}
public BaseBarLineChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public BaseBarLineChart(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
protected void init(){
horizontalAxis = new HorizontalAxis();
leftVerticalAxis = new VerticalAxis(IAxis.AxisDirection.LEFT);
rightVerticalAxis = new VerticalAxis(IAxis.AxisDirection.RIGHT);
super.init();
}
protected void drawContent(Canvas canvas){
horizontalAxis.computeScale(chartData, chartRect, paint);
if (chartData.getScaleData().isLeftHasValue)
leftVerticalAxis.computeScale(chartData, chartRect, paint);
if (chartData.getScaleData().isRightHasValue)
rightVerticalAxis.computeScale(chartData, chartRect, paint);
if (chartData.getScaleData().isLeftHasValue)
leftVerticalAxis.draw(canvas, chartRect, matrixHelper, paint, chartData);
if (chartData.getScaleData().isRightHasValue)
rightVerticalAxis.draw(canvas, chartRect, matrixHelper, paint, chartData);
horizontalAxis.draw(canvas, chartRect, matrixHelper, paint, chartData);
computeScaleRect();
provider.drawProvider(canvas, chartRect, matrixHelper, paint);
}
/**
* 计算绘制刻度之后的大小
*/
private void computeScaleRect(){
Rect rect = chartData.getScaleData().scaleRect;
computeChartRect(rect);
}
public void setHeightPercent(){
}
public HorizontalAxis getHorizontalAxis() {
return horizontalAxis;
}
public VerticalAxis getLeftVerticalAxis() {
return leftVerticalAxis;
}
public VerticalAxis getRightVerticalAxis() {
return rightVerticalAxis;
}
}
package com.daivd.chart.core.base;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import com.daivd.chart.component.Legend;
import com.daivd.chart.component.PicGeneration;
import com.daivd.chart.component.base.IChartTitle;
import com.daivd.chart.component.base.ILegend;
import com.daivd.chart.data.ChartData;
import com.daivd.chart.data.ColumnData;
import com.daivd.chart.data.PicOption;
import com.daivd.chart.data.ScaleData;
import com.daivd.chart.component.ChartTitle;
import com.daivd.chart.component.EmptyView;
import com.daivd.chart.component.base.IEmpty;
import com.daivd.chart.listener.OnChartChangeListener;
import com.daivd.chart.listener.OnClickColumnListener;
import com.daivd.chart.listener.OnClickLegendListener;
import com.daivd.chart.listener.ChartGestureObserver;
import com.daivd.chart.matrix.MatrixHelper;
import com.daivd.chart.provider.IProvider;
/**
* 基本图表
* Created by huang on 2017/9/26.
*/
public abstract class BaseChart<P extends IProvider<C>, C extends ColumnData> extends View
implements ChartGestureObserver, OnClickLegendListener<C>, OnChartChangeListener {
protected int width; //高
protected int height; //宽
protected int paddingLeft = 10;
protected int paddingRight = 10;
protected int paddingTop = 30;
protected int paddingBottom = 30;
protected Rect chartRect = new Rect();
protected ILegend<C> legend;
protected IChartTitle chartTitle;
protected P provider;//内容绘制者
protected Paint paint;
protected ChartData<C> chartData;
protected MatrixHelper matrixHelper;
protected boolean isShowChartName = true;
protected boolean isCharEmpty;
protected IEmpty emptyView;
private OnClickLegendListener<C> onClickLegendListener;
private boolean isFirstAnim = true;
public BaseChart(Context context) {
super(context);
init();
}
public BaseChart(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public BaseChart(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
/**
* 初始化组件
*/
protected void init() {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
chartTitle = new ChartTitle();
legend = new Legend<>();
legend.setOnClickLegendListener(this);
matrixHelper = new MatrixHelper(getContext());
matrixHelper.register(this);
matrixHelper.setOnTableChangeListener(this);
emptyView = new EmptyView();
provider = initProvider();
}
/**
* 将触摸事件交给Iouch处理
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
return matrixHelper.handlerTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
matrixHelper.onDisallowInterceptEvent(this, event);
return super.dispatchTouchEvent(event);
}
/**
* 获取大小
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
width = w;
height = h;
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (chartData != null) {
resetScaleData();
computePaddingRect();
if (isShowChartName) {
//计算标题的大小
chartTitle.computeRect(chartRect);
chartTitle.draw(canvas, chartData.getChartName(), paint);
}
if (!chartData.isEmpty()) {
//计算图例的大小
legend.computeRect(chartRect);
//绘制图例
legend.draw(canvas, chartData, paint);
}
if (!isCharEmpty) {
//绘制内容
drawContent(canvas);
} else {
//绘制空白
emptyView.computeRect(chartRect);
emptyView.draw(canvas, paint);
}
}
}
//待改善
private void resetScaleData() {
ScaleData scaleData = chartData.getScaleData();
scaleData.scaleRect.set(0, 0, 0, 0);
}
protected abstract void drawContent(Canvas canvas);
protected void computeChartRect(Rect rect) {
chartRect.left = chartRect.left + rect.left;
chartRect.top = chartRect.top + rect.top;
chartRect.bottom = chartRect.bottom - rect.bottom;
chartRect.right = chartRect.right - rect.right;
}
/**
* 计算图表的区域
*
* @return
*/
private void computePaddingRect() {
chartRect.left = paddingLeft;
chartRect.top = paddingTop;
chartRect.bottom = height - paddingBottom;
chartRect.right = width - paddingRight;
}
public void setPadding(int[] padding) {
this.paddingLeft = padding[0];
this.paddingTop = padding[1];
this.paddingRight = padding[2];
this.paddingBottom = padding[3];
}
protected abstract P initProvider();
/**
* 设置图内容绘制者
*/
public void setProvider(P provider) {
this.provider = provider;
}
public P getProvider() {
return provider;
}
public ChartData<C> getChartData() {
return chartData;
}
/**
* 设置图表数据源
*/
public void setChartData(ChartData<C> chartData) {
isCharEmpty = !provider.calculation(chartData);
this.chartData = chartData;
invalidate();
}
private int duration = 400;
private Interpolator interpolator;
/**
* 动画
*/
private void startChartAnim() {
if (interpolator == null)
startChartAnim(duration);
else
startChartAnim(duration, interpolator);
}
/**
* 动画
*
* @param duration 时间
*/
public void startChartAnim(int duration) {
new DecelerateInterpolator();
provider.startAnim(this, duration, new DecelerateInterpolator());
}
public void startChartAnim(int duration, Interpolator interpolator) {
this.duration = duration;
this.interpolator = interpolator;
provider.startAnim(this, duration, interpolator);
}
@Override
public void onClick(float x, float y) {
PointF pointF = new PointF(x, y);
provider.clickPoint(pointF);
legend.onClickLegend(pointF);
invalidate();
}
@Override
public void onClickLegend(C c, Legend<C> legend) {
isCharEmpty = !provider.calculation(chartData);
if (!isFirstAnim) {
startChartAnim();
}
if (onClickLegendListener != null) {
onClickLegendListener.onClickLegend(c, legend);
}
}
public IEmpty getEmptyView() {
return emptyView;
}
public void setEmptyView(IEmpty emptyView) {
this.emptyView = emptyView;
}
public boolean isShowChartName() {
return isShowChartName;
}
public void setShowChartName(boolean showChartName) {
isShowChartName = showChartName;
}
public IChartTitle getChartTitle() {
return chartTitle;
}
public void setChartTitle(IChartTitle chartTitle) {
this.chartTitle = chartTitle;
}
public ILegend<C> getLegend() {
return legend;
}
/**
* 设置是否可以缩放
*
* @param zoom
*/
public void setZoom(boolean zoom) {
matrixHelper.setCanZoom(zoom);
}
@Override
public void onTableChanged(float translateX, float translateY) {
invalidate();
}
public MatrixHelper getMatrixHelper() {
return matrixHelper;
}
public void setOnClickColumnListener(OnClickColumnListener<C> onClickColumnListener) {
provider.setOnClickColumnListener(onClickColumnListener);
}
public void setOnClickLegendListener(OnClickLegendListener<C> onClickLegendListener) {
this.onClickLegendListener = onClickLegendListener;
}
/**
* 是否动画只执行一次
*
* @param isFirstAnim
*/
public void setFirstAnim(boolean isFirstAnim) {
this.isFirstAnim = isFirstAnim;
}
public boolean save() {
return save(new PicOption(chartData.getChartName()));
}
public boolean save(PicOption picOption) {
return new PicGeneration<BaseChart>().save(this, picOption);
}
}
\ No newline at end of file
package com.daivd.chart.core.base;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import com.daivd.chart.data.ColumnData;
import com.daivd.chart.matrix.RotateHelper;
import com.daivd.chart.provider.IProvider;
/**
* Created by huang on 2017/10/9.
* 饼图
*/
public abstract class BaseRotateChart<P extends IProvider<C>,C extends ColumnData> extends BaseChart<P,C> implements RotateHelper.OnRotateListener{
private RotateHelper rotateHelper;
private boolean isOnRotate;
public BaseRotateChart(Context context) {
super(context);
}
public BaseRotateChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public BaseRotateChart(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void drawContent(Canvas canvas) {
provider.drawProvider(canvas, chartRect, matrixHelper, paint);
}
@Override
protected P initProvider() {
rotateHelper = new RotateHelper(this);
return initProvider(rotateHelper);
}
protected abstract P initProvider(RotateHelper rotateHelper);
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction()== MotionEvent.ACTION_DOWN){
isOnRotate = rotateHelper.containsPoint(event);
}
if(isOnRotate){
super.onTouchEvent(event);
return rotateHelper.handlerTouchEvent(event);
}
return super.onTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if(isOnRotate){
rotateHelper.onDisallowInterceptEvent(this,event);
}
return super.dispatchTouchEvent(event);
}
public void setRotate(boolean rotate) {
if(rotate){
setZoom(false);
}
rotateHelper.setRotate(rotate);
}
@Override
public void onRotate(double angle) {
invalidate();
}
}
package com.daivd.chart.data;
import com.daivd.chart.component.base.IAxis;
import java.util.List;
/**
* Created by huang on 2017/9/26.
*/
public class BarData extends ColumnData<List<Double>> {
public BarData(String name, String unit, int color, List<Double> chartYDataList) {
super(name,unit,color,chartYDataList);
}
public BarData(String name, String unit, int direction, int color, List<Double> datas) {
super(name,unit,direction,color,datas);
}
}
package com.daivd.chart.data;
import java.util.List;
/**
* Created by huang on 2017/10/19.
*/
public class BarLineData extends BarData {
public static final int BROKEN= 0;
public static final int CURVE = 1;
public static final int BAR = 2;
private int model = BAR;
public BarLineData(String name, String unit, int color, List<Double> chartYDataList) {
super(name, unit, color, chartYDataList);
}
public BarLineData(String name, int model,String unit, int color,List<Double> chartYDataList) {
super(name, unit, color, chartYDataList);
this.model = model;
}
public BarLineData(String name, String unit, int direction, int color, List<Double> datas) {
super(name, unit, direction, color, datas);
}
public BarLineData(String name,int model, String unit, int direction, int color, List<Double> datas) {
super(name, unit, direction, color, datas);
this.model = model;
}
public int getModel() {
return model;
}
public void setModel(int model) {
this.model = model;
}
}
package com.daivd.chart.data;
import java.util.Arrays;
import java.util.List;
/**
* Created by huang on 2017/9/26.
* chart data
*/
public class ChartData<T extends ColumnData> {
private String chartName;
private List<String> charXDataList;
private ScaleData scaleData = new ScaleData();
private List<T> columnDataList;
public ChartData(String chartName,List<String> charXDataList, T... columnDataList){
this.chartName = chartName;
this.charXDataList = charXDataList;
this.columnDataList = Arrays.asList(columnDataList);
}
public ChartData(String chartName,List<String> charXDataList, List<T> columnDataList){
this.chartName = chartName;
this.charXDataList = charXDataList;
this.columnDataList = columnDataList;
}
public String getChartName() {
return chartName;
}
public void setChartName(String chartName) {
this.chartName = chartName;
}
public boolean isEmpty() {
return columnDataList== null || this.columnDataList.size() ==0 ;
}
public List<String> getCharXDataList() {
return charXDataList;
}
public void setCharXDataList(List<String> charXDataList) {
this.charXDataList = charXDataList;
}
public ScaleData getScaleData() {
return scaleData;
}
public void setScaleData(ScaleData scaleData) {
this.scaleData = scaleData;
}
public List<T> getColumnDataList() {
return columnDataList;
}
public void setColumnDataList(List<T> columnDataList) {
this.columnDataList = columnDataList;
}
}
package com.daivd.chart.data;
import com.daivd.chart.component.base.IAxis;
/**
* Created by huang on 2017/9/26.
*/
public class ColumnData<T> {
public String name;
private String unit;
private int color;
private boolean isDraw = true;
private int direction = IAxis.AxisDirection.LEFT;
private T chartYDataList;
public ColumnData(String name, String unit, int color, T chartYDataList) {
this.name = name;
this.unit = unit;
this.color = color;
this.chartYDataList = chartYDataList;
}
public ColumnData(String name, String unit, int direction, int color, T datas) {
this.name = name;
this.unit = unit;
this.color = color;
this.chartYDataList = datas;
if(direction == IAxis.AxisDirection.LEFT || direction == IAxis.AxisDirection.RIGHT){
this.direction = direction;
}
}
public int getDirection() {
return direction;
}
public void setDirection(int direction) {
this.direction = direction;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUnit() {
return unit;
}
public void setUnit(String unit) {
this.unit = unit;
}
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
public T getChartYDataList() {
return chartYDataList;
}
public void setChartYDataList(T chartYDataList) {
this.chartYDataList = chartYDataList;
}
public boolean isDraw() {
return isDraw;
}
public void setDraw(boolean draw) {
isDraw = draw;
}
}
package com.daivd.chart.data;
import com.daivd.chart.data.style.LineStyle;
import com.daivd.chart.provider.component.line.ILineModel;
import com.daivd.chart.provider.component.point.IPoint;
import java.util.List;
/**
* Created by huang on 2017/9/26.
*/
public class LineData extends BarData {
private IPoint point;
private ILineModel lineModel;
private LineStyle lineStyle;
public LineData(String name, String unit, int color, List<Double> chartYDataList) {
super(name,unit,color,chartYDataList);
}
public LineData(String name, String unit, int direction, int color, List<Double> datas) {
super(name,unit,direction,color,datas);
}
public IPoint getPoint() {
return point;
}
public void setPoint(IPoint point) {
this.point = point;
}
public ILineModel getLineModel() {
return lineModel;
}
public void setLineModel(ILineModel lineModel) {
this.lineModel = lineModel;
}
public LineStyle getLineStyle() {
return lineStyle;
}
public void setLineStyle(LineStyle lineStyle) {
this.lineStyle = lineStyle;
}
}
package com.daivd.chart.data;
import android.graphics.Bitmap;
/**
* Created by huang on 2017/10/30.
* 保存图片Option
*/
public class PicOption {
private String fileName;
private String subFolderPath ="chart";
private String fileDescription="chart pic";
private Bitmap.CompressFormat format= Bitmap.CompressFormat.PNG;
private int quality = 100;
public PicOption(String fileName) {
this.fileName = fileName;
}
public String getFileName() {
return fileName;
}
public String getSubFolderPath() {
return subFolderPath;
}
public PicOption setSubFolderPath(String subFolderPath) {
this.subFolderPath = subFolderPath;
return this;
}
public String getFileDescription() {
return fileDescription;
}
public PicOption setFileDescription(String fileDescription) {
this.fileDescription = fileDescription;
return this;
}
public Bitmap.CompressFormat getFormat() {
return format;
}
public PicOption setFormat(Bitmap.CompressFormat format) {
this.format = format;
return this;
}
public int getQuality() {
return quality;
}
public PicOption setQuality(int quality) {
this.quality = quality;
return this;
}
}
package com.daivd.chart.data;
import com.daivd.chart.component.base.IAxis;
/**
* Created by huang on 2017/9/26.
*/
public class PieData extends ColumnData<Double> {
public PieData(String name, String unit, int color, Double chartYDataList) {
super(name, unit, color, chartYDataList);
}
public PieData(String name, String unit, IAxis.AxisDirection direction, int color, Double datas) {
super(name, unit, color, datas);
}
}
package com.daivd.chart.data;
import java.util.List;
/**
* Created by huang on 2017/9/26.
*/
public class RadarData extends BarData {
public RadarData(String name, String unit, int color, List<Double> chartYDataList) {
super(name,unit,color,chartYDataList);
}
}
package com.daivd.chart.data;
import java.util.List;
/**
* Created by huang on 2017/9/26.
*/
public class RoseData extends BarData {
public RoseData(String name, String unit, int color, List<Double> chartYDataList) {
super(name,unit,color,chartYDataList);
}
}
package com.daivd.chart.data;
import android.graphics.Rect;
import com.daivd.chart.component.base.IAxis;
import com.daivd.chart.matrix.MatrixHelper;
import java.util.ArrayList;
import java.util.List;
/**
* Created by huang on 2017/9/26.
* Scale data
*/
public class ScaleData {
public boolean isLeftHasValue = false;
public boolean isRightHasValue = false;
//竖轴数据
public double maxLeftValue; //最大值
public double minLeftValue; //最小值
//竖轴数据
public double maxRightValue; //最大值
public double minRightValue; //最小值
public int totalScale = 7; //刻度数量
public Rect scaleRect = new Rect();
//横轴数据
public int rowSize; //列数据
public float zoom = MatrixHelper.MIN_ZOOM; //放大比例
public List<Double> getScaleList(int direction){
List<Double> scaleList = new ArrayList<>();
int total = (int) (totalScale *zoom);
double scale = getTotalScaleLength(direction) /(total-1);
double minValue = getMinScaleValue(direction);
for(int i = 0;i < total;i++){
scaleList.add(minValue +scale*i);
}
return scaleList;
}
/**
* 获取最大刻度
* @param direction
* @return
*/
public double getMaxScaleValue(int direction){
if(direction == IAxis.AxisDirection.LEFT){
return maxLeftValue;
}
return maxRightValue;
}
/**
* 获取最小刻度
* @param direction
* @return
*/
public double getMinScaleValue(int direction){
if(direction == IAxis.AxisDirection.LEFT){
return minLeftValue;
}
return minRightValue;
}
/**
*获取总刻度值
* @param direction 方向
* @return 总刻度
*/
public double getTotalScaleLength(int direction){
if(direction == IAxis.AxisDirection.LEFT){
return maxLeftValue - minLeftValue;
}
return maxRightValue - minRightValue;
}
public Rect getOffsetRect(Rect rect, Rect offsetRect){
rect.left = rect.left + offsetRect.left;
rect.right = rect.right - offsetRect.right;
rect.top = rect.top + offsetRect.top;
rect.bottom = rect.bottom - offsetRect.bottom;
return rect;
}
public void resetScale(ScaleSetData scaleSetData,int direction){
if(direction == IAxis.AxisDirection.LEFT){
if(scaleSetData.isStartZoom()){
minLeftValue = 0;
}else {
if(scaleSetData.isHasMinValue()){
minLeftValue = scaleSetData.getMinValue();
}
}
if(scaleSetData.isHasMaxValue()){
maxLeftValue = scaleSetData.getMaxValue();
}
}else{
if(scaleSetData.isStartZoom()){
minRightValue = 0;
}else {
if(scaleSetData.isHasMinValue()){
minRightValue = scaleSetData.getMinValue();
}
}
if(scaleSetData.isHasMaxValue()){
maxRightValue = scaleSetData.getMaxValue();
}
}
}
}
package com.daivd.chart.data;
/**
* Created by huang on 2017/10/18.
*/
public class ScaleSetData {
private boolean isStartZoom;
private boolean isHasMaxValue;
private boolean isHasMinValue;
private double maxValue;
private double minValue;
public boolean isStartZoom() {
return isStartZoom;
}
public void setStartZoom(boolean startZoom) {
isHasMinValue = false;
isStartZoom = startZoom;
}
public boolean isHasMaxValue() {
return isHasMaxValue;
}
public boolean isHasMinValue() {
return isHasMinValue;
}
public double getMaxValue() {
return maxValue;
}
public void setMaxValue(double maxValue) {
isHasMaxValue = true;
this.maxValue = maxValue;
}
public double getMinValue() {
return minValue;
}
public void setMinValue(double minValue) {
isHasMinValue = true;
this.minValue = minValue;
}
}
package com.daivd.chart.data.format;
/**
* 格式化
* <p>用于横轴和竖轴文本格式化</p>
* @author huangyanbin
*/
public interface IFormat<T> {
String format( T t);
}
package com.daivd.chart.data.style;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Paint;
import com.daivd.chart.utils.DensityUtils;
/**
* Created by huang on 2017/9/27.
*/
public class FontStyle implements IStyle{
public static int defaultFontSize = 12;
public static int defaultFontColor = Color.parseColor("#333333");
private int textSize;
private int textColor;
private int padding = 0;
public static void setDefaultTextSize(int defaultTextSize){
defaultFontSize = defaultTextSize;
}
public static void setDefaultTextSpSize(Context context,int defaultTextSpSize){
defaultFontSize = DensityUtils.sp2px(context,defaultTextSpSize);
}
public static void setDefaultTextColor(int defaultTextColor){
defaultFontColor = defaultTextColor;
}
public FontStyle() {
}
public FontStyle(int textSize, int textColor) {
this.textSize = textSize;
this.textColor = textColor;
}
public FontStyle(Context context,int sp, int textColor) {
this.textSize = DensityUtils.sp2px(context,sp);
this.textColor = textColor;
}
public int getTextSize() {
if(textSize == 0){
return defaultFontSize;
}
return textSize;
}
public FontStyle setTextSize(int textSize) {
this.textSize = textSize;
return this;
}
public FontStyle setTextSpSize(Context context,int sp){
this.setTextSize(DensityUtils.sp2px(context,sp));
return this;
}
public int getTextColor() {
if(textColor == 0){
return defaultFontColor;
}
return textColor;
}
public FontStyle setTextColor(int textColor) {
this.textColor = textColor;
return this;
}
public int getPadding() {
return padding;
}
public void setPadding(int padding) {
this.padding = padding;
}
@Override
public void fillPaint(Paint paint){
paint.setColor(getTextColor());
paint.setTextSize(getTextSize());
paint.setStyle(Paint.Style.FILL);
}
}
package com.daivd.chart.data.style;
import android.graphics.Paint;
/**
* Created by huang on 2017/9/27.
*/
public interface IStyle {
void fillPaint(Paint paint);
}
package com.daivd.chart.data.style;
import android.content.Context;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.PathEffect;
import com.daivd.chart.utils.DensityUtils;
/**
* Created by huang on 2017/9/27.
*/
public class LineStyle implements IStyle {
private float width;
private int color;
private PathEffect effect = new PathEffect();
private static float defaultLineSize = 2f;
private static int defaultLineColor = Color.parseColor("#888888");
public LineStyle() {
}
public LineStyle(float width, int color) {
this.width = width;
this.color = color;
}
public LineStyle(Context context,float dp, int color) {
this.width = DensityUtils.dp2px(context,dp);
this.color = color;
}
public static void setDefaultLineSize(float width){
defaultLineSize = width;
}
public static void setDefaultLineSize(Context context,float dp){
defaultLineSize = DensityUtils.dp2px(context,dp);
}
public static void setDefaultLineColor(int color){
defaultLineColor = color;
}
public float getWidth() {
if(width == 0){
return defaultLineSize;
}
return width;
}
public LineStyle setWidth(float width) {
this.width = width;
return this;
}
public LineStyle setWidth(Context context,int dp) {
this.width = DensityUtils.dp2px(context,dp);
return this;
}
public int getColor() {
if(color == 0){
return defaultLineColor;
}
return color;
}
public LineStyle setColor(int color) {
this.color = color;
return this;
}
public LineStyle setEffect(PathEffect effect) {
this.effect = effect;
return this;
}
@Override
public void fillPaint(Paint paint){
paint.setColor(getColor());
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(getWidth());
paint.setPathEffect(effect);
}
}
package com.daivd.chart.data.style;
import android.content.Context;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import com.daivd.chart.utils.DensityUtils;
/**
* Created by huang on 2017/9/27.
*/
public class PointStyle implements IStyle {
public static final int CIRCLE = 0;
public static final int SQUARE = 1;
public static final int RECT = 2;
private float width;
private int color;
private int shape;
private Paint.Style style;
private static float defaultPointSize = 10f;
private static int defaultPointColor = Color.parseColor("#888888");
private boolean isDraw = true;
public PointStyle() {
}
public PointStyle(float width, int color) {
this.width = width;
this.color = color;
}
public PointStyle(Context context, float dp, int color) {
this.width = DensityUtils.dp2px(context,dp);
this.color = color;
}
public static void setDefaultPointSize(float width){
defaultPointSize = width;
}
public static void setDefaultLineSize(Context context,float dp){
defaultPointSize = DensityUtils.dp2px(context,dp);
}
public static void setDefaultPointColor(int color){
defaultPointColor = color;
}
public float getWidth() {
if(width == 0){
return defaultPointSize;
}
return width;
}
public void setWidth(float width) {
this.width = width;
}
public void setWidth(Context context,int dp) {
this.width = DensityUtils.dp2px(context,dp);
}
public int getColor() {
if(color == 0){
return defaultPointColor;
}
return color;
}
public void setColor(int color) {
this.color = color;
}
public int getShape() {
return shape;
}
public void setShape(int shape) {
this.shape = shape;
}
public Paint.Style getStyle() {
if(style == null){
return Paint.Style.FILL;
}
return style;
}
public void setStyle(Paint.Style style) {
this.style = style;
}
public boolean isDraw() {
return isDraw;
}
public void setDraw(boolean draw) {
isDraw = draw;
}
@Override
public void fillPaint(Paint paint){
paint.setColor(getColor());
paint.setStyle(getStyle());
paint.setStrokeWidth(getWidth());
}
}
package com.daivd.chart.exception;
/**
* Created by huang on 2017/9/26.
*/
public class ChartException extends RuntimeException {
public ChartException(String message) {
super(message);
}
}
package com.daivd.chart.listener;
/**
* Created by huang on 2017/9/29.
*/
public interface ChartGestureObserver {
void onClick(float x,float y);
}
package com.daivd.chart.listener;
import java.util.ArrayList;
import java.util.List;
/**
* Created by huang on 2017/9/29.
*/
public abstract class Observable<T> {
public final ArrayList<T> observables = new ArrayList<>();
/**AttachObserver(通过实例注册观察者)
**/
public void register(T observer){
if(observer==null) throw new NullPointerException();
synchronized(observables){
if(!observables.contains(observer)){
observables.add(observer);
}
}
}
/**UnattachObserver(注销观察者)
**/
public void unRegister(T observer){
if(observer==null) throw new NullPointerException();
if(observables.contains(observer)){
observables.remove(observer);
}
}
public void unRegisterAll(){
synchronized(observables){
observables.clear();
}
}
/**Ruturnthesizeofobservers*/
public int countObservers(){
synchronized(observables){
return observables.size();
}
}
/**
*notify all observer(通知所有观察者,在子类中实现)
*/
public abstract void notifyObservers(List<T> observers);
}
package com.daivd.chart.listener;
/**
* Created by huang on 2017/11/4.
*/
public interface OnChartChangeListener {
void onTableChanged(float translateX, float translateY);
}
package com.daivd.chart.listener;
import com.daivd.chart.data.ColumnData;
/**
* Created by huang on 2017/9/30.
*/
public interface OnClickColumnListener<C extends ColumnData> {
void onClickColumn(C c,int position);
}
package com.daivd.chart.listener;
import com.daivd.chart.component.Legend;
import com.daivd.chart.data.ColumnData;
/**
* Created by huang on 2017/9/30.
*/
public interface OnClickLegendListener<C extends ColumnData> {
void onClickLegend(C c, Legend<C> legend);
}
package com.daivd.chart.matrix;
import android.view.MotionEvent;
import com.daivd.chart.core.base.BaseChart;
/**
* Created by huang on 2017/10/18.
*/
public interface ITouch {
/**
* 用于判断是否请求不拦截事件
* 解决手势冲突问题
* @param chart
* @param event
*/
void onDisallowInterceptEvent(BaseChart chart, MotionEvent event);
/**
* 处理touchEvent
* @param event
*/
boolean handlerTouchEvent(MotionEvent event);
}
package com.daivd.chart.matrix;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ViewConfiguration;
import android.view.ViewParent;
import android.view.animation.DecelerateInterpolator;
import android.widget.Scroller;
import com.daivd.chart.core.base.BaseChart;
import com.daivd.chart.listener.Observable;
import com.daivd.chart.listener.ChartGestureObserver;
import com.daivd.chart.listener.OnChartChangeListener;
import java.util.List;
/**
* Created by huang on 2017/9/29.
* 图表放大缩小协助类
*/
public class MatrixHelper extends Observable<ChartGestureObserver> implements ITouch, ScaleGestureDetector.OnScaleGestureListener {
/**
* 最大缩放比
*/
private static final int MAX_ZOOM = 5;
/**
* 最小缩放比
*/
public static final int MIN_ZOOM = 1;
/**
* 当前缩放比
*/
private float zoom = MIN_ZOOM; //缩放比例 不得小于1
/**
* X轴位移的距离
*/
private int translateX; //以左上角为准,X轴位移的距离
/**
* y轴位移的距离
*/
private int translateY;//以左上角为准,y轴位移的距离
/**
* 缩放手势
*/
private ScaleGestureDetector mScaleGestureDetector;
/**
* 移动手势
*/
private GestureDetector mGestureDetector;
/**
* 是否可以缩放
*/
private boolean isCanZoom;
/**
* 是否正在缩放
*/
private boolean isScale; //是否正在缩放
/**
* 原始图表大小
*/
private Rect originalRect; //原始大小
/**
* 缩放之后的大小
*/
private Rect zoomRect;
/**
* 点击X,y点
*/
private float mDownX, mDownY;
/**
* 屏幕的手指点个数
*/
private int pointMode;
/**
* 宽缩放比
*/
private float widthMultiple =1;
/**
* 滚动辅助
*/
private Scroller scroller;
/**
* 最小速率
*/
private int mMinimumVelocity;
/**
* 是否正在飞滚
*/
private boolean isFling;
/**
* 飞滚速率
*/
private float flingRate = 0.8f; //速率
/**
* 图表缩放监听
*/
private OnChartChangeListener listener;
/**
* 缩放移动手势辅助类构造方法
* @param context context
*/
public MatrixHelper(Context context) {
mScaleGestureDetector = new ScaleGestureDetector(context, this);
mGestureDetector = new GestureDetector(context, new OnChartGestureListener());
final ViewConfiguration configuration = ViewConfiguration.get(context);
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
scroller = new Scroller(context);
}
/**
* 处理手势
*/
@Override
public boolean handlerTouchEvent(MotionEvent event) {
if (isCanZoom) {
mScaleGestureDetector.onTouchEvent(event);
}
mGestureDetector.onTouchEvent(event);
return true;
}
/**
* 判断是否需要接收触摸事件
*/
@Override
public void onDisallowInterceptEvent(BaseChart chart, MotionEvent event) {
if (!isCanZoom) {
return;
}
ViewParent parent = chart.getParent();
if (zoomRect == null || originalRect == null) {
parent.requestDisallowInterceptTouchEvent(false);
return;
}
switch (event.getAction()&MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
pointMode = 1;
//ACTION_DOWN的时候,赶紧把事件hold住
mDownX = event.getX();
mDownY = event.getY();
if(originalRect.contains((int)mDownX,(int)mDownY)){ //判断是否落在图表内容区中
parent.requestDisallowInterceptTouchEvent(true);
}else{
parent.requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
pointMode += 1;
parent.requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
if (pointMode > 1) {
parent.requestDisallowInterceptTouchEvent(true);
return;
}
float disX = event.getX() - mDownX;
float disY = event.getY() - mDownY;
boolean isDisallowIntercept = true;
if (Math.abs(disX) > Math.abs(disY)) {
if ((disX > 0 && toRectLeft()) || (disX < 0 && toRectRight())) { //向右滑动
isDisallowIntercept = false;
}
} else {
/* if ((disY > 0 && toRectTop()) || (disY < 0 && toRectBottom())) {*/
isDisallowIntercept = false;
/* }*/
}
parent.requestDisallowInterceptTouchEvent(isDisallowIntercept);
break;
case MotionEvent.ACTION_POINTER_UP:
pointMode -= 1;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
pointMode = 0;
parent.requestDisallowInterceptTouchEvent(false);
}
}
private boolean toRectLeft() {
return translateX <= -(zoomRect.width() - originalRect.width()) / 2;
}
private boolean toRectRight() {
return translateX >= (zoomRect.width() - originalRect.width()) / 2;
}
private boolean toRectBottom() {
return translateY >= (zoomRect.height() - originalRect.height()) / 2;
}
private boolean toRectTop() {
return translateY <= -(zoomRect.height() - originalRect.height()) / 2;
}
@Override
public void notifyObservers(List<ChartGestureObserver> observers) {
}
private float tempScale = MIN_ZOOM; //缩放比例 不得小于1
private int tempTranslateX; //以左上角为准,X轴位移的距离
private int tempTranslateY;//以左上角为准,y轴位移的距离
class OnChartGestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public void onLongPress(MotionEvent e) {
super.onLongPress(e);
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
translateX += distanceX;
translateY += distanceY;
notifyViewChanged();
return true;
}
@Override
public boolean onDown(MotionEvent e) {
return true;
}
/**
* 飞滚
*/
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if(Math.abs(velocityX) >mMinimumVelocity || Math.abs(velocityY) >mMinimumVelocity) {
scroller.setFinalX(0);
scroller.setFinalY(0);
tempTranslateX = translateX;
tempTranslateY = translateY;
scroller.fling(0,0,(int)velocityX,(int)velocityY,-50000,50000
,-50000,50000);
isFling = true;
startFilingAnim(false);
}
return true;
}
//双击
@Override
public boolean onDoubleTap(MotionEvent e) {
if (isCanZoom) {
float oldZoom = zoom;
if (isScale) { //缩小
zoom = zoom / 1.5f;
if (zoom < 1) {
zoom = MIN_ZOOM;
isScale = false;
}
} else { //放大
zoom = zoom * 1.5f;
if (zoom > MAX_ZOOM) {
zoom = MAX_ZOOM;
isScale = true;
}
}
float factor = zoom / oldZoom;
resetTranslate(factor);
notifyObservers(observables);
}
return true;
}
//单击
@Override
public boolean onSingleTapUp(MotionEvent e) {
notifyViewChanged();
for (ChartGestureObserver observer : observables) {
observer.onClick(e.getX(), e.getY());
}
return true;
}
}
private Point startPoint = new Point(0, 0);
private Point endPoint = new Point();
private TimeInterpolator interpolator = new DecelerateInterpolator();
private PointEvaluator evaluator= new PointEvaluator();
/**
* 开始飞滚
* @param doubleWay 双向飞滚
*/
private void startFilingAnim(boolean doubleWay) {
int scrollX =Math.abs(scroller.getFinalX());
int scrollY =Math.abs(scroller.getFinalY());
if(doubleWay){
endPoint.set((int) (scroller.getFinalX() * flingRate),
(int) (scroller.getFinalY() * flingRate));
}else {
if (scrollX > scrollY) {
endPoint.set((int) (scroller.getFinalX() * flingRate), 0);
} else {
endPoint.set(0, (int) (scroller.getFinalY() * flingRate));
}
}
final ValueAnimator valueAnimator = ValueAnimator.ofObject(evaluator,startPoint,endPoint);
valueAnimator.setInterpolator(interpolator);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if(isFling) {
Point point = (Point) animation.getAnimatedValue();
translateX = tempTranslateX - point.x;
translateY = tempTranslateY - point.y;
notifyViewChanged();
}else{
animation.cancel();
}
}
});
int duration = (int)(Math.max(scrollX,scrollY)*flingRate)/2;
valueAnimator.setDuration(duration>300 ?300:duration);
valueAnimator.start();
}
public void notifyViewChanged(){
if(listener != null) {
listener.onTableChanged(translateX, translateY);
}
}
/**
* 缩放开始
*/
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
tempScale = this.zoom;
return true;
}
/**
* 正在缩放
*/
@Override
public boolean onScale(ScaleGestureDetector detector) {
float oldZoom = zoom;
float scale = detector.getScaleFactor();
this.zoom = (float) (tempScale * Math.pow(scale, 2));
float factor = zoom / oldZoom;
resetTranslate(factor);
notifyObservers(observables);
if (this.zoom > MAX_ZOOM) {
this.zoom = MAX_ZOOM;
return true;
} else if (this.zoom < 1) {
this.zoom = 1;
return true;
}
return false;
}
/**
* 缩放结束
*/
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
}
/**
* 重新计算偏移量
* * @param factor
*/
private void resetTranslate(float factor) {
translateX = (int) (translateX * factor);
translateY = (int) (translateY * factor);
}
/**
* 获取图片内容的缩放大小
*
* @param providerRect 内容的大小
* @return 缩放后内容的大小
*/
public Rect getZoomProviderRect(Rect providerRect) {
originalRect = providerRect;
Rect scaleRect = new Rect();
int oldw = providerRect.width();
int oldh = providerRect.height();
int multipleWidth = (int) (oldw *widthMultiple);
int newWidth = (int) (multipleWidth * zoom);
int newHeight = (int) (oldh * zoom);
int offsetX = (int) (oldw*(zoom-1))/2;
int offsetY =(int) (oldh*(zoom-1))/2;
int maxTranslateLeft = (int)Math.abs(oldw*(zoom-1) / 2);
int maxTranslateRight = newWidth - oldw - offsetX;
int maxTranslateY = Math.abs(newHeight - oldh) / 2;
if(translateX >maxTranslateRight){
translateX = maxTranslateRight;
}
if(translateX < -maxTranslateLeft){
translateX = -maxTranslateLeft;
}
if (Math.abs(translateY) > maxTranslateY) {
translateY = translateY > 0 ? maxTranslateY : -maxTranslateY;
}
scaleRect.left = providerRect.left - offsetX - translateX;
scaleRect.right=scaleRect.left+newWidth;
scaleRect.top = providerRect.top - offsetY - translateY;
scaleRect.bottom = scaleRect.top+newHeight;
zoomRect = scaleRect;
return scaleRect;
}
/**
* 获取宽的倍数
* <p>一旦设置倍数,宽度会变大相应倍数,这样就可以横向滑动了。</p>
*/
public float getWidthMultiple() {
return widthMultiple;
}
/**
* 设置宽的倍数
* <p>一旦设置倍数,宽度会变大相应倍数,这样就可以横向滑动了。</p>
* @param widthMultiple 宽的倍数
*/
public void setWidthMultiple(float widthMultiple) {
this.widthMultiple = widthMultiple;
}
/**
* 是否可以缩放
* @return 是否可以缩放
*/
public boolean isCanZoom() {
zoom = 1f;
return isCanZoom;
}
/**
* 设置是否可以缩放
* @param canZoom 是否可以缩放
*/
public void setCanZoom(boolean canZoom) {
isCanZoom = canZoom;
}
/**
* 获取当前缩放比例
* @return 缩放比例
*/
public float getZoom() {
return zoom;
}
/**
* 设置缩放滚动监听
* @return 缩放滚动监听
*/
public OnChartChangeListener getOnTableChangeListener() {
return listener;
}
/**
* 获取缩放滚动监听
* @param onTableChangeListener 缩放滚动监听
*/
public void setOnTableChangeListener(OnChartChangeListener onTableChangeListener) {
this.listener = onTableChangeListener;
}
}
package com.daivd.chart.matrix;
import android.animation.TypeEvaluator;
import android.graphics.Point;
/**
* Created by huang on 2017/11/3.
*/
public class PointEvaluator implements TypeEvaluator {
private Point point = new Point();
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
Point startPoint = (Point) startValue;
Point endPoint = (Point) endValue;
int x = (int) (startPoint.x + fraction * (endPoint.x - startPoint.x));
int y = (int) (startPoint.y + fraction * (endPoint.y - startPoint.y));
point.set(x,y);
return point;
}
}
package com.daivd.chart.matrix;
import android.graphics.Rect;
import android.os.Handler;
import android.view.MotionEvent;
import android.view.ViewParent;
import com.daivd.chart.core.base.BaseChart;
/**
* Created by huang on 2017/10/10.
*/
public class RotateHelper implements ITouch {
private int mRadius;
/**
* 检测按下到抬起时旋转的角度
*/
private float mTmpAngle;
/**
* 检测按下到抬起时使用的时间
*/
private long mDownTime;
/**
* 判断是否正在自动滚动
*/
private boolean isFling;
/**
* 布局时的开始角度
*/
private double mStartAngle = 0;
/**
* 当每秒移动角度达到该值时,认为是快速移动
*/
private static final int FLINGABLE_VALUE = 300;
/**
* 如果移动角度达到该值,则屏蔽点击
*/
private static final int NOCLICK_VALUE = 3;
/**
* 当每秒移动角度达到该值时,认为是快速移动
*/
private int mFlingableValue = FLINGABLE_VALUE;
private Handler mHandler = new Handler();
/**
* 记录上一次的x,y坐标
*/
private float mLastX;
private float mLastY;
private boolean isRotate;
private Rect originRect;
/**
* 自动滚动的Runnable
*/
private AutoFlingRunnable mFlingRunnable;
private OnRotateListener listener;
public RotateHelper(OnRotateListener listener){
this.listener = listener;
}
@Override
public boolean handlerTouchEvent(MotionEvent event) {
if(!isRotate()){
return false;
}
float x = event.getX();
float y = event.getY();
// Log.e("TAG", "x = " + x + " , y = " + y);
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
mLastX = x;
mLastY = y;
mDownTime = System.currentTimeMillis();
mTmpAngle = 0;
// 如果当前已经在快速滚动
if (isFling)
{
// 移除快速滚动的回调
mHandler.removeCallbacks(mFlingRunnable);
isFling = false;
return true;
}
break;
case MotionEvent.ACTION_MOVE:
/**
* 获得开始的角度
*/
float start = getAngle(mLastX, mLastY);
/**
* 获得当前的角度
*/
float end = getAngle(x, y);
// Log.e("TAG", "start = " + start + " , end =" + end);
// 如果是一、四象限,则直接end-start,角度值都是正值
if (getQuadrant(x, y) == 1 || getQuadrant(x, y) == 4)
{
mStartAngle += end - start;
mTmpAngle += end - start;
} else
// 二、三象限,色角度值是付值
{
mStartAngle += start - end;
mTmpAngle += start - end;
}
// 重新布局
listener.onRotate(mStartAngle);
mLastX = x;
mLastY = y;
break;
case MotionEvent.ACTION_UP:
// 计算,每秒移动的角度
float anglePerSecond = mTmpAngle * 1000
/ (System.currentTimeMillis() - mDownTime);
// Log.e("TAG", anglePrMillionSecond + " , mTmpAngel = " +
// mTmpAngle);
// 如果达到该值认为是快速移动
if (Math.abs(anglePerSecond) > mFlingableValue && !isFling)
{
// post一个任务,去自动滚动
mHandler.post(mFlingRunnable = new AutoFlingRunnable(anglePerSecond));
return true;
}
// 如果当前旋转角度超过NOCLICK_VALUE屏蔽点击
if (Math.abs(mTmpAngle) > NOCLICK_VALUE)
{
return true;
}
break;
}
return true;
}
@Override
public void onDisallowInterceptEvent(BaseChart chart, MotionEvent event){
ViewParent parent = chart.getParent();
if (!isRotate() || originRect == null) {
parent.requestDisallowInterceptTouchEvent(false);
return;
}
switch (event.getAction()&MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
if(containsPoint(event)){
parent.requestDisallowInterceptTouchEvent(true);
}else {
parent.requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
parent.requestDisallowInterceptTouchEvent(false);
break;
}
}
public boolean containsPoint(MotionEvent event){
if(originRect != null) {
float x = event.getX();
float y = event.getY();
int centerY = originRect.centerY();
int centerX = originRect.centerX();
return x >= centerX - mRadius && x <= centerX + mRadius
&& y >= centerY - mRadius && y <= centerY + mRadius;
}
return false;
}
/**
* 自动滚动的任务
*
* @author zhy
*
*/
private class AutoFlingRunnable implements Runnable
{
private float angelPerSecond;
public AutoFlingRunnable(float velocity)
{
this.angelPerSecond = velocity;
}
public void run()
{
// 如果小于20,则停止
if ((int) Math.abs(angelPerSecond) < 20)
{
isFling = false;
return;
}
isFling = true;
// 不断改变mStartAngle,让其滚动,/30为了避免滚动太快
mStartAngle += (angelPerSecond / 30);
// 逐渐减小这个值
angelPerSecond /= 1.0666F;
mHandler.postDelayed(this, 30);
// 重新布局
listener.onRotate(mStartAngle);
}
}
/**
* 根据触摸的位置,计算角度
*
* @param xTouch
* @param yTouch
* @return
*/
private float getAngle(float xTouch, float yTouch)
{
double x = xTouch- originRect.left - (mRadius );
double y = yTouch- originRect.top - (mRadius );
return (float) (Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI);
}
/**
* 根据当前位置计算象限
*
* @param x
* @param y
* @return
*/
private int getQuadrant(float x, float y)
{
int tmpX = (int) (x- originRect.left - mRadius );
int tmpY = (int) (y- originRect.top - mRadius );
if (tmpX >= 0)
{
return tmpY >= 0 ? 4 : 1;
} else
{
return tmpY >= 0 ? 3 : 2;
}
}
public interface OnRotateListener{
void onRotate(double angle);
}
public void setRadius(int radius) {
this.mRadius = radius;
}
public void setOriginRect(Rect originRect){
this.originRect = originRect;
}
public double getStartAngle() {
return mStartAngle;
}
public boolean isRotate() {
return isRotate;
}
public void setRotate(boolean rotate) {
isRotate = rotate;
}
}
package com.daivd.chart.provider;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.view.animation.Interpolator;
import com.daivd.chart.core.base.BaseChart;
import com.daivd.chart.data.ChartData;
import com.daivd.chart.data.ColumnData;
import com.daivd.chart.data.style.FontStyle;
import com.daivd.chart.listener.OnClickColumnListener;
import com.daivd.chart.provider.component.mark.IMark;
import com.daivd.chart.matrix.MatrixHelper;
import com.daivd.chart.provider.component.text.IText;
/**
* Created by huang on 2017/9/26.
*/
public abstract class BaseProvider<C extends ColumnData> implements IProvider<C> {
private float progress = 1;
protected PointF pointF;
protected IMark<C> markView;
private boolean isOpenMark;
private boolean isShowText =true;
protected ChartData<C> chartData;
private Rect providerRect;
private IText text;
protected OnClickColumnListener<C> onClickColumnListener;
@Override
public boolean calculation(ChartData<C> chartData) {
this.chartData = chartData;
return calculationChild(chartData);
}
public abstract boolean calculationChild(ChartData<C> chartData);
@Override
public void drawProvider(Canvas canvas, Rect rect, MatrixHelper helper, Paint paint) {
providerRect = rect;
matrixRectStart(canvas,rect);
Rect zoomRect = helper.getZoomProviderRect(rect);
drawProvider(canvas,zoomRect,rect,paint);
matrixRectEnd(canvas,rect);
}
protected void matrixRectStart(Canvas canvas, Rect rect){
canvas.save();
canvas.clipRect(rect);
}
protected void matrixRectEnd(Canvas canvas, Rect rect){
canvas.restore();
}
protected abstract void drawProvider(Canvas canvas, Rect zoomRect,Rect rect, Paint paint);
@Override
public void clickPoint(PointF point) {
pointF = point;
}
@Override
public void startAnim(final BaseChart chartView, int duration, Interpolator interpolator) {
ValueAnimator animator = ValueAnimator.ofFloat(0,1);
animator.setDuration(duration);
animator.setInterpolator(interpolator);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
progress = (float) animation.getAnimatedValue();
chartView.invalidate();
}
});
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationCancel(Animator animation) {
progress = 1;
chartView.invalidate();
}
@Override
public void onAnimationEnd(Animator animation) {
progress = 1;
chartView.invalidate();
}
});
animator.start();
}
public void drawPointText(Canvas canvas,double value,float x,float y,int position,int line,Paint paint){
if(text != null) {
String val = String.valueOf(value);
if (containsRect(x,y)) {
text.drawText(canvas,val,x,y,position,line,paint);
}
}
}
public boolean containsRect( float x,float y){
Rect rect = providerRect;
return y>= rect.top -1 && y<= rect.bottom+1
&& x>= rect.left-1 && x<= rect.right+1;
}
public boolean isOpenMark() {
return isOpenMark;
}
public void setOpenMark(boolean openMark) {
isOpenMark = openMark;
}
public abstract double[] setMaxMinValue(double maxMinValue, double minValue);
@Override
public void setMarkView(IMark markView) {
this.markView = markView;
}
protected float getAnimValue(float value){
return value*progress;
}
public Rect getProviderRect() {
return providerRect;
}
public float getProgress() {
return progress;
}
public boolean isShowText() {
return isShowText;
}
public void setShowText(boolean showText) {
isShowText = showText;
}
public void setOnClickColumnListener(OnClickColumnListener<C> onClickColumnListener) {
this.onClickColumnListener = onClickColumnListener;
}
public IText getText() {
return text;
}
public void setText(IText text) {
this.text = text;
}
}
package com.daivd.chart.provider;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.view.animation.Interpolator;
import com.daivd.chart.core.base.BaseChart;
import com.daivd.chart.data.ChartData;
import com.daivd.chart.data.ColumnData;
import com.daivd.chart.listener.OnClickColumnListener;
import com.daivd.chart.provider.component.mark.IMark;
import com.daivd.chart.matrix.MatrixHelper;
/**
* Created by huang on 2017/9/26.
*/
public interface IProvider<C extends ColumnData> {
boolean calculation( ChartData<C> chartData);
void drawProvider(Canvas canvas, Rect rect, MatrixHelper helper, Paint paint);
void clickPoint(PointF point);
void startAnim(BaseChart chartView, int duration, Interpolator interpolator);
void setMarkView(IMark markView);
void setOnClickColumnListener(OnClickColumnListener<C> onClickColumnListener);
}
package com.daivd.chart.provider.barLine;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import com.daivd.chart.data.BarData;
/**柱状内容绘制
* Created by huang on 2017/9/26.
*/
public class Bar3DProvider extends BarProvider<BarData> {
/**
* 绘制柱
* @param canvas 画布
* @param rect 范围
* @param value 数值
* @param position 位置
* @param line 线位置
* @param paint 画笔
*/
@Override
protected void drawBar(Canvas canvas,Rect rect,double value,int position,int line,Paint paint){
canvas.drawRect(rect, paint);
float w = rect.right - rect.left;
float offsetY= w/2;
float offsetX = w/3f;
Path path = new Path();
path.moveTo(rect.left,rect.top);
path.lineTo(rect.left+offsetX,rect.top-offsetY);
path.lineTo(rect.right+offsetX,rect.top-offsetY);
path.lineTo(rect.right,rect.top);
path.close();
canvas.drawPath(path,paint);
path.reset();
path.moveTo(rect.right,rect.top);
path.lineTo(rect.right,rect.bottom);
path.lineTo(rect.right+offsetX,rect.bottom-offsetY);
path.lineTo(rect.right+offsetX,rect.top-offsetY);
path.close();
canvas.drawPath(path,paint);
drawPointText(canvas,value,(rect.right + rect.left) / 2, rect.top-offsetY,position,line, paint );
}
@Override
public double[] setMaxMinValue(double maxValue, double minValue) {
double dis = Math.abs(maxValue -minValue);
maxValue = maxValue + dis*0.4;
if(minValue >0){
minValue = 0;
}else{
minValue = minValue - dis*0.4;
}
return new double[]{maxValue,minValue};
}
}
package com.daivd.chart.provider.barLine;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import com.daivd.chart.data.BarLineData;
import com.daivd.chart.data.style.LineStyle;
import com.daivd.chart.exception.ChartException;
import com.daivd.chart.provider.component.line.BrokenLineModel;
import com.daivd.chart.provider.component.line.CurveLineModel;
import java.util.ArrayList;
import java.util.List;
/**状状图和线性图结合
* Created by huang on 2017/9/26.
*/
public class BarLineProvider extends BarProvider<BarLineData> {
private List<Float> pointX = new ArrayList<>();
private List<Float> pointY = new ArrayList<>();
private LineStyle lineStyle = new LineStyle();
@Override
public void drawProvider(Canvas canvas, Rect zoomRect, Rect rect, Paint paint) {
paint.setStyle(Paint.Style.FILL);
List<BarLineData> columnDataList = chartData.getColumnDataList();
int barColumnSize = getBarColumnSize();
int rowSize = chartData.getCharXDataList().size();
double perWidth = zoomRect.width()/(barColumnSize*rowSize);
double perBarWidth =perWidth - getCategoryPadding() / 2;
int columnSize = columnDataList.size();
int barPosition =-1;
for (int i = 0; i < columnSize; i++) {
BarLineData columnData = columnDataList.get(i);
if (!columnData.isDraw()) {
continue;
}
if(columnData.getModel() == BarLineData.BAR) {
barPosition++;
}
for (int j = 0; j < rowSize; j++) {
double value = columnData.getChartYDataList().get(j);
int top = (int) getStartY(zoomRect, value, columnData.getDirection());
if(columnData.getModel() == BarLineData.BAR) {
int left = (int) (((j * barColumnSize + barPosition) * perBarWidth)
+ j * getCategoryPadding() + zoomRect.left);
int right = (int) (left + perBarWidth) - getSeriesPadding();
int bottom = zoomRect.bottom;
paint.setColor(columnData.getColor());
Rect barRect = new Rect(left, top, right, bottom);
drawBar(canvas, barRect, value,i,j, paint);
}else{
float x = (float)(zoomRect.left+perWidth*(j+0.5));
drawLine(canvas,j== 0,j == rowSize-1,columnData,x,top,value,i,j,paint);
}
}
}
drawLevelLine(canvas, zoomRect, paint);
}
protected void drawLine(Canvas canvas, boolean isNewBar,boolean isLast, BarLineData columnData, float x,float y, double value,int position,int line, Paint paint) {
if(columnData.getModel() ==BarLineData.CURVE
|| columnData.getModel() ==BarLineData.BROKEN){
if(isNewBar){
pointX.clear();
pointY.clear();
}
pointX.add(x);
pointY.add(y);
drawPointText(canvas,value,x, y,position,line,paint);
if(isLast){
Path path;
if(columnData.getModel() ==BarLineData.CURVE){
path = new CurveLineModel().getLinePath(pointX,pointY);
}else{
path = new BrokenLineModel().getLinePath(pointX,pointY);
}
lineStyle.setColor(paint.getColor());
lineStyle.fillPaint(paint);
canvas.drawPath(path,paint);
}
}
}
protected int getBarColumnSize() {
List<BarLineData> lineDates = chartData.getColumnDataList();
int barSize = 0;
for(int i =0;i < lineDates.size();i++){
if(lineDates.get(i).getModel() == BarLineData.BAR){
barSize++;
}
}
if(barSize == 0){
throw new ChartException("There must be a set of bar data!");
}
return barSize;
}
public LineStyle getLineStyle() {
return lineStyle;
}
public void setLineStyle(LineStyle lineStyle) {
this.lineStyle = lineStyle;
}
}
package com.daivd.chart.provider.barLine;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import com.daivd.chart.data.BarData;
import com.daivd.chart.utils.ColorUtils;
import java.util.List;
/**
* 柱状内容绘制
* Created by huang on 2017/9/26.
*/
public class BarProvider<C extends BarData> extends BaseBarLineProvider<C> {
private int categoryPadding = 20;
private int seriesPadding = 10;
@Override
public void drawProvider(Canvas canvas, Rect zoomRect, Rect rect, Paint paint) {
paint.setStyle(Paint.Style.FILL);
paint.setTextAlign(Paint.Align.LEFT);
List<C> columnDataList = chartData.getColumnDataList();
int columnSize = columnDataList.size();
int rowSize = chartData.getCharXDataList().size();
double width = (zoomRect.width()-(columnSize+1)*categoryPadding) / (columnSize * rowSize);
PointF clickPoint = null;
int clickPosition = 0;
int clickColumnPosition = 0;
boolean isClickRect1 = (pointF != null && rect.contains((int) pointF.x, (int) pointF.y));
boolean isClickRect = false;
for (int i = 0; i < columnSize; i++) {
for (int j = 0; j < rowSize; j++) {
C columnData = columnDataList.get(i);
if (!columnData.isDraw()) {
continue;
}
double value = columnData.getChartYDataList().get(j);
int left = (int) (((j * columnSize + i) * width)
+ j * categoryPadding + zoomRect.left);
int right = (int) (left + width) - seriesPadding;
int top = (int) getStartY(zoomRect, value, columnData.getDirection());
int bottom = zoomRect.bottom;
paint.setColor(columnData.getColor());
if (isClickRect1 && isClickRect(left, right, top, bottom)) {
paint.setColor(ColorUtils.getDarkerColor(columnData.getColor()));
isClickRect = true;
clickPoint = new PointF((left + right) / 2, top);
clickPosition = j;
clickColumnPosition = i;
if (onClickColumnListener != null) {
onClickColumnListener.onClickColumn(columnData, j);
}
}
Rect barRect = new Rect(left, top, right, bottom);
drawBar(canvas, barRect, value,i,j, paint);
drawTip(canvas,left+barRect.width()/2,top,columnData,j);
}
}
drawLevelLine(canvas, zoomRect, paint);
if (isClickRect && containsRect(clickPoint.x, clickPoint.y)) {
drawCross(canvas, clickPoint.x, clickPoint.y, zoomRect, paint);
super.matrixRectEnd(canvas, rect);
drawMark(canvas,clickPoint.x, clickPoint.y,rect, clickPosition, clickColumnPosition);
}
}
@Override
protected void matrixRectEnd(Canvas canvas, Rect rect) {
}
protected void drawBar(Canvas canvas, Rect rect,double value, int position,int line,Paint paint) {
canvas.drawRect(rect, paint);
drawPointText(canvas, value, (rect.right + rect.left) / 2, rect.top, position,line,paint);
}
private void drawCross(Canvas canvas, float x, float y, Rect rect, Paint paint) {
if (isOpenCross() && getCross() != null) {
PointF pointF = new PointF(x, y);
getCross().drawCross(canvas, pointF, rect, paint);
}
}
protected boolean isClickRect(float left, float right, float top, float bottom) {
return pointF != null && pointF.x >= left && pointF.x <= right && pointF.y >= top && pointF.y <= bottom;
}
@Override
public double[] setMaxMinValue(double maxValue, double minValue) {
double dis = Math.abs(maxValue - minValue);
maxValue = maxValue + dis * 0.2;
if (minValue > 0) {
minValue = 0;
} else {
minValue = minValue - dis * 0.2;
}
return new double[]{maxValue, minValue};
}
public int getSeriesPadding() {
return seriesPadding;
}
public void setSeriesPadding(int seriesPadding) {
this.seriesPadding = seriesPadding;
}
public int getCategoryPadding() {
return categoryPadding;
}
public void setCategoryPadding(int categoryPadding) {
this.categoryPadding = categoryPadding;
}
}
package com.daivd.chart.provider.barLine;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import com.daivd.chart.component.base.IAxis;
import com.daivd.chart.data.ChartData;
import com.daivd.chart.provider.component.cross.ICross;
import com.daivd.chart.provider.component.grid.IGrid;
import com.daivd.chart.provider.component.level.ILevel;
import com.daivd.chart.provider.component.level.LevelLine;
import com.daivd.chart.data.BarData;
import com.daivd.chart.data.ScaleData;
import com.daivd.chart.exception.ChartException;
import com.daivd.chart.provider.BaseProvider;
import com.daivd.chart.provider.component.path.IPath;
import com.daivd.chart.provider.component.tip.ITip;
import java.util.ArrayList;
import java.util.List;
/**线和柱状内容绘制
* Created by huang on 2017/9/26.
*/
public abstract class BaseBarLineProvider<C extends BarData> extends BaseProvider<C> {
private ICross cross;
private boolean isOpenCross;
protected ITip<C,?> tip;
private List<ILevel> levelLine = new ArrayList<>();
private float chartPercent = 1;
private boolean isFromBottom;
@Override
public boolean calculationChild( ChartData<C> chartData) {
this.chartData = chartData;
ScaleData scaleData =this.chartData.getScaleData();
List<C> columnDataList = chartData.getColumnDataList();
if(columnDataList == null || columnDataList.size() == 0){
return false;
}
scaleData.rowSize = chartData.getCharXDataList().size();
int columnSize = columnDataList.size();
for(int i = 0 ; i <columnSize; i++){
BarData columnData = columnDataList.get(i);
if(!columnData.isDraw()){
continue;
}
List<Double> chartYDataList = columnData.getChartYDataList();
if(chartYDataList == null || chartYDataList.size() == 0){
throw new ChartException("Please set up Column data");
}
// if(chartYDataList.size() != scaleData.rowSize){
// throw new ChartException("Column rows data inconsistency");
// }
double[] scale = getColumnScale(chartYDataList);
scale = setMaxMinValue(scale[0],scale[1]);
if(columnData.getDirection() == IAxis.AxisDirection.LEFT){
if(!scaleData.isLeftHasValue){
scaleData.maxLeftValue = scale[0];
scaleData.minLeftValue = scale[1];
scaleData.isLeftHasValue = true;
}else{
scaleData.maxLeftValue = Math.max( scaleData.maxLeftValue,scale[0]);
scaleData.minLeftValue = Math.min( scaleData.minLeftValue,scale[1]);
}
}else{
if(!scaleData.isRightHasValue){
scaleData.maxRightValue = scale[0];
scaleData.minRightValue= scale[1];
scaleData.isRightHasValue = true;
}else{
scaleData.maxRightValue = Math.max(scaleData.maxRightValue,scale[0]);
scaleData.minRightValue = Math.min(scaleData.minRightValue,scale[1]);
}
}
}
return chartData.getScaleData().rowSize != 0;
}
private double[] getColumnScale(List<Double> values) {
double maxValue = 0;
double minValue =0;
int size = values.size();
for(int j= 0;j < size;j++) {
double d = values.get(j) ;
if(j == 0){
maxValue = d;
minValue = d;
}
if (maxValue < d){
maxValue = d;
}else if(minValue >d){
minValue = d;
}
}
return new double[] {maxValue,minValue};
}
protected float getStartY(Rect zoomRect, double value, int direction){
ScaleData scaleData = chartData.getScaleData();
double minValue = scaleData.getMinScaleValue(direction);
double totalScaleLength = scaleData.getTotalScaleLength(direction);
float length = (float) ((value - minValue) * zoomRect.height()*chartPercent / totalScaleLength);
return zoomRect.bottom-(isFromBottom?0:(1-chartPercent)/2*zoomRect.height()) - length;
}
/**
* 绘制水平线
*/
void drawLevelLine(Canvas canvas, Rect zoomRect, Paint paint){
if(levelLine.size() > 0) {
for(ILevel level:levelLine){
float levelY = getStartY(zoomRect,level.getValue(),level.getAxisDirection());
if(containsRect(getProviderRect().centerX(),levelY)) {
level.drawLevel(canvas, getProviderRect(), levelY, paint);
}
}
}
}
/**
*绘制提示
*/
void drawTip(Canvas canvas,float x, float y,C c,int position){
if(tip != null){
tip.drawTip(canvas,x,y,getProviderRect(),c,position);
}
}
void drawMark(Canvas canvas,float x, float y,Rect rect, int position, int columnPosition) {
if (markView != null && isOpenMark()) {
markView.drawMark(canvas,x, y, rect,chartData.getCharXDataList().get(position),
chartData.getColumnDataList().get(columnPosition), position);
}
}
public abstract double[] setMaxMinValue(double maxMinValue, double minValue);
public void addLevelLine(ILevel levelLine) {
this.levelLine.add(levelLine);
}
public boolean isOpenCross() {
return isOpenCross;
}
public void setOpenCross(boolean openCross) {
isOpenCross = openCross;
}
public ICross getCross() {
return cross;
}
public void setCross(ICross cross) {
this.cross = cross;
}
public List<ILevel> getLevelLine() {
return levelLine;
}
public ITip<C,?> getTip() {
return tip;
}
public void setTip(ITip<C,?> tip) {
this.tip = tip;
}
public float getChartPercent() {
return chartPercent;
}
public boolean isFromBottom() {
return isFromBottom;
}
public void setFromBottom(boolean fromBottom) {
isFromBottom = fromBottom;
}
public void setChartPercent(float chartPercent) {
this.chartPercent = chartPercent;
}
}
package com.daivd.chart.provider.barLine;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import com.daivd.chart.data.LineData;
import com.daivd.chart.data.style.LineStyle;
import com.daivd.chart.provider.component.grid.IGrid;
import com.daivd.chart.provider.component.line.BrokenLineModel;
import com.daivd.chart.provider.component.line.ILineModel;
import com.daivd.chart.provider.component.path.IPath;
import com.daivd.chart.provider.component.path.LinePath;
import com.daivd.chart.provider.component.point.IPoint;
import com.daivd.chart.utils.ColorUtils;
import java.util.ArrayList;
import java.util.List;
/**
* 线内容绘制
* Created by huang on 2017/9/26.
*/
public class LineProvider extends BaseBarLineProvider<LineData> {
private LineStyle lineStyle = new LineStyle();
private IPoint point;
private int rowSize;
private boolean isArea; //是否打开面积图
private boolean isDrawLine = true;
private int filterPointTextCount = 30; //最多显示30个点文字
private ILineModel lineModel = new BrokenLineModel();
private IPath path;
private int perWidth;
private IGrid grid;
private boolean isStartZero = true;
private int areaAlpha = 125;
@Override
public void drawProvider(Canvas canvas, Rect zoomRect, Rect rect, Paint paint) {
List<LineData> columnDataList = chartData.getColumnDataList();
int columnSize = columnDataList.size();
rowSize = chartData.getCharXDataList().size();
int filter = rowSize / filterPointTextCount;
filter = filter < 1 ? 1 : filter;
if (grid != null) {
grid.drawClickBg(canvas, pointF, zoomRect, perWidth, paint);
}
for (int i = 0; i < columnSize; i++) {
LineData lineData = columnDataList.get(i);
paint.setColor(lineData.getColor());
if (!lineData.isDraw()) {
continue;
}
List<Float> pointX = new ArrayList<>();
List<Float> pointY = new ArrayList<>();
List<Double> chartYDataList = lineData.getChartYDataList();
for (int j = 0; j < chartYDataList.size(); j++) {
double value = chartYDataList.get(j);
float x = getStartX(zoomRect, j);
float y = getStartY(zoomRect, value, lineData.getDirection());
if (grid != null && i == 0) {
grid.drawGrid(canvas, x, zoomRect, perWidth, paint);
grid.drawColumnContent(canvas, j, zoomRect, rect, perWidth, paint);
}
pointX.add(x);
pointY.add(y);
if (j % filter == 0) {
drawPointText(canvas, value, x, y, j, i, paint);
}
}
canvas.save();
canvas.clipRect(rect);
drawLine(canvas, rect, zoomRect, paint, lineData, pointX, pointY);
drawTip(canvas, pointX, pointY, lineData);
canvas.restore();
drawPoint(canvas, pointX, pointY, lineData, paint);
}
drawLevelLine(canvas, zoomRect, paint);
drawClickCross(canvas, rect, zoomRect, paint);
}
private void drawLine(Canvas canvas, Rect rect, Rect zoomRect, Paint paint, LineData lineData, List<Float> pointX, List<Float> pointY) {
LineStyle style = lineData.getLineStyle();
style = style == null ? lineStyle : style;
style.fillPaint(paint);
paint.setColor(lineData.getColor());
if (isDrawLine) {
ILineModel model = lineData.getLineModel();
model = model == null ? lineModel : model;
Path path = model.getLinePath(pointX, pointY);
if (isArea) {
paint.setStyle(Paint.Style.FILL);
path.lineTo(rect.right, rect.bottom);
path.lineTo(rect.left, rect.bottom);
path.close();
int alphaColor = ColorUtils.changeAlpha(paint.getColor(), areaAlpha);
paint.setColor(alphaColor);
}
getPath().drawPath(canvas, zoomRect, path, perWidth, paint, getProgress());
}
}
@Override
protected void matrixRectEnd(Canvas canvas, Rect rect) {
}
@Override
protected void matrixRectStart(Canvas canvas, Rect rect) {
}
private float getStartX(Rect zoomRect, int position) {
if (isStartZero) {
perWidth = (zoomRect.width()) / (rowSize - 1);
return (float) (position * perWidth + zoomRect.left);
} else {
perWidth = (zoomRect.width()) / rowSize;
return (float) (position * perWidth + perWidth / 2 + zoomRect.left);
}
}
private void drawClickCross(Canvas canvas, Rect rect, Rect zoomRect, Paint paint) {
if (pointF != null && (isOpenCross() || isOpenMark())) {
int minPositionX = 0, minPositionY = 0;
float centerX = 0, centerY = 0;
LineData clickLineData = null;
float minDisX = zoomRect.width();
for (int i = 0; i < rowSize; i++) {
float startX = getStartX(zoomRect, i);
float disX = Math.abs(pointF.x - startX);
if (disX < minDisX) {
minPositionX = i;
minDisX = disX;
centerX = startX;
} else {
break;
}
}
List<LineData> lineDates = chartData.getColumnDataList();
float minDisY = zoomRect.height();
boolean isHasLine = false;
for (int j = 0; j < lineDates.size(); j++) {
LineData lineData = lineDates.get(j);
if (!lineData.isDraw()) {
continue;
}
isHasLine = true;
double value;
if (minPositionX < lineData.getChartYDataList().size()) {
value = lineData.getChartYDataList().get(minPositionX);
} else{
break;
}
float startY = getStartY(zoomRect, value, lineData.getDirection());
float disY = Math.abs(pointF.y - startY);
if (disY < minDisY) {
centerY = startY;
minDisY = disY;
clickLineData = lineData;
minPositionY = j;
}
}
if (!isHasLine || !containsRect(centerX, centerY)) {
return;
}
if (onClickColumnListener != null) {
onClickColumnListener.onClickColumn(clickLineData, minPositionX);
}
if (isOpenCross() && getCross() != null) {
getCross().drawCross(canvas, new PointF(centerX, centerY), rect, paint);
}
drawMark(canvas, centerX, centerY, rect, minPositionX, minPositionY);
}
}
private void drawPoint(Canvas canvas, List<Float> pointX, List<Float> pointY, LineData lineData, Paint paint) {
IPoint linePoint = lineData.getPoint();
linePoint = linePoint == null ? point : linePoint;
if (linePoint != null) {
for (int i = 0; i < pointY.size(); i++) {
float x = pointX.get(i);
float y = pointY.get(i);
if (containsRect(x, y)) {
linePoint.drawPoint(canvas, x, y, i, false, paint);
}
}
}
}
private void drawTip(Canvas canvas, List<Float> pointX, List<Float> pointY, LineData lineData) {
if (tip != null) {
for (int i = 0; i < pointY.size(); i++) {
float x = pointX.get(i);
float y = pointY.get(i);
drawTip(canvas, x, y, lineData, i);
}
}
}
public LineStyle getLineStyle() {
return lineStyle;
}
@Override
public double[] setMaxMinValue(double maxValue, double minValue) {
double dis = Math.abs(maxValue - minValue);
maxValue = maxValue + dis * 0.3;
if (minValue > 0) {
minValue = 0;
} else {
minValue = minValue - dis * 0.3;
}
return new double[]{maxValue, minValue};
}
public IPath getPath() {
if (path == null) {
path = new LinePath();
}
return path;
}
public IGrid getGrid() {
return grid;
}
public void setGrid(IGrid grid) {
this.grid = grid;
}
public void setPath(IPath path) {
this.path = path;
}
public void setArea(boolean isArea) {
this.isArea = isArea;
}
/**
* 设置线模式
*
* @param lineModel
*/
public void setLineModel(ILineModel lineModel) {
this.lineModel = lineModel;
}
/**
* 设置是否绘制线
*
* @param drawLine 绘制线
*/
public void setDrawLine(boolean drawLine) {
isDrawLine = drawLine;
}
/**
* 过滤图表文本数量
* 当数据过多时,,来过滤文字显示,保持图表美观
*
* @return 过滤图表文本数量
*/
public int getFilterPointTextCount() {
return filterPointTextCount;
}
/**
* 过滤图表文本数量
* 当数据过多时,可以使用该方法,来过滤文字显示,保持图表美观
*
* @return
*/
public void setFilterPointTextCount(int filterPointTextCount) {
this.filterPointTextCount = filterPointTextCount;
}
public void setPoint(IPoint point) {
this.point = point;
}
/**
* 设置是否从0开始
*
* @return 从0开始
*/
public boolean isStartZero() {
return isStartZero;
}
/**
* 设置是否从0开始
*
* @param startZero 从0开始
*/
public void setStartZero(boolean startZero) {
isStartZero = startZero;
}
/**
* 获取面积图透明度
*
* @return 透明度 0-255
*/
public int getAreaAlpha() {
return areaAlpha;
}
/**
* 设置面积图的透明度
*
* @param areaAlpha 透明度 0-255
*/
public void setAreaAlpha(int areaAlpha) {
this.areaAlpha = areaAlpha;
}
}
package com.daivd.chart.provider.component.cross;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import com.daivd.chart.data.style.LineStyle;
/**
* Created by huang on 2017/10/19.
* 双方向
*/
public class DoubleDriCross implements ICross{
LineStyle crossStyle = new LineStyle();
@Override
public void drawCross(Canvas canvas, PointF pointF, Rect rect, Paint paint) {
crossStyle.fillPaint(paint);
canvas.drawLine(pointF.x, rect.top, pointF.x, rect.bottom, paint);
canvas.drawLine(rect.left, pointF.y, rect.right, pointF.y, paint);
}
public LineStyle getCrossStyle() {
return crossStyle;
}
public void setCrossStyle(LineStyle crossStyle) {
this.crossStyle = crossStyle;
}
}
package com.daivd.chart.provider.component.cross;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
/**
* Created by huang on 2017/10/19.
* 十字轴
*/
public interface ICross {
void drawCross(Canvas canvas, PointF pointF, Rect rect, Paint paint);
}
package com.daivd.chart.provider.component.cross;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import com.daivd.chart.data.style.LineStyle;
import com.daivd.chart.provider.component.point.IPoint;
import com.daivd.chart.provider.component.point.Point;
/**
* Created by huang on 2017/10/19.
* 竖轴方向
*/
public class VerticalCross implements ICross {
LineStyle crossStyle = new LineStyle();
IPoint point = new Point();
@Override
public void drawCross(Canvas canvas, PointF pointF, Rect rect, Paint paint) {
crossStyle.fillPaint(paint);
canvas.drawLine(pointF.x, rect.top, pointF.x, rect.bottom, paint);
if (point != null) {
point.drawPoint(canvas, pointF.x, pointF.y, 0, false, paint);
}
}
public IPoint getPoint() {
return point;
}
public void setPoint(IPoint point) {
this.point = point;
}
public LineStyle getCrossStyle() {
return crossStyle;
}
public void setCrossStyle(LineStyle crossStyle) {
this.crossStyle = crossStyle;
}
}
package com.daivd.chart.provider.component.grid;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
/**
* Created by huang on 2017/12/1.
*/
public interface IGrid {
/**
* 绘制网格
* @param canvas
* @param x
* @param rect
* @param perWidth
* @param paint
*/
void drawGrid(Canvas canvas, float x, Rect rect, int perWidth, Paint paint);
/**
* 绘制点击背景
* @param canvas
* @param pointF
* @param rect
* @param perWidth
* @param paint
*/
void drawClickBg(Canvas canvas, PointF pointF, Rect rect, int perWidth, Paint paint);
/**
* 绘制列内容
* @param canvas
* @param position
* @param rect
* @param perWidth
* @param paint
*/
void drawColumnContent(Canvas canvas, int position, Rect zoomRect, Rect rect, int perWidth, Paint paint);
}
package com.daivd.chart.provider.component.level;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
/**
* Created by huang on 2017/10/19.
*/
public interface ILevel {
int getAxisDirection();
void drawLevel(Canvas canvas, Rect rect, float y, Paint paint);
double getValue();
}
package com.daivd.chart.provider.component.level;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import com.daivd.chart.component.base.IAxis;
import com.daivd.chart.data.style.FontStyle;
import com.daivd.chart.data.style.LineStyle;
/**
* Created by huang on 2017/9/30.
* LevelLine
*/
public class LevelLine implements ILevel{
public static int left = 0;
public static int right = 1;
private LineStyle lineStyle = new LineStyle();
private double value;
private int textDirection = right;
private FontStyle textStyle = new FontStyle();
private int direction = IAxis.AxisDirection.LEFT;
public LevelLine(double value) {
this.value = value;
}
public LineStyle getLineStyle() {
return lineStyle;
}
public void setLineStyle(LineStyle lineStyle) {
this.lineStyle = lineStyle;
}
public double getValue() {
return value;
}
public void setValue(double value) {
this.value = value;
}
public int getTextDirection() {
return textDirection;
}
public void setTextDirection(int textDirection) {
this.textDirection = textDirection;
}
public FontStyle getTextStyle() {
return textStyle;
}
public int getAxisDirection() {
return direction;
}
public void setDirection(int direction) {
this.direction = direction;
}
public void setTextStyle(FontStyle textStyle) {
this.textStyle = textStyle;
}
@Override
public void drawLevel(Canvas canvas, Rect rect, float y, Paint paint) {
getLineStyle().fillPaint(paint);
Path path = new Path();
path.moveTo(rect.left, y);
path.lineTo(rect.right, y);
canvas.drawPath(path, paint);
getTextStyle().fillPaint(paint);
float textHeight = paint.measureText("1", 0, 1);
float startX;
float startY = y - textHeight + getLineStyle().getWidth();
String levelLineValue = String.valueOf(getValue());
if (getTextDirection() == LevelLine.left) {
startX = rect.left;
} else {
startX = rect.right - textHeight * levelLineValue.length();
}
canvas.drawText(levelLineValue, startX, startY, paint);
}
}
package com.daivd.chart.provider.component.line;
import android.graphics.Path;
import java.util.List;
public class BrokenLineModel implements ILineModel {
@Override
public Path getLinePath(List<Float> pointX, List<Float> pointY) {
Path path = new Path();
for(int i = 0; i < pointY.size();i++){
float x = pointX.get(i);
float y = pointY.get(i);
if(i == 0){
path.moveTo(x,y);
}else{
path.lineTo(x,y);
}
}
return path;
}
}
package com.daivd.chart.provider.component.line;
import android.graphics.Path;
import java.util.LinkedList;
import java.util.List;
/**曲线内容绘制
* Created by huang on 2017/9/27.
*/
public class CurveLineModel implements ILineModel {
private final int STEP = 12;
@Override
public Path getLinePath(List<Float> pointX, List<Float> pointY) {
List<Cubic> calculate_x = calculate(pointX);
List<Cubic> calculate_y = calculate(pointY);
Path path = new Path();
if (null != calculate_x && null != calculate_y && calculate_y.size() >= calculate_x.size()) {
path.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 <= STEP; j++) {
float u = j / (float) STEP;
path.lineTo(calculate_x.get(i).eval(u), calculate_y.get(i).eval(u));
}
}
}
return path;
}
/**
* 计算曲线.
* @param x
* @return
*/
private List<Cubic> calculate(List<Float> x) {
if (null != x && x.size() > 0) {
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];
}
List<Cubic> cubicList = new LinkedList<>();
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]);
cubicList.add(c);
}
return cubicList;
}
return null;
}
private class Cubic {
float a, b, c, d; /* a + b*u + c*u^2 +d*u^3 */
Cubic(float a, float b, float c, float d) {
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
/** evaluate cubic */
float eval(float u) {
return (((d * u) + c) * u + b) * u + a;
}
}
}
package com.daivd.chart.provider.component.line;
import android.graphics.Path;
import java.util.List;
/**
* Created by huang on 2017/10/15.
*/
public interface ILineModel {
Path getLinePath(List<Float> pointX, List<Float> pointY);
}
package com.daivd.chart.provider.component.mark;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import com.daivd.chart.provider.component.tip.SingleLineBubbleTip;
import com.daivd.chart.data.BarData;
import com.daivd.chart.utils.DensityUtils;
import com.david.chart.R;
/**
* Created by huang on 2017/9/28.
*/
public class BubbleMarkView<C extends BarData> implements IMark<C> {
private SingleLineBubbleTip<String> bubbleTip;
private Paint paint;
public BubbleMarkView(Context context) {
paint = new Paint();
paint.setAntiAlias(true);
paint.setTextSize(DensityUtils.sp2px(context,13));
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.WHITE);
bubbleTip = new SingleLineBubbleTip<String>(context,R.mipmap.round_rect,R.mipmap.triangle,paint){
@Override
public boolean isShowTip(String s,int position) {
return true;
}
@Override
public String format(String s, int position) {
return s;
}
};
}
public Paint getPaint() {
return paint;
}
@Override
public void drawMark(Canvas canvas, float x, float y, Rect rect, String content, BarData data, int position) {
String text = content + data.getChartYDataList().get(position) + data.getUnit();
bubbleTip.drawTip(canvas, x, y, rect, text,position);
}
}
package com.daivd.chart.provider.component.mark;
import android.graphics.Canvas;
import android.graphics.Rect;
import com.daivd.chart.data.ColumnData;
/**
* Created by huang on 2017/9/28.
*/
public interface IMark<C extends ColumnData> {
/**
* 绘制Mark
*/
void drawMark(Canvas canvas,float x, float y,Rect rect, String content, C data, int position);
}
package com.daivd.chart.provider.component.path;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
/**
* Created by huang on 2017/11/30.
*/
public interface IPath {
void drawPath(Canvas canvas, Rect rect, Path path, int perWidth, Paint paint, float progress);
}
package com.daivd.chart.provider.component.path;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
/**
* Created by huang on 2017/11/30.
*/
public class LinePath implements IPath{
public void drawPath(Canvas canvas, Rect rect, Path path, int perWidth, Paint paint,float progress){
canvas.drawPath(path, paint);
}
}
package com.daivd.chart.provider.component.point;
/**
* Created by huang on 2017/12/13.
*/
public interface ILegendPoint extends IPoint{
float getWidth();
float getHeight();
}
package com.daivd.chart.provider.component.point;
import android.graphics.Canvas;
import android.graphics.Paint;
/**
* Created by huang on 2017/10/20.
*/
public interface IPoint {
void drawPoint(Canvas canvas, float x, float y, int position, boolean isShowDefaultColor, Paint paint);
}
package com.daivd.chart.provider.component.point;
import android.graphics.Canvas;
import android.graphics.Paint;
import com.daivd.chart.data.style.PointStyle;
/**
* Created by huang on 2017/10/20.
*/
public class LegendPoint implements ILegendPoint {
private PointStyle pointStyle = new PointStyle();
public void drawPoint(Canvas canvas, float x, float y, int position,boolean isShowDefaultColor, Paint paint){
float w = pointStyle.getWidth();
if(isShowDefaultColor){
pointStyle.fillPaint(paint);
}else {
int oldColor =paint.getColor();
pointStyle.fillPaint(paint);
paint.setColor(oldColor);
}
if (pointStyle.getShape() == PointStyle.CIRCLE) {
canvas.drawCircle(x, y, w/2, paint);
} else if (pointStyle.getShape() == PointStyle.SQUARE) {
canvas.drawRect(x - w/2 , y - w/2 , x + w/2, y + w/2 , paint);
} else if (pointStyle.getShape() == PointStyle.RECT) {
canvas.drawRect(x - w * 2 / 3, y - w/2 , x + w * 2 / 3, y + w/2, paint);
}
}
@Override
public float getWidth() {
if(pointStyle.getShape() == PointStyle.RECT){
return pointStyle.getWidth() * 4/ 3;
}
return pointStyle.getWidth();
}
@Override
public float getHeight() {
return pointStyle.getWidth();
}
public PointStyle getPointStyle() {
return pointStyle;
}
}
package com.daivd.chart.provider.component.point;
import android.graphics.Canvas;
import android.graphics.Paint;
import com.daivd.chart.data.style.PointStyle;
/**
* Created by huang on 2017/10/20.
*/
public class Point implements IPoint {
private PointStyle pointStyle = new PointStyle();
public void drawPoint(Canvas canvas,float x, float y,int position,boolean isShowDefaultColor, Paint paint){
float w = pointStyle.getWidth();
if(isShowDefaultColor){
pointStyle.fillPaint(paint);
}else {
int oldColor =paint.getColor();
pointStyle.fillPaint(paint);
paint.setColor(oldColor);
}
if (pointStyle.getShape() == PointStyle.CIRCLE) {
canvas.drawCircle(x, y, w/2, paint);
} else if (pointStyle.getShape() == PointStyle.SQUARE) {
canvas.drawRect(x - w/2 , y - w/2 , x + w/2, y + w/2 , paint);
} else if (pointStyle.getShape() == PointStyle.RECT) {
canvas.drawRect(x - w * 2 / 3, y - w/2 , x + w * 2 / 3, y + w/2, paint);
}
}
public PointStyle getPointStyle() {
return pointStyle;
}
}
package com.daivd.chart.provider.component.text;
import android.graphics.Canvas;
import android.graphics.Paint;
/**
* Created by huang on 2017/11/30.
*/
public interface IText {
void drawText(Canvas canvas, String value, float x, float y, int position, int line, Paint paint);
}
package com.daivd.chart.provider.component.tip;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.NinePatch;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import com.daivd.chart.utils.DensityUtils;
/**
* Created by huang on 2017/10/20.
* 气泡提示
*/
public abstract class BaseBubbleTip<C,S> implements ITip<C,S>{
public static final int INVALID = -1;
private Rect tipRect;
private Paint paint;
private int verticalPadding;
private int horizontalPadding;
private Bitmap triangleBitmap;
private NinePatch ninePatch;
private boolean isReversal = false;
protected int deviation;
private float alpha;
private int colorFilter = INVALID;
public BaseBubbleTip(Context context, int backgroundDrawableID, int triangleDrawableID, Paint paint){
tipRect = new Rect();
this.paint = paint;
triangleBitmap = Bitmap.createBitmap(BitmapFactory.decodeResource(context.getResources(), triangleDrawableID));
if(ninePatch == null) {
Bitmap bmp_9 = BitmapFactory.decodeResource(context.getResources(),
backgroundDrawableID);
ninePatch = new NinePatch(bmp_9, bmp_9.getNinePatchChunk(), null);
}
verticalPadding = DensityUtils.dp2px(context,5);
horizontalPadding = verticalPadding;
deviation = DensityUtils.dp2px(context,5);
}
public void drawTip(Canvas canvas, float x, float y, Rect rect,C c,int position) {
if(isShowTip(c,position)) {
S content = format(c,position);
int triangleWidth = triangleBitmap.getWidth();
int triangleHeight = triangleBitmap.getHeight();
int textWidth = getTextWidth(content);
int textHeight = getTextHeight(content);
int w = textWidth + horizontalPadding * 2;
int h = textHeight + verticalPadding * 2;
tipRect.left = (int) x - w / 2;
tipRect.right = (int) x + w / 2;
tipRect.bottom = (int) y - triangleHeight + triangleWidth / 8;
tipRect.top = tipRect.bottom - h;
int tranX = 0;
if(x > rect.left && x < rect.right) {
if (tipRect.left < rect.left) {
tranX = rect.left - tipRect.left - triangleWidth / 2;
} else if (tipRect.right > rect.right) {
tranX = rect.right - tipRect.right + triangleWidth / 2;
}
if (tipRect.top < rect.top) {
showBottom(canvas, x, y, content, textWidth, textHeight, tranX);
} else if (tipRect.bottom > rect.bottom) {
showTop(canvas, x, y, content, textWidth, textHeight, tranX);
} else if (isReversal) {
showBottom(canvas, x, y, content, textWidth, textHeight, tranX);
} else {
showTop(canvas, x, y, content, textWidth, textHeight, tranX);
}
}
}
}
public abstract int getTextHeight(S content);
public abstract int getTextWidth(S content);
public abstract void drawText(Canvas canvas,Rect tipRect,S content,int textWidth,int textHeight,Paint paint);
private void showTop(Canvas canvas, float x, float y, S content, int textWidth, int textHeight,int tranX) {
canvas.save();
int triangleWidth = triangleBitmap.getWidth();
int triangleHeight = triangleBitmap.getHeight();
startColorFilter();
// paint.setShadowLayer(40, 10, 50, Color.BLACK);
canvas.drawBitmap(triangleBitmap,x-triangleWidth/2,
y-triangleHeight,paint);
// paint.clearShadowLayer();
canvas.translate(tranX,0);
ninePatch.draw(canvas, tipRect);
clearColorFilter();
drawText(canvas, tipRect,content,textWidth,textHeight,paint);
canvas.restore();
}
private void showBottom(Canvas canvas, float x, float y, S content, int textWidth,int textHeight,int tranX) {
canvas.save();
int triangleWidth = triangleBitmap.getWidth();
int triangleHeight = triangleBitmap.getHeight();
canvas.rotate(180,x,y);
startColorFilter();
canvas.drawBitmap(triangleBitmap,x-triangleWidth/2,
y-triangleHeight,paint);
canvas.translate(-tranX,0);
ninePatch.draw(canvas, tipRect);
clearColorFilter();
paint.setColorFilter(null);
canvas.rotate(180, tipRect.centerX(), tipRect.centerY());
drawText(canvas, tipRect,content,textWidth,textHeight,paint);
canvas.restore();
}
private void startColorFilter() {
if (colorFilter != INVALID){
paint.setColorFilter(new PorterDuffColorFilter(colorFilter, PorterDuff.Mode.SRC_IN));
ninePatch.setPaint(paint);
paint.setAlpha((int) (alpha*255));
}
}
private void clearColorFilter(){
if(colorFilter != INVALID) {
paint.setColorFilter(null);
paint.setAlpha(255);
}
}
public Paint getPaint() {
return paint;
}
public void setPaint(Paint paint) {
this.paint = paint;
}
public boolean isReversal() {
return isReversal;
}
public void setReversal(boolean reversal) {
isReversal = reversal;
}
public void setVerticalPadding(int verticalPadding) {
this.verticalPadding = verticalPadding;
}
public int getVerticalPadding() {
return verticalPadding;
}
public int getHorizontalPadding() {
return horizontalPadding;
}
public void setHorizontalPadding(int horizontalPadding) {
this.horizontalPadding = horizontalPadding;
}
public int getColorFilter() {
return colorFilter;
}
public void setColorFilter(int colorFilter) {
this.colorFilter = colorFilter;
}
public float getAlpha() {
return alpha;
}
public void setAlpha(float alpha) {
this.alpha = alpha;
}
}
package com.daivd.chart.provider.component.tip;
import android.graphics.Canvas;
import android.graphics.Rect;
/**
* Created by huang on 2017/10/20.
*/
public interface ITip<C,S> {
void drawTip(Canvas canvas, float x, float y, Rect rect, C content,int position);
boolean isShowTip(C c,int position);
S format(C c,int position);
}
package com.daivd.chart.provider.component.tip;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import com.daivd.chart.utils.DensityUtils;
/**
* Created by huang on 2017/10/20.
* 气泡提示
*/
public abstract class MultiLineBubbleTip<C> extends BaseBubbleTip<C,String[]>{
private int lineSpacing;
public MultiLineBubbleTip(Context context, int backgroundDrawableID, int triangleDrawableID, Paint paint) {
super(context, backgroundDrawableID, triangleDrawableID, paint);
lineSpacing = DensityUtils.dp2px(context,3);
}
@Override
public int getTextHeight(String[] content) {
Paint.FontMetrics fontMetrics = getPaint().getFontMetrics();
int textHeight= (int) (fontMetrics.bottom - fontMetrics.top);
return (textHeight +lineSpacing)*content.length - lineSpacing;
}
@Override
public int getTextWidth(String[] content) {
int maxLength = 0;
for(int i = 0;i < content.length;i++) {
int length = (int) getPaint().measureText(content[i]);
if (length > maxLength) {
maxLength = length;
}
}
return maxLength;
}
@Override
public void drawText(Canvas canvas, Rect tipRect, String[] content, int textWidth, int textHeight, Paint paint) {
int lineHeight =textHeight/content.length;
for(int i = 0;i < content.length;i++) {
String c = content[i];
int bottom = tipRect.top+getVerticalPadding()+lineHeight+ (lineHeight)*i - lineSpacing-deviation/2;
int left = tipRect.centerX()-textWidth/2;
canvas.drawText(c,left,bottom,paint);
}
}
}
package com.daivd.chart.provider.component.tip;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
/**
* Created by huang on 2017/10/20.
* 单行气泡提示
*/
public abstract class SingleLineBubbleTip<C> extends BaseBubbleTip<C,String> {
public SingleLineBubbleTip(Context context, int backgroundDrawableID, int triangleDrawableID, Paint paint){
super(context,backgroundDrawableID,triangleDrawableID,paint);
}
@Override
public int getTextHeight(String content) {
Paint.FontMetrics fontMetrics = getPaint().getFontMetrics();
return (int) (fontMetrics.bottom - fontMetrics.top);
}
@Override
public int getTextWidth(String content) {
return (int) getPaint().measureText(content);
}
@Override
public void drawText(Canvas canvas, Rect tipRect, String content, int textWidth,int textHeight, Paint paint) {
canvas.drawText(content,tipRect.centerX()-textWidth/2,tipRect.centerY()+textHeight/2-deviation/2,paint);
}
}
package com.daivd.chart.provider.pie;
import android.content.Context;
import android.graphics.Camera;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import com.daivd.chart.data.ChartData;
import com.daivd.chart.data.PieData;
/**
* Created by huang on 2017/10/9.
* 3D饼图
* 试验中,未完成
*/
public class Pie3DProvider extends PieProvider {
private int borderWidth = 20;
//提供摄像头
private Camera camera = new Camera();
public Pie3DProvider(Context context){
//拉远摄像头Z轴
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
float newZ = -displayMetrics.density * 6;
camera.setLocation(0,0,newZ);
}
@Override
public boolean calculationChild(ChartData<PieData> chartData) {
return true;
}
/**
* 变形开始
*
*/
@Override
protected void matrixRectStart(Canvas canvas, Rect rect) {
canvas.save();
camera.save();
canvas.translate(rect.centerX(),rect.centerY());
camera.rotateX(60);
camera.applyToCanvas(canvas);
canvas.translate(-rect.centerX(),-rect.centerY());
canvas.clipRect(rect);
if(rotateHelper != null && rotateHelper.isRotate()){
canvas.rotate((float) rotateHelper.getStartAngle(),rect.centerX(),rect.centerY());
}
}
@Override
protected void drawProvider(Canvas canvas, Rect zoomRect, Rect rect, Paint paint) {
super.drawProvider(canvas, zoomRect, rect, paint);
PointF centerPoint = getCenterPoint();
paint.setStrokeWidth(borderWidth);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.parseColor("#80FFFFFF"));
int radius = getCenterRadius();
canvas.drawCircle(centerPoint.x,centerPoint.y,radius*getCenterCirclePercent()+borderWidth/2,paint);
canvas.drawCircle(centerPoint.x,centerPoint.y,radius-borderWidth/2,paint);
}
/**
* 变形结束
*
*/
@Override
protected void matrixRectEnd(Canvas canvas, Rect rect) {
camera.restore();
super.matrixRectEnd(canvas, rect);
}
}
package com.daivd.chart.provider.pie;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import com.daivd.chart.data.ChartData;
import com.daivd.chart.data.format.IFormat;
import com.daivd.chart.data.PieData;
import com.daivd.chart.data.style.FontStyle;
import com.daivd.chart.matrix.RotateHelper;
import com.daivd.chart.provider.BaseProvider;
import com.daivd.chart.utils.ColorUtils;
import java.util.List;
/**
* Created by huang on 2017/10/9.
*/
public class PieProvider extends BaseProvider<PieData> {
private RectF oval;
private int totalAngle = 360;
private PointF centerPoint;
private int centerRadius;
private double clickAngle = -1;
private final float startAngle = -90;
protected RotateHelper rotateHelper;
private FontStyle textStyle = new FontStyle();
private float centerCirclePercent = 0.3f;
private boolean isClick;
private IFormat<Double> valueFormat;
private PorterDuffXfermode porterDuffXfermode;
@Override
public boolean calculationChild(ChartData<PieData> chartData) {
return true;
}
/**
*变形开始
*/
@Override
protected void matrixRectStart(Canvas canvas, Rect rect) {
super.matrixRectStart(canvas, rect);
if(rotateHelper != null && rotateHelper.isRotate()){
canvas.rotate((float) rotateHelper.getStartAngle(),rect.centerX(),rect.centerY());
}
}
/**
* 绘制内容
*/
@Override
protected void drawProvider(Canvas canvas, Rect zoomRect, Rect rect, Paint paint) {
int layerId = canvas.saveLayer(rect.left,rect.top,rect.right,rect.bottom, null, Canvas.ALL_SAVE_FLAG);
float startAngle = this.startAngle;
float totalAngle = getAnimValue(this.totalAngle);
paint.setStyle(Paint.Style.FILL);
int h = zoomRect.height();
int w = zoomRect.width();
int maxRadius = Math.min(w/2, h/2);
int x = maxRadius / 10;
centerRadius = maxRadius - x;
if(oval == null) {
oval = new RectF(zoomRect.centerX() - centerRadius, zoomRect.centerY() - centerRadius,
zoomRect.centerX() + centerRadius, zoomRect.centerY() + centerRadius);
centerPoint = new PointF(zoomRect.centerX(), zoomRect.centerY());
}
if(rotateHelper != null){
rotateHelper.setRadius(centerRadius);
rotateHelper.setOriginRect(rect);
}
List<PieData> pieDataList = chartData.getColumnDataList();
float totalScores = 0f;
for (PieData pieData: pieDataList) {
totalScores += pieData.getChartYDataList();
}
for (int i = 0; i < pieDataList.size(); i++) {
PieData pieData = pieDataList.get(i);
double value = pieData.getChartYDataList();
float sweepAngle = (float) (totalAngle * value / totalScores);
if(pieData.isDraw()) {
paint.setColor(pieData.getColor());
if (clickAngle != -1 && clickAngle > startAngle && clickAngle < startAngle + sweepAngle) {
paint.setColor(ColorUtils.getDarkerColor(pieData.getColor()));
if(isClick && onClickColumnListener != null){
onClickColumnListener.onClickColumn(pieData,0);
isClick = false;
}
}
canvas.drawArc(oval, startAngle, sweepAngle, true, paint);
if(isShowText()) {
canvas.save();
canvas.rotate(startAngle + sweepAngle / 2 - this.startAngle, zoomRect.centerX(), zoomRect.centerY());
textStyle.fillPaint(paint);
int textHeight = (int) paint.measureText("1", 0, 1);
String val =valueFormat != null ?valueFormat.format(value):String.valueOf(value);
canvas.drawText(val, zoomRect.centerX() - val.length() * textHeight / 2, zoomRect.centerY() - maxRadius / 2, paint);
canvas.restore();
}
}
startAngle += sweepAngle;
}
//裁切中间圆
if(porterDuffXfermode == null){
porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT);
}
paint.setXfermode(porterDuffXfermode);
if(centerCirclePercent >0) {
paint.setColor(Color.TRANSPARENT);
canvas.drawCircle(rect.centerX(),rect.centerY(),centerRadius *centerCirclePercent,paint);
}
canvas.restoreToCount(layerId);
paint.setXfermode(null);
}
@Override
public double[] setMaxMinValue(double maxMinValue, double minValue) {
return new double[0];
}
@Override
public void clickPoint(PointF point) {
super.clickPoint(point);
if(centerPoint != null){
int x = (int) (point.x - centerPoint.x);
int y = (int) (point.y - centerPoint.y);
int z = (int) Math.sqrt(Math.pow(Math.abs(x), 2) + Math.pow(Math.abs(y), 2));
if (/*z >= centerRadius - circleBorder / 2 &&*/ z <= centerRadius + 20) {
double angle = Math.abs(Math.toDegrees(Math.atan((point.y - centerPoint.y) / (point.x - centerPoint.x))));
if (x >= 0 && y < 0) {
angle = 90 - angle;
} else if (x >= 0 && y >= 0) {
angle = 90 + angle;
} else if (x < 0 && y >= 0) {
angle = 270 - angle;
} else {
angle = 270 + angle;
}
angle = (angle -rotateHelper.getStartAngle())%360;
angle = angle <0 ? 360+angle:angle;
clickAngle = angle + startAngle;
isClick = true;
return;
}
clickAngle = -1;
}
}
public PointF getCenterPoint() {
return centerPoint;
}
public void setCenterPoint(PointF centerPoint) {
this.centerPoint = centerPoint;
}
public int getCenterRadius() {
return centerRadius;
}
public void setCenterRadius(int centerRadius) {
this.centerRadius = centerRadius;
}
public void setValueFormat(IFormat<Double> valueFormat) {
this.valueFormat = valueFormat;
}
public void setCenterCirclepercent(float centerCirclePercent) {
this.centerCirclePercent = centerCirclePercent;
}
public float getCenterCirclePercent() {
return centerCirclePercent;
}
public void setCenterCirclePercent(float centerCirclePercent) {
this.centerCirclePercent = centerCirclePercent;
}
public void setRotateHelper(RotateHelper rotateHelper) {
this.rotateHelper = rotateHelper;
}
}
package com.daivd.chart.provider.radar;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import com.daivd.chart.component.base.IAxis;
import com.daivd.chart.data.ChartData;
import com.daivd.chart.data.format.IFormat;
import com.daivd.chart.data.BarData;
import com.daivd.chart.data.RadarData;
import com.daivd.chart.data.ScaleData;
import com.daivd.chart.data.style.FontStyle;
import com.daivd.chart.data.style.LineStyle;
import com.daivd.chart.exception.ChartException;
import com.daivd.chart.matrix.RotateHelper;
import com.daivd.chart.provider.BaseProvider;
import java.util.List;
/**
* Created by huang on 2017/10/9.
*雷达图内容绘制
*/
public class RadarProvider extends BaseProvider<RadarData> {
private int centerRadius;
private RotateHelper rotateHelper;
private FontStyle scaleStyle = new FontStyle();
private LineStyle lineStyle = new LineStyle();
protected LineStyle gridStyle = new LineStyle(); //网格样式
private int textHeight;
private boolean isShowScale;
private IFormat<Double> scaleFormat;
@Override
protected void matrixRectStart(Canvas canvas, Rect rect) {
super.matrixRectStart(canvas, rect);
if(rotateHelper != null && rotateHelper.isRotate()){
canvas.rotate((float) rotateHelper.getStartAngle(),rect.centerX(),rect.centerY());
}
}
@Override
protected void drawProvider(Canvas canvas, Rect zoomRect, Rect rect, Paint paint) {
int w = zoomRect.width();
int h = zoomRect.height();
int maxRadius = Math.min(w/2, h/2);
textHeight = (int) (paint.measureText("1",0,1));
scaleStyle.fillPaint(paint);
String oneXData = chartData.getCharXDataList().get(0);
int x = maxRadius / 10 + textHeight * oneXData.length();
centerRadius = maxRadius - x;
if(rotateHelper != null){
rotateHelper.setOriginRect(rect);
rotateHelper.setRadius(centerRadius);
}
drawRadarBorder(canvas,zoomRect,paint);
drawRadarLines(canvas,zoomRect,paint);
drawLinePath(canvas,zoomRect,paint);
drawQuadrantText(canvas,zoomRect,paint);
}
private void drawLinePath(Canvas canvas,Rect zoomRect,Paint paint){
List<String> charXDataList = chartData.getCharXDataList();
int count = charXDataList.size();
ScaleData scaleData = chartData.getScaleData();
List<RadarData> columnDataList = chartData.getColumnDataList();
double scaleLength = scaleData.getTotalScaleLength(IAxis.AxisDirection.LEFT);
float angle = (float) (Math.PI*2/count);
Path path = new Path();
lineStyle.fillPaint(paint);
for(int i = 0;i <columnDataList.size();i++){
BarData columnData = columnDataList.get(i);
if(columnData.isDraw()) {
paint.setColor(columnData.getColor());
path.reset();
for (int j = 0; j < columnData.getChartYDataList().size(); j++) {
double value = columnData.getChartYDataList().get(j);
float curR = getAnimValue((float) ( value * centerRadius / scaleLength));
if (j == 0) {
path.moveTo(zoomRect.centerX() + curR, zoomRect.centerY());
} else {
//根据半径,计算出蜘蛛丝上每个点的坐标
float x = (float) (zoomRect.centerX() + curR * Math.cos(angle * j));
float y = (float) (zoomRect.centerY() + curR * Math.sin(angle * j));
path.lineTo(x, y);
}
}
path.close();
canvas.drawPath(path, paint);
}
}
}
private void drawRadarBorder(Canvas canvas,Rect zoomRect,Paint paint){
List<String> charXDataList = chartData.getCharXDataList();
int count = charXDataList.size();
float angle = (float) (Math.PI*2/count);
ScaleData scaleData = chartData.getScaleData();
List<Double> scaleList= scaleData.getScaleList(IAxis.AxisDirection.LEFT);
double maxScale = scaleData.getMaxScaleValue(IAxis.AxisDirection.LEFT);
Path path = new Path();
for(int i = 0;i < scaleList.size();i++){
path.reset();
double value = scaleList.get(i);
float curR = (float)(value*centerRadius/maxScale);
for(int j = 0; j <count;j++){
float y;
float x;
if(j==0){
x = zoomRect.centerX()+curR;
y = zoomRect.centerY();
path.moveTo(x,y);
}else{
//根据半径,计算出蜘蛛丝上每个点的坐标
x= (float) (zoomRect.centerX()+curR*Math.cos(angle*j));
y = (float) (zoomRect.centerY()+curR*Math.sin(angle*j));
path.lineTo(x,y);
}
}
gridStyle.fillPaint(paint);
path.close();
canvas.drawPath(path,paint);
scaleStyle.fillPaint(paint);
int textHeight = (int) paint.measureText("1",0,1);
if(isShowScale) {
String valueStr = getFormatValue(value);
canvas.drawText(valueStr, zoomRect.centerX() - textHeight*valueStr.length()/2 , (float)( zoomRect.centerY() - curR*Math.sin(Math.PI/3)), paint);
}
}
}
private String getFormatValue(double value){
return scaleFormat !=null ? scaleFormat.format(value):String.valueOf(value);
}
/**
* 绘制象限文字
* @param canvas
*/
private void drawQuadrantText(Canvas canvas,Rect zoomRect,Paint paint){
scaleStyle.fillPaint(paint);
List<String> charXDataList = chartData.getCharXDataList();
int count = charXDataList.size();
float angle = (float) (Math.PI*2/count);
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
float fontHeight = fontMetrics.descent - fontMetrics.ascent;
for(int i=0;i<count;i++){
String xData = charXDataList.get(i);
float x = (float) (zoomRect.centerX()+(centerRadius+fontHeight/2)*Math.cos(angle*i));
float y = (float) (zoomRect.centerY()+(centerRadius+fontHeight/2)*Math.sin(angle*i));
if(angle*i>=0&&angle*i<=Math.PI/2){//第4象限
canvas.drawText(xData, x,y,paint);
}else if(angle*i>=3*Math.PI/2&&angle*i<=Math.PI*2){//第3象限
canvas.drawText(xData, x,y,paint);
}else if(angle*i>Math.PI/2&&angle*i<=Math.PI){//第2象限
float dis = paint.measureText(xData);//文本长度
canvas.drawText(xData, x-dis,y,paint);
}else if(angle*i>=Math.PI&&angle*i<3*Math.PI/2){//第1象限
float dis = paint.measureText(xData);//文本长度
canvas.drawText(xData, x-dis,y,paint);
}
}
}
/**
* 绘制直线
*/
private void drawRadarLines(Canvas canvas,Rect zoomRect,Paint paint){
gridStyle.fillPaint(paint);
Path path = new Path();
int count = chartData.getCharXDataList().size();
float angle = (float) (Math.PI*2/count);
for(int i=0;i<count;i++){
path.reset();
path.moveTo(zoomRect.centerX(), zoomRect.centerY());
float x = (float) (zoomRect.centerX()+centerRadius*Math.cos(angle*i));
float y = (float) (zoomRect.centerY()+centerRadius*Math.sin(angle*i));
path.lineTo(x, y);
canvas.drawPath(path, paint);
}
}
/* @Override
public void clickPoint(PointF point) {
super.clickPoint(point);
if(centerPoint != null){
int x = (int) (point.x - centerPoint.x);
int y = (int) (point.y - centerPoint.y);
int z = (int) Math.sqrt(Math.pow(Math.abs(x), 2) + Math.pow(Math.abs(y), 2));
if (*//*z >= centerRadius - circleBorder / 2 &&*//* z <= centerRadius + 20) {
double angle = Math.abs(Math.toDegrees(Math.atan((point.y - centerPoint.y) / (point.x - centerPoint.x))));
if (x >= 0 && y < 0) {
angle = 90 - angle;
} else if (x >= 0 && y >= 0) {
angle = 90 + angle;
} else if (x < 0 && y >= 0) {
angle = 270 - angle;
} else {
angle = 270 + angle;
}
angle = (angle -rotateHelper.getStartAngle())%360;
angle = angle <0 ? 360+angle:angle;
clickAngle = angle + startAngle;
return;
}
clickAngle = -1;
}
}*/
@Override
public boolean calculationChild( ChartData<RadarData> chartData) {
this.chartData = chartData;
ScaleData scaleData =this.chartData.getScaleData();
List<RadarData> columnDatas = chartData.getColumnDataList();
if(columnDatas == null || columnDatas.size() == 0){
return false;
}
int columnSize = columnDatas.size();
for(int i = 0 ; i <columnSize; i++){
BarData columnData = columnDatas.get(i);
if(!columnData.isDraw()){
continue;
}
List<Double> datas = columnData.getChartYDataList();
if(datas == null || datas.size() == 0){
throw new ChartException("Please set up Column data");
}
scaleData.rowSize = datas.size();
if(datas.size() != scaleData.rowSize){
throw new ChartException("Column rows data inconsistency");
}
double[] scale = getColumnScale(datas);
scale = setMaxMinValue(scale[0],scale[1]);
if(columnData.getDirection() == IAxis.AxisDirection.LEFT){
if(!scaleData.isLeftHasValue){
scaleData.maxLeftValue = scale[0];
scaleData.minLeftValue = scale[1];
scaleData.isLeftHasValue = true;
}else{
scaleData.maxLeftValue = Math.max( scaleData.maxLeftValue,scale[0]);
scaleData.minLeftValue = Math.min( scaleData.minLeftValue,scale[1]);
}
}else{
if(!scaleData.isRightHasValue){
scaleData.maxRightValue = scale[0];
scaleData.minRightValue= scale[1];
scaleData.isRightHasValue = true;
}else{
scaleData.maxRightValue = Math.max(scaleData.maxRightValue,scale[0]);
scaleData.minRightValue = Math.min(scaleData.minRightValue,scale[1]);
}
}
}
if(chartData.getScaleData().rowSize == 0){
return false;
}
return true;
}
@Override
public double[] setMaxMinValue(double maxValue, double minValue) {
double dis = Math.abs(maxValue -minValue);
maxValue = maxValue + dis*0.2;
if(minValue >0){
minValue = 0;
}else{
minValue = minValue - dis*0.2;
}
return new double[]{maxValue,minValue};
}
private double[] getColumnScale(List<Double> values) {
double maxValue = 0;
double minValue =0;
int size = values.size();
for(int j= 0;j < size;j++) {
double d = values.get(j) ;
if(j == 0){
maxValue = d;
minValue = d;
}
if (maxValue < d){
maxValue = d;
}else if(minValue >d){
minValue = d;
}
}
return new double[] {maxValue,minValue};
}
public FontStyle getScaleStyle() {
return scaleStyle;
}
public LineStyle getLineStyle() {
return lineStyle;
}
public LineStyle getGridStyle() {
return gridStyle;
}
public void setRotateHelper(RotateHelper rotateHelper) {
this.rotateHelper = rotateHelper;
}
public void setScaleFormat(IFormat<Double> scaleFormat) {
this.scaleFormat = scaleFormat;
}
public void setShowScale(boolean showScale) {
isShowScale = showScale;
}
}
package com.daivd.chart.provider.rose;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import com.daivd.chart.component.base.IAxis;
import com.daivd.chart.data.ChartData;
import com.daivd.chart.data.format.IFormat;
import com.daivd.chart.data.BarData;
import com.daivd.chart.data.RoseData;
import com.daivd.chart.data.ScaleData;
import com.daivd.chart.data.style.FontStyle;
import com.daivd.chart.data.style.LineStyle;
import com.daivd.chart.exception.ChartException;
import com.daivd.chart.matrix.RotateHelper;
import com.daivd.chart.provider.BaseProvider;
import java.util.List;
/**
* Created by huang on 2017/10/9.
*/
public class RoseProvider extends BaseProvider<RoseData> {
private int centerRadius;
private RotateHelper rotateHelper;
private FontStyle scaleStyle = new FontStyle();
private LineStyle lineStyle = new LineStyle();
private float startAngle=0;
protected LineStyle gridStyle = new LineStyle(); //网格样式
private int textHeight;
private boolean isShowScale;
private IFormat<Double> scaleFormat;
@Override
protected void matrixRectStart(Canvas canvas, Rect rect) {
super.matrixRectStart(canvas, rect);
if (rotateHelper != null && rotateHelper.isRotate()) {
canvas.rotate((float) rotateHelper.getStartAngle(), rect.centerX(), rect.centerY());
}
}
@Override
protected void drawProvider(Canvas canvas, Rect zoomRect, Rect rect, Paint paint) {
int w = zoomRect.width();
int h = zoomRect.height();
int maxRadius = Math.min(w / 2, h / 2);
textHeight = (int) (paint.measureText("1", 0, 1));
scaleStyle.fillPaint(paint);
String oneXData = chartData.getCharXDataList().get(0);
int x = maxRadius / 10 + textHeight * oneXData.length();
centerRadius = maxRadius - x;
if (rotateHelper != null) {
rotateHelper.setOriginRect(rect);
rotateHelper.setRadius(centerRadius);
}
drawRadarBorder(canvas, zoomRect, paint);
drawRadarLines(canvas, zoomRect, paint);
drawLinePath(canvas, zoomRect, rect, paint);
drawQuadrantText(canvas, zoomRect, paint);
drawScale(canvas,zoomRect,paint);
}
private void drawLinePath(Canvas canvas, Rect zoomRect, Rect rect, Paint paint) {
List<String> charXDataList = chartData.getCharXDataList();
int count = charXDataList.size();
ScaleData scaleData = chartData.getScaleData();
List<RoseData> columnDataList = chartData.getColumnDataList();
double maxScale = scaleData.getMaxScaleValue(IAxis.AxisDirection.LEFT);
float angle = 360f / count;
startAngle = angle/2;
paint.setStyle(Paint.Style.FILL);
for (int j = 0; j < count; j++) {
double value = 0;
for (BarData lineData :columnDataList) {
if(lineData.isDraw()) {
value += lineData.getChartYDataList().get(j);
}
}
for (int i = columnDataList.size() - 1; i >= 0; i--) {
BarData lineData = columnDataList.get(i);
if(lineData.isDraw()) {
float curR = getAnimValue((float) (value * centerRadius / maxScale));
RectF rectF = new RectF(zoomRect.centerX() - curR, zoomRect.centerY() - curR, zoomRect.centerX() + curR, zoomRect.centerY() + curR);
paint.setColor(lineData.getColor());
canvas.drawArc(rectF, angle * j + angle / 6 - startAngle - 90, angle - angle / 6, true, paint);
value -= lineData.getChartYDataList().get(j);
}
}
}
}
private void drawRadarBorder(Canvas canvas, Rect zoomRect, Paint paint) {
gridStyle.fillPaint(paint);
canvas.drawCircle(zoomRect.centerX(), zoomRect.centerY(), centerRadius, paint);
canvas.drawCircle(zoomRect.centerX(), zoomRect.centerY(), centerRadius / 2, paint);
}
private void drawScale(Canvas canvas, Rect zoomRect, Paint paint){
double maxScale = chartData.getScaleData().getMaxScaleValue(IAxis.AxisDirection.LEFT);
if(isShowScale) {
scaleStyle.fillPaint(paint);
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
float fontHeight = fontMetrics.descent - fontMetrics.ascent;
canvas.drawText(getFormatValue(maxScale),
zoomRect.centerX(), zoomRect.centerY() - centerRadius+fontHeight, paint);
canvas.drawText(getFormatValue(maxScale/2),
zoomRect.centerX(),zoomRect.centerY()-centerRadius/2+fontHeight,paint);
}
}
/**
* 绘制象限文字
*
* @param canvas
*/
private void drawQuadrantText(Canvas canvas, Rect zoomRect, Paint paint) {
scaleStyle.fillPaint(paint);
List<String> charXDataList = chartData.getCharXDataList();
int count = charXDataList.size();
float angle = (float) (Math.PI*2/count);
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
float fontHeight = fontMetrics.descent - fontMetrics.ascent;
for(int i=0;i<count;i++){
float realAngle = (float) (angle*i - Math.PI/2);
String xData = charXDataList.get(i);
float x = (float) (zoomRect.centerX()+(centerRadius+fontHeight/2)*Math.cos(realAngle));
float y = (float) (zoomRect.centerY()+(centerRadius+fontHeight/2)*Math.sin(realAngle))+5;
if(realAngle>=0&&realAngle<=Math.PI/2){//第4象限
canvas.drawText(xData, x,y,paint);
}else if(realAngle<0){//第3象限
canvas.drawText(xData, x,y,paint);
}else if(realAngle>Math.PI/2&&realAngle<=Math.PI){//第2象限
float dis = paint.measureText(xData);//文本长度
canvas.drawText(xData, x-dis,y,paint);
}else if(realAngle>=Math.PI&&realAngle<3*Math.PI/2){//第1象限
float dis = paint.measureText(xData);//文本长度
canvas.drawText(xData, x-dis,y,paint);
}
}
}
/**
* 绘制直线
*/
private void drawRadarLines(Canvas canvas, Rect zoomRect, Paint paint) {
gridStyle.fillPaint(paint);
Path path = new Path();
int count = chartData.getCharXDataList().size();
float angle = (float) (Math.PI * 2 / count);
for (int i = 0; i < count; i++) {
path.reset();
path.moveTo(zoomRect.centerX(), zoomRect.centerY());
float x = (float) (zoomRect.centerX() + centerRadius * Math.cos(angle * i-Math.PI/2));
float y = (float) (zoomRect.centerY() + centerRadius * Math.sin(angle * i-Math.PI/2));
path.lineTo(x, y);
canvas.drawPath(path, paint);
}
}
private String getFormatValue(double value){
return scaleFormat !=null ? scaleFormat.format(value):String.valueOf(value);
}
/* @Override
public void clickPoint(PointF point) {
super.clickPoint(point);
if(centerPoint != null){
int x = (int) (point.x - centerPoint.x);
int y = (int) (point.y - centerPoint.y);
int z = (int) Math.sqrt(Math.pow(Math.abs(x), 2) + Math.pow(Math.abs(y), 2));
if (*//*z >= centerRadius - circleBorder / 2 &&*//* z <= centerRadius + 20) {
double angle = Math.abs(Math.toDegrees(Math.atan((point.y - centerPoint.y) / (point.x - centerPoint.x))));
if (x >= 0 && y < 0) {
angle = 90 - angle;
} else if (x >= 0 && y >= 0) {
angle = 90 + angle;
} else if (x < 0 && y >= 0) {
angle = 270 - angle;
} else {
angle = 270 + angle;
}
angle = (angle -rotateHelper.getStartAngle())%360;
angle = angle <0 ? 360+angle:angle;
clickAngle = angle + startAngle;
return;
}
clickAngle = -1;
}
}*/
void setSelection(int position) {
}
@Override
public boolean calculationChild(ChartData<RoseData> chartData) {
this.chartData = chartData;
ScaleData scaleData = this.chartData.getScaleData();
scaleData.maxLeftValue = 0;
List<RoseData> columnDatas = chartData.getColumnDataList();
if (columnDatas == null || columnDatas.size() == 0) {
return false;
}
int columnSize = columnDatas.size();
for (int j = 0; j < chartData.getCharXDataList().size(); j++) {
float maxValue = 0;
for (int i = 0; i < columnSize; i++) {
BarData columnData = columnDatas.get(i);
if (!columnData.isDraw()) {
continue;
}
List<Double> datas = columnData.getChartYDataList();
if (datas == null || datas.size() == 0) {
throw new ChartException("Please set up Column data");
}
scaleData.rowSize = datas.size();
if (datas.size() != scaleData.rowSize) {
throw new ChartException("Column rows data inconsistency");
}
double value = datas.get(j);
if (value < 0) {
throw new ChartException("The value cannot be <0");
}
maxValue += value;
}
if (!scaleData.isLeftHasValue) {
scaleData.maxLeftValue = maxValue;
scaleData.isLeftHasValue = true;
} else {
scaleData.maxLeftValue = Math.max(maxValue, scaleData.maxLeftValue);
}
}
scaleData.maxLeftValue = setMaxMinValue(scaleData.maxLeftValue, 0)[0];
return true;
}
@Override
public double[] setMaxMinValue ( double maxValue, double minValue){
double dis = Math.abs(maxValue - minValue);
maxValue = maxValue + dis * 0.5;
return new double[]{maxValue, minValue};
}
public FontStyle getScaleStyle() {
return scaleStyle;
}
public LineStyle getLineStyle() {
return lineStyle;
}
public LineStyle getGridStyle() {
return gridStyle;
}
public void setRotateHelper(RotateHelper rotateHelper) {
this.rotateHelper = rotateHelper;
}
public void setScaleFormat(IFormat<Double> scaleFormat) {
this.scaleFormat = scaleFormat;
}
public void setShowScale(boolean showScale) {
isShowScale = showScale;
}
}
package com.daivd.chart.utils;
import android.graphics.Color;
/**
* Created by huang on 2017/9/28.
*/
public class ColorUtils {
public static int getDarkerColor(int color){
float[] hsv = new float[3];
Color.colorToHSV(color, hsv); // convert to hsv
// make darker
hsv[1] = hsv[1] + 0.1f; // more saturation
hsv[2] = hsv[2] - 0.1f; // less brightness
int darkerColor = Color.HSVToColor(hsv);
return darkerColor ;
}
public static int getBrighterColor(int color){
float[] hsv = new float[3];
Color.colorToHSV(color, hsv); // convert to hsv
hsv[1] = hsv[1] - 0.1f; // less saturation
hsv[2] = hsv[2] + 0.1f; // more brightness
int darkerColor = Color.HSVToColor(hsv);
return darkerColor ;
}
/**
* 修改颜色透明度
* @param color
* @param alpha
* @return
*/
public static int changeAlpha(int color, int alpha) {
int red = Color.red(color);
int green = Color.green(color);
int blue = Color.blue(color);
return Color.argb(alpha, red, green, blue);
}
}
package com.daivd.chart.utils;
import android.content.Context;
import android.util.TypedValue;
//常用单位转换的辅助类
public class DensityUtils {
private DensityUtils() {
/* cannot be instantiated */
throw new UnsupportedOperationException("cannot be instantiated");
}
/**
* dp转px
*
* @param context
* @return
*/
public static int dp2px(Context context, float dpVal)
{
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
dpVal, context.getResources().getDisplayMetrics());
}
/**
* sp转px
*
* @param context
* @return
*/
public static int sp2px(Context context, float spVal)
{
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
spVal, context.getResources().getDisplayMetrics());
}
/**
* px转dp
*
* @param context
* @param pxVal
* @return
*/
public static float px2dp(Context context, float pxVal)
{
final float scale = context.getResources().getDisplayMetrics().density;
return (pxVal / scale);
}
/**
* px转sp
*
* @param pxVal
* @return
*/
public static float px2sp(Context context, float pxVal)
{
return (pxVal / context.getResources().getDisplayMetrics().scaledDensity);
}
}
\ No newline at end of file
package com.daivd.chart.utils;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import com.daivd.chart.data.style.FontStyle;
/**
* Created by huang on 2017/11/1.
*/
public class DrawUtils {
public static int getTextHeight(FontStyle style, Paint paint){
style.fillPaint(paint);
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
return (int) (fontMetrics.descent - fontMetrics.ascent);
}
public static int getTextHeight(Paint paint){
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
return (int) (fontMetrics.descent - fontMetrics.ascent);
}
public static float getTextCenterY(int centerY,Paint paint){
return centerY-((paint.descent() + paint.ascent()) / 2);
}
public static boolean isMixRect(Rect rect,int left,int top,int right,int bottom){
return rect.bottom>= top && rect.right >= left && rect.top <bottom && rect.left< right;
}
public static boolean isClick(int left, int top, int right, int bottom, PointF clickPoint){
return clickPoint.x >= left && clickPoint.x <=right && clickPoint.y>=top && clickPoint.y <=bottom;
}
public static boolean isMixHorizontalRect(Rect rect,int left,int right){
return rect.right >= left && rect.left<= right;
}
public static boolean isVerticalMixRect(Rect rect,int top,int bottom){
return rect.bottom>= top && rect.top <=bottom;
}
}
<vector android:height="24dp" android:viewportHeight="1024.0"
android:viewportWidth="1024.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#4096EE" android:pathData="M846.4,359.1c137.9,84.4 138.2,220.9 0,305.5l-523.7,320.4C184.9,1069.4 73.1,1006.2 73.1,845.9V178C73.1,16.7 184.5,-45.9 322.7,38.7l523.7,320.5z"/>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>
<resources>
<string name="app_name">Smartchart</string>
</resources>
......@@ -53,5 +53,7 @@ dependencies {
// }
testImplementation rootProject.ext.dependencies["junit"]
implementation rootProject.ext.dependencies["BaseRecyclerViewAdapter"]
implementation 'com.github.Liberuman:ShadowDrawable:0.1'//陰影背景
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'//報表圖
implementation project(':chart')
}
......@@ -3,6 +3,7 @@
package="com.gingersoft.gsa.cloud.main">
<application>
<activity android:name=".mvp.ui.activity.NewMainActivity"/>
<activity android:name=".mvp.ui.activity.MainActivity"/>
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.gingersoft.gsa.cloud.main">
......@@ -17,9 +18,9 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".mvp.ui.activity.MainActivity"/>
<activity
android:name=".mvp.ui.activity.MainActivity"
android:name=".mvp.ui.activity.NewMainActivity"
android:theme="@style/MainTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
......@@ -32,7 +33,6 @@
<meta-data
android:name="network.config.GlobalConfiguration"
android:value="ConfigModule" />
<meta-data
android:name="design_width_in_dp"
android:value="360" />
......@@ -41,5 +41,4 @@
android:value="540" />
</application>
</manifest>
</manifest>
\ No newline at end of file
......@@ -5,6 +5,7 @@ import com.billy.cc.core.component.CCResult;
import com.billy.cc.core.component.CCUtil;
import com.billy.cc.core.component.IComponent;
import com.gingersoft.gsa.cloud.main.mvp.ui.activity.MainActivity;
import com.gingersoft.gsa.cloud.main.mvp.ui.activity.NewMainActivity;
public class ComponentMain implements IComponent {
......@@ -71,7 +72,7 @@ public class ComponentMain implements IComponent {
}
private void openActivity(CC cc) {
CCUtil.navigateTo(cc, MainActivity.class);
CCUtil.navigateTo(cc, NewMainActivity.class);
CC.sendCCResult(cc.getCallId(), CCResult.success());
}
......
package com.gingersoft.gsa.cloud.main.di.component;
import dagger.BindsInstance;
import dagger.Component;
import com.jess.arms.di.component.AppComponent;
import com.gingersoft.gsa.cloud.main.di.module.MainTopModule;
import com.gingersoft.gsa.cloud.main.mvp.contract.MainTopContract;
import com.jess.arms.di.scope.FragmentScope;
import com.gingersoft.gsa.cloud.main.mvp.ui.fragment.MainTopFragment;
/**
* ================================================
* Description:
* <p>
* Created by MVPArmsTemplate on 02/18/2020 17:21
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* <a href="https://github.com/JessYanCoding/MVPArms">Star me</a>
* <a href="https://github.com/JessYanCoding/MVPArms/wiki">See me</a>
* <a href="https://github.com/JessYanCoding/MVPArmsTemplate">模版请保持更新</a>
* ================================================
*/
@FragmentScope
@Component(modules = MainTopModule.class, dependencies = AppComponent.class)
public interface MainTopComponent {
void inject(MainTopFragment fragment);
@Component.Builder
interface Builder {
@BindsInstance
MainTopComponent.Builder view(MainTopContract.View view);
MainTopComponent.Builder appComponent(AppComponent appComponent);
MainTopComponent build();
}
}
\ No newline at end of file
package com.gingersoft.gsa.cloud.main.di.component;
import dagger.BindsInstance;
import dagger.Component;
import com.jess.arms.di.component.AppComponent;
import com.gingersoft.gsa.cloud.main.di.module.NewMainModule;
import com.gingersoft.gsa.cloud.main.mvp.contract.NewMainContract;
import com.jess.arms.di.scope.ActivityScope;
import com.gingersoft.gsa.cloud.main.mvp.ui.activity.NewMainActivity;
/**
* ================================================
* Description:
* <p>
* Created by MVPArmsTemplate on 02/18/2020 17:14
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* <a href="https://github.com/JessYanCoding/MVPArms">Star me</a>
* <a href="https://github.com/JessYanCoding/MVPArms/wiki">See me</a>
* <a href="https://github.com/JessYanCoding/MVPArmsTemplate">模版请保持更新</a>
* ================================================
*/
@ActivityScope
@Component(modules = NewMainModule.class, dependencies = AppComponent.class)
public interface NewMainComponent {
void inject(NewMainActivity activity);
@Component.Builder
interface Builder {
@BindsInstance
NewMainComponent.Builder view(NewMainContract.View view);
NewMainComponent.Builder appComponent(AppComponent appComponent);
NewMainComponent build();
}
}
\ No newline at end of file
package com.gingersoft.gsa.cloud.main.di.module;
import com.jess.arms.di.scope.FragmentScope;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import com.gingersoft.gsa.cloud.main.mvp.contract.MainTopContract;
import com.gingersoft.gsa.cloud.main.mvp.model.MainTopModel;
/**
* ================================================
* Description:
* <p>
* Created by MVPArmsTemplate on 02/18/2020 17:21
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* <a href="https://github.com/JessYanCoding/MVPArms">Star me</a>
* <a href="https://github.com/JessYanCoding/MVPArms/wiki">See me</a>
* <a href="https://github.com/JessYanCoding/MVPArmsTemplate">模版请保持更新</a>
* ================================================
*/
@Module
public abstract class MainTopModule {
@Binds
abstract MainTopContract.Model bindMainTopModel(MainTopModel model);
}
\ No newline at end of file
package com.gingersoft.gsa.cloud.main.di.module;
import com.jess.arms.di.scope.ActivityScope;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import com.gingersoft.gsa.cloud.main.mvp.contract.NewMainContract;
import com.gingersoft.gsa.cloud.main.mvp.model.NewMainModel;
/**
* ================================================
* Description:
* <p>
* Created by MVPArmsTemplate on 02/18/2020 17:14
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* <a href="https://github.com/JessYanCoding/MVPArms">Star me</a>
* <a href="https://github.com/JessYanCoding/MVPArms/wiki">See me</a>
* <a href="https://github.com/JessYanCoding/MVPArmsTemplate">模版请保持更新</a>
* ================================================
*/
@Module
public abstract class NewMainModule {
@Binds
abstract NewMainContract.Model bindNewMainModel(NewMainModel model);
}
\ No newline at end of file
package com.gingersoft.gsa.cloud.main.mvp.contract;
import com.gingersoft.gsa.cloud.main.mvp.model.bean.HomeTurnoverBean;
import com.jess.arms.mvp.IView;
import com.jess.arms.mvp.IModel;
import java.util.Map;
import io.reactivex.Observable;
import okhttp3.RequestBody;
/**
* ================================================
* Description:
* <p>
* Created by MVPArmsTemplate on 02/18/2020 17:21
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* <a href="https://github.com/JessYanCoding/MVPArms">Star me</a>
* <a href="https://github.com/JessYanCoding/MVPArms/wiki">See me</a>
* <a href="https://github.com/JessYanCoding/MVPArmsTemplate">模版请保持更新</a>
* ================================================
*/
public interface MainTopContract {
//对于经常使用的关于UI的方法可以定义到IView中,如显示隐藏进度条,和显示文字消息
interface View extends IView {
void loadBusinessInfo(Map<String, HomeTurnoverBean.DataBean> dataBeanMap);
}
//Model层定义接口,外部只需关心Model返回的数据,无需关心内部细节,即是否使用缓存
interface Model extends IModel {
Observable<HomeTurnoverBean> getRestaurantReport(RequestBody requestBody);
}
}
package com.gingersoft.gsa.cloud.main.mvp.contract;
import com.jess.arms.mvp.IView;
import com.jess.arms.mvp.IModel;
/**
* ================================================
* Description:
* <p>
* Created by MVPArmsTemplate on 02/18/2020 17:14
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* <a href="https://github.com/JessYanCoding/MVPArms">Star me</a>
* <a href="https://github.com/JessYanCoding/MVPArms/wiki">See me</a>
* <a href="https://github.com/JessYanCoding/MVPArmsTemplate">模版请保持更新</a>
* ================================================
*/
public interface NewMainContract {
//对于经常使用的关于UI的方法可以定义到IView中,如显示隐藏进度条,和显示文字消息
interface View extends IView {
}
//Model层定义接口,外部只需关心Model返回的数据,无需关心内部细节,即是否使用缓存
interface Model extends IModel {
}
}
package com.gingersoft.gsa.cloud.main.mvp.model;
import android.app.Application;
import com.gingersoft.gsa.cloud.main.mvp.model.bean.HomeTurnoverBean;
import com.gingersoft.gsa.cloud.main.mvp.model.service.HomeService;
import com.google.gson.Gson;
import com.jess.arms.integration.IRepositoryManager;
import com.jess.arms.mvp.BaseModel;
import com.jess.arms.di.scope.FragmentScope;
import javax.inject.Inject;
import com.gingersoft.gsa.cloud.main.mvp.contract.MainTopContract;
import io.reactivex.Observable;
import me.jessyan.retrofiturlmanager.RetrofitUrlManager;
import okhttp3.RequestBody;
/**
* ================================================
* Description:
* <p>
* Created by MVPArmsTemplate on 02/18/2020 17:21
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* <a href="https://github.com/JessYanCoding/MVPArms">Star me</a>
* <a href="https://github.com/JessYanCoding/MVPArms/wiki">See me</a>
* <a href="https://github.com/JessYanCoding/MVPArmsTemplate">模版请保持更新</a>
* ================================================
*/
@FragmentScope
public class MainTopModel extends BaseModel implements MainTopContract.Model {
@Inject
Gson mGson;
@Inject
Application mApplication;
@Inject
public MainTopModel(IRepositoryManager repositoryManager) {
super(repositoryManager);
}
@Override
public void onDestroy() {
super.onDestroy();
this.mGson = null;
this.mApplication = null;
}
@Override
public Observable<HomeTurnoverBean> getRestaurantReport(RequestBody requestBody) {
RetrofitUrlManager.getInstance().putDomain("common", "https://m.ricepon.com/member-web/api/");
return mRepositoryManager.obtainRetrofitService(HomeService.class)
.getRestaurantReport(requestBody);
}
}
\ No newline at end of file
package com.gingersoft.gsa.cloud.main.mvp.model;
import android.app.Application;
import com.google.gson.Gson;
import com.jess.arms.integration.IRepositoryManager;
import com.jess.arms.mvp.BaseModel;
import com.jess.arms.di.scope.ActivityScope;
import javax.inject.Inject;
import com.gingersoft.gsa.cloud.main.mvp.contract.NewMainContract;
/**
* ================================================
* Description:
* <p>
* Created by MVPArmsTemplate on 02/18/2020 17:14
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* <a href="https://github.com/JessYanCoding/MVPArms">Star me</a>
* <a href="https://github.com/JessYanCoding/MVPArms/wiki">See me</a>
* <a href="https://github.com/JessYanCoding/MVPArmsTemplate">模版请保持更新</a>
* ================================================
*/
@ActivityScope
public class NewMainModel extends BaseModel implements NewMainContract.Model {
@Inject
Gson mGson;
@Inject
Application mApplication;
@Inject
public NewMainModel(IRepositoryManager repositoryManager) {
super(repositoryManager);
}
@Override
public void onDestroy() {
super.onDestroy();
this.mGson = null;
this.mApplication = null;
}
}
\ No newline at end of file
package com.gingersoft.gsa.cloud.main.mvp.presenter;
import android.app.Application;
import com.gingersoft.gsa.cloud.base.utils.constans.HttpsConstans;
import com.gingersoft.gsa.cloud.base.utils.time.TimeUtils;
import com.gingersoft.gsa.cloud.main.mvp.model.bean.HomeTurnoverBean;
import com.jess.arms.integration.AppManager;
import com.jess.arms.di.scope.FragmentScope;
import com.jess.arms.mvp.BasePresenter;
import com.jess.arms.http.imageloader.ImageLoader;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.schedulers.Schedulers;
import me.jessyan.retrofiturlmanager.RetrofitUrlManager;
import me.jessyan.rxerrorhandler.core.RxErrorHandler;
import me.jessyan.rxerrorhandler.handler.ErrorHandleSubscriber;
import okhttp3.FormBody;
import okhttp3.RequestBody;
import javax.inject.Inject;
import com.gingersoft.gsa.cloud.main.mvp.contract.MainTopContract;
import com.jess.arms.utils.RxLifecycleUtils;
/**
* ================================================
* Description:
* <p>
* Created by MVPArmsTemplate on 02/18/2020 17:21
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* <a href="https://github.com/JessYanCoding/MVPArms">Star me</a>
* <a href="https://github.com/JessYanCoding/MVPArms/wiki">See me</a>
* <a href="https://github.com/JessYanCoding/MVPArmsTemplate">模版请保持更新</a>
* ================================================
*/
@FragmentScope
public class MainTopPresenter extends BasePresenter<MainTopContract.Model, MainTopContract.View> {
@Inject
RxErrorHandler mErrorHandler;
@Inject
Application mApplication;
@Inject
ImageLoader mImageLoader;
@Inject
AppManager mAppManager;
@Inject
public MainTopPresenter(MainTopContract.Model model, MainTopContract.View rootView) {
super(model, rootView);
}
@Override
public void onDestroy() {
super.onDestroy();
this.mErrorHandler = null;
this.mAppManager = null;
this.mImageLoader = null;
this.mApplication = null;
}
public void getRestaurantReport(String restaurantId) {
RequestBody requestBody = new FormBody.Builder()
.add("restaurantId", restaurantId)
.add("startTime", TimeUtils.getYesterdayTime(TimeUtils.DATE_FORMAT_DATE))
.add("endTime", TimeUtils.getCurrentTimeInString(TimeUtils.DATE_FORMAT_DATE))
.build();
mModel.getRestaurantReport(requestBody)
.subscribeOn(Schedulers.io())
.doOnSubscribe(disposable -> mRootView.showLoading(""))
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
.doAfterTerminate(() -> mRootView.hideLoading())
.compose(RxLifecycleUtils.bindToLifecycle(mRootView))
.subscribe(new ErrorHandleSubscriber<HomeTurnoverBean>(mErrorHandler) {
@Override
public void onNext(@NonNull HomeTurnoverBean info) {
if (info != null && info.isSuccess()) {
if (info.getData() != null) {
mRootView.loadBusinessInfo(info.getData());
}
} else {
mRootView.showMessage("營業報表獲取失敗");
}
}
@Override
public void onComplete() {
super.onComplete();
RetrofitUrlManager.getInstance().putDomain("common", HttpsConstans.ROOT_SERVER_ADDRESS_FORMAL);
}
@Override
public void onError(Throwable t) {
super.onError(t);
RetrofitUrlManager.getInstance().putDomain("common", HttpsConstans.ROOT_SERVER_ADDRESS_FORMAL);
}
});
}
}
package com.gingersoft.gsa.cloud.main.mvp.presenter;
import android.app.Application;
import com.jess.arms.integration.AppManager;
import com.jess.arms.di.scope.ActivityScope;
import com.jess.arms.mvp.BasePresenter;
import com.jess.arms.http.imageloader.ImageLoader;
import me.jessyan.rxerrorhandler.core.RxErrorHandler;
import javax.inject.Inject;
import com.gingersoft.gsa.cloud.main.mvp.contract.NewMainContract;
/**
* ================================================
* Description:
* <p>
* Created by MVPArmsTemplate on 02/18/2020 17:14
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* <a href="https://github.com/JessYanCoding/MVPArms">Star me</a>
* <a href="https://github.com/JessYanCoding/MVPArms/wiki">See me</a>
* <a href="https://github.com/JessYanCoding/MVPArmsTemplate">模版请保持更新</a>
* ================================================
*/
@ActivityScope
public class NewMainPresenter extends BasePresenter<NewMainContract.Model, NewMainContract.View> {
@Inject
RxErrorHandler mErrorHandler;
@Inject
Application mApplication;
@Inject
ImageLoader mImageLoader;
@Inject
AppManager mAppManager;
@Inject
public NewMainPresenter(NewMainContract.Model model, NewMainContract.View rootView) {
super(model, rootView);
}
@Override
public void onDestroy() {
super.onDestroy();
this.mErrorHandler = null;
this.mAppManager = null;
this.mImageLoader = null;
this.mApplication = null;
}
}
......@@ -73,8 +73,6 @@ public class MainActivity extends BaseFragmentActivity<MainPresenter> implements
public void initData(@Nullable Bundle savedInstanceState) {
initTabs();
initPagers();
// mPresenter.initAdapter(this);
// mPresenter.initItemClickListener(this);
}
private void initPagers() {
......
package com.gingersoft.gsa.cloud.main.mvp.ui.activity;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.billy.cc.core.component.CC;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.gingersoft.gsa.cloud.main.R2;
import com.gingersoft.gsa.cloud.main.di.component.DaggerNewMainComponent;
import com.gingersoft.gsa.cloud.main.mvp.model.bean.SectionHeader;
import com.gingersoft.gsa.cloud.main.mvp.model.bean.SectionItem;
import com.gingersoft.gsa.cloud.main.mvp.ui.adapter.HomeFunctionAdapter;
import com.gingersoft.gsa.cloud.main.mvp.ui.adapter.MainOrderingAdapter;
import com.jess.arms.base.BaseActivity;
import com.jess.arms.di.component.AppComponent;
import com.jess.arms.utils.ArmsUtils;
import com.gingersoft.gsa.cloud.main.mvp.contract.NewMainContract;
import com.gingersoft.gsa.cloud.main.mvp.presenter.NewMainPresenter;
import com.gingersoft.gsa.cloud.main.R;
import com.qmuiteam.qmui.widget.section.QMUISection;
import com.sxu.shadowdrawable.ShadowDrawable;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import static com.jess.arms.utils.Preconditions.checkNotNull;
import static com.qmuiteam.qmui.util.QMUIDisplayHelper.dpToPx;
/**
* ================================================
* Description:
* <p>
* Created by MVPArmsTemplate on 02/18/2020 17:14
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* <a href="https://github.com/JessYanCoding/MVPArms">Star me</a>
* <a href="https://github.com/JessYanCoding/MVPArms/wiki">See me</a>
* <a href="https://github.com/JessYanCoding/MVPArmsTemplate">模版请保持更新</a>
* ================================================
*/
public class NewMainActivity extends BaseActivity<NewMainPresenter> implements NewMainContract.View {
@BindView(R2.id.rv_ordering_meals)
RecyclerView rvOrdering;//點餐
@BindView(R2.id.rv_management)
RecyclerView rvManagement;//管理
@BindView(R2.id.rv_staff_management)
RecyclerView rvStaffManagement;//管理
@Override
public void setupActivityComponent(@NonNull AppComponent appComponent) {
DaggerNewMainComponent //如找不到该类,请编译一下项目
.builder()
.appComponent(appComponent)
.view(this)
.build()
.inject(this);
}
@Override
public int initView(@Nullable Bundle savedInstanceState) {
return R.layout.activity_new_main; //如果你不需要框架帮你设置 setContentView(id) 需要自行设置,请返回 0
}
@Override
public void initData(@Nullable Bundle savedInstanceState) {
ShadowDrawable.setShadowDrawable(findViewById(R.id.layout_ordering_meals), Color.parseColor("#FFFFFF"), dpToPx(5),
Color.parseColor("#73888888"), dpToPx(5), 0, 2);
ShadowDrawable.setShadowDrawable(findViewById(R.id.layout_management), Color.parseColor("#FFFFFF"), dpToPx(5),
Color.parseColor("#00000000"), dpToPx(5), 0, 2);
List<SectionItem> data = new ArrayList<>();
data.add(new SectionItem(R.drawable.ic_dining_table_mode, "餐檯模式"));
data.add(new SectionItem(R.drawable.ic_delivery_mode, "外送模式"));
data.add(new SectionItem(R.drawable.ic_outsourcing_model, "外賣模式"));
data.add(new SectionItem(R.drawable.ic_pre_order_mode, "預點餐模式"));
MainOrderingAdapter adapter = new MainOrderingAdapter(R.layout.main_home_funcation_item, data);
rvOrdering.setLayoutManager(new GridLayoutManager(mContext, 4) {
@Override
public boolean canScrollVertically() {
return false;
}
});
adapter.setOnItemClickListener((adapter1, view, position) -> {
if(data.get(position).getText().equals("餐檯模式")){
CC.obtainBuilder("Component.Table")
.setActionName("showTableActivity")
.build()
.call();
}
});
rvOrdering.setAdapter(adapter);
List<SectionItem> managementData = new ArrayList<>();
managementData.add(new SectionItem(R.drawable.ic_meals_menu_management, "餐牌管理"));
managementData.add(new SectionItem(R.drawable.ic_dining_table_management, "餐檯管理"));
managementData.add(new SectionItem(R.drawable.ic_print_management, "打印管理"));
managementData.add(new SectionItem(R.drawable.ic_pay_management, "支付管理"));
managementData.add(new SectionItem(R.drawable.ic_discount_management, "折扣管理"));
MainOrderingAdapter managementAdapter = new MainOrderingAdapter(R.layout.main_home_management_funcation_item, managementData);
rvManagement.setLayoutManager(new GridLayoutManager(mContext, 5) {
@Override
public boolean canScrollVertically() {
return false;
}
});
rvManagement.setAdapter(managementAdapter);
managementAdapter.setOnItemClickListener((adapter12, view, position) -> {
if(managementData.get(position).getText().equals("打印管理")){
CC.obtainBuilder("Component.Print")
.setActionName("showPrintActivity")
.build()
.call();
}
});
List<SectionItem> staffData = new ArrayList<>();
staffData.add(new SectionItem(R.drawable.ic_staff_management, "員工管理"));
staffData.add(new SectionItem(R.drawable.ic_authority_management, "權限管理"));
staffData.add(new SectionItem(R.drawable.ic_operation_record, "操作記錄"));
MainOrderingAdapter staffAdapter = new MainOrderingAdapter(R.layout.main_home_management_funcation_item, staffData);
rvStaffManagement.setLayoutManager(new GridLayoutManager(mContext, 5) {
@Override
public boolean canScrollVertically() {
return false;
}
});
rvStaffManagement.setAdapter(staffAdapter);
}
@Override
public void initIntent() {
}
@Override
public void initTopBar() {
}
@Override
public void initLanguage() {
}
@Override
public void initLayoutParams() {
}
@Override
public void initLayoutVisible() {
}
@Override
public void showLoading(String message) {
}
@Override
public void hideLoading() {
}
@Override
public void showMessage(@NonNull String message) {
checkNotNull(message);
ArmsUtils.snackbarText(message);
}
@Override
public void launchActivity(@NonNull Intent intent) {
checkNotNull(intent);
ArmsUtils.startActivity(intent);
}
@Override
public void killMyself() {
finish();
}
}
package com.gingersoft.gsa.cloud.main.mvp.ui.adapter;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import com.gingersoft.gsa.cloud.main.R;
import com.gingersoft.gsa.cloud.main.mvp.model.bean.SectionItem;
import java.util.List;
import androidx.annotation.Nullable;
/**
* Created by Wyh on 2020/2/19.
* 首頁點餐
*/
public class MainOrderingAdapter extends BaseQuickAdapter<SectionItem, BaseViewHolder> {
public MainOrderingAdapter(int layoutResId, @Nullable List<SectionItem> data) {
super(layoutResId, data);
}
// public MainOrderingAdapter(@Nullable List<SectionItem> data) {
// super(R.layout.main_home_funcation_item, data);
// }
@Override
protected void convert(BaseViewHolder helper, SectionItem item) {
helper.setImageResource(R.id.iv_main_home_item_function_icon, item.getImage());
helper.setText(R.id.tv_main_home_item_function_name, item.getText());
}
}
package com.gingersoft.gsa.cloud.main.mvp.ui.fragment;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.daivd.chart.component.axis.BaseAxis;
import com.daivd.chart.component.axis.HorizontalAxis;
import com.daivd.chart.component.axis.VerticalAxis;
import com.daivd.chart.component.base.IAxis;
import com.daivd.chart.core.LineChart;
import com.daivd.chart.data.ChartData;
import com.daivd.chart.data.LineData;
import com.daivd.chart.data.style.FontStyle;
import com.daivd.chart.data.style.LineStyle;
import com.daivd.chart.data.style.PointStyle;
import com.daivd.chart.listener.OnClickColumnListener;
import com.daivd.chart.provider.component.point.Point;
import com.daivd.chart.provider.component.tip.MultiLineBubbleTip;
import com.daivd.chart.utils.DensityUtils;
import com.gingersoft.gsa.cloud.base.application.GsaCloudApplication;
import com.gingersoft.gsa.cloud.base.utils.time.TimeUtils;
import com.gingersoft.gsa.cloud.main.R2;
import com.gingersoft.gsa.cloud.main.mvp.model.bean.HomeTurnoverBean;
import com.gingersoft.gsa.cloud.main.mvp.ui.activity.MainActivity;
import com.jess.arms.base.BaseFragment;
import com.jess.arms.di.component.AppComponent;
import com.jess.arms.utils.ArmsUtils;
import com.gingersoft.gsa.cloud.main.di.component.DaggerMainTopComponent;
import com.gingersoft.gsa.cloud.main.mvp.contract.MainTopContract;
import com.gingersoft.gsa.cloud.main.mvp.presenter.MainTopPresenter;
import com.gingersoft.gsa.cloud.main.R;
import com.sxu.shadowdrawable.ShadowDrawable;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import butterknife.BindView;
import butterknife.ButterKnife;
import static com.jess.arms.utils.Preconditions.checkNotNull;
import static com.qmuiteam.qmui.util.QMUIDisplayHelper.dpToPx;
/**
* ================================================
* Description:
* <p>
* Created by MVPArmsTemplate on 02/18/2020 17:21
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* <a href="https://github.com/JessYanCoding/MVPArms">Star me</a>
* <a href="https://github.com/JessYanCoding/MVPArms/wiki">See me</a>
* <a href="https://github.com/JessYanCoding/MVPArmsTemplate">模版请保持更新</a>
* ================================================
*/
public class MainTopFragment extends BaseFragment<MainTopPresenter> implements MainTopContract.View {
// line_chart_view
@BindView(R2.id.line_chart_view)
LineChart lineChart;
@BindView(R2.id.tv_today_amount)
TextView tvTurnover;
@BindView(R2.id.tv_today_time)
TextView time;
@BindView(R2.id.tv_total_amount_project)
TextView tvProjectAmount;//項目總金額
@BindView(R2.id.tv_consumers_number)
TextView tvConsumersNumber;//消費人數
@BindView(R2.id.tv_bill_number)
TextView tvBillNumber;//賬單數
public static MainTopFragment newInstance() {
MainTopFragment fragment = new MainTopFragment();
return fragment;
}
@Override
public void setupFragmentComponent(@NonNull AppComponent appComponent) {
DaggerMainTopComponent //如找不到该类,请编译一下项目
.builder()
.appComponent(appComponent)
.view(this)
.build()
.inject(this);
}
@Override
public View initView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main_top, container, false);
ButterKnife.bind(this, view);
ShadowDrawable.setShadowDrawable(view.findViewById(R.id.cv_white_bg), Color.parseColor("#FFFFFF"), dpToPx(5),
Color.parseColor("#73888888"), dpToPx(5), 0, 2);
ShadowDrawable.setShadowDrawable(view.findViewById(R.id.layout_info), Color.parseColor("#FFFFFF"), dpToPx(5),
Color.parseColor("#73888888"), dpToPx(5), 0, 2);
return view;
}
@Override
public void initData(@Nullable Bundle savedInstanceState) {
mPresenter.getRestaurantReport( "26");//GsaCloudApplication.getRestaurantId(mContext) 獲取餐廳營業信息
time.setText(TimeUtils.getCurrentTimeInString(new SimpleDateFormat("YYYY-MM-dd E")));
loadReport();//加載報表線形圖
}
private void loadReport() {
Resources res = getResources();
FontStyle.setDefaultTextSpSize(mContext, 12);
List<String> chartYDataList = new ArrayList<>();
chartYDataList.add("00:00");
chartYDataList.add("04:00");
chartYDataList.add("08:00");
chartYDataList.add("12:00");
chartYDataList.add("16:00");
chartYDataList.add("20:00");
chartYDataList.add("24:00");
List<LineData> ColumnDatas = new ArrayList<>();
//第一條線
ArrayList<Double> tempList1 = new ArrayList<>();
tempList1.add(12350d);
tempList1.add(12340d);
tempList1.add(2450d);
tempList1.add(1470d);
tempList1.add(1690d);
tempList1.add(1570d);
final LineData columnData1 = new LineData("今日", "℃", getResources().getColor(R.color.theme_color), tempList1);
columnData1.setLineStyle(new LineStyle(mContext, 5, getResources().getColor(R.color.theme_color)));
//第二條線
ArrayList<Double> humidityList = new ArrayList<>();
humidityList.add(1340d);
humidityList.add(1320d);
humidityList.add(480d);
humidityList.add(590d);
humidityList.add(360d);
humidityList.add(1830d);
humidityList.add(1980d);
humidityList.add(1490d);
LineData columnData2 = new LineData("昨日", "", getResources().getColor(R.color.today_line), humidityList);
columnData2.setLineStyle(new LineStyle(mContext, 5, getResources().getColor(R.color.today_line)));
ColumnDatas.add(columnData1);
ColumnDatas.add(columnData2);
ChartData<LineData> chartData2 = new ChartData<>("Line chart", chartYDataList, ColumnDatas);
//設置曲線圖樣式
lineChart.setLineModel(LineChart.CURVE_MODEL);
//獲取左側垂直軸線
VerticalAxis verticalAxis = lineChart.getLeftVerticalAxis();
//獲取橫向軸線
HorizontalAxis horizontalAxis = lineChart.getHorizontalAxis();
//右側軸線
VerticalAxis rightAxis = lineChart.getRightVerticalAxis();
//設置右側軸線不顯示
rightAxis.setDisplay(false);
//设置竖轴方向在左側
verticalAxis.setAxisDirection(IAxis.AxisDirection.LEFT);
verticalAxis.setStartZero(true);
verticalAxis.setMaxValue(18000);
//设置网格
verticalAxis.setDrawGrid(true);
//设置横轴方向
horizontalAxis.setAxisDirection(IAxis.AxisDirection.BOTTOM);
//不顯示網格
horizontalAxis.setDrawGrid(false);
horizontalAxis.getAxisStyle().setWidth(mContext,1).setColor(res.getColor(R.color.theme_color));
//设置线条样式
verticalAxis.getAxisStyle().setWidth(mContext, 1).setColor(res.getColor(R.color.theme_color));
//設置網格虛線樣式
DashPathEffect effects = new DashPathEffect(new float[]{1, 2, 4, 8}, 1);
verticalAxis.setGridStyle(new LineStyle(0.5f, res.getColor(R.color.report_dotted_line_color)));
verticalAxis.getGridStyle().setEffect(effects);
FontStyle scaleStyle = new FontStyle(mContext, 9, res.getColor(R.color.report_left_scale));
horizontalAxis.setScaleStyle(scaleStyle);
verticalAxis.setScaleStyle(scaleStyle);
lineChart.setZoom(false);
//开启十字架
lineChart.getProvider().setOpenCross(true);
//设置显示点
Point point = new Point();
point.getPointStyle().setShape(PointStyle.CIRCLE);
//设置显示点的样式
lineChart.getProvider().setPoint(point);
//设置显示标题
lineChart.setShowChartName(false);
//底部不顯示顏色代表的意思
lineChart.getLegend().setDisplay(false);
lineChart.getMatrixHelper().setWidthMultiple(0.9f);
lineChart.getHorizontalAxis().setRotateAngle(0);//設置底部文字旋轉角度
lineChart.setFirstAnim(false);
lineChart.setChartData(chartData2);
lineChart.setOnClickColumnListener((lineData, pos) -> {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setTextSize(DensityUtils.sp2px(mContext, 13));
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.WHITE);
MultiLineBubbleTip tip = new MultiLineBubbleTip<LineData>(mContext,
R.mipmap.round_rect, R.mipmap.triangle, paint) {
@Override
public boolean isShowTip(LineData lineData, int position) {
return position == pos;
}
@Override
public String[] format(LineData lineData, int position) {
String title = lineData.getName();
String value = lineData.getChartYDataList().get(position) + lineData.getUnit();
return new String[]{title, value};
}
};
tip.setColorFilter(Color.parseColor("#FA8072"));
tip.setAlpha(0.8f);
lineChart.getProvider().setTip(tip);
lineChart.invalidate();
});
}
/**
* 通过此方法可以使 Fragment 能够与外界做一些交互和通信, 比如说外部的 Activity 想让自己持有的某个 Fragment 对象执行一些方法,
* 建议在有多个需要与外界交互的方法时, 统一传 {@link Message}, 通过 what 字段来区分不同的方法, 在 {@link #setData(Object)}
* 方法中就可以 {@code switch} 做不同的操作, 这样就可以用统一的入口方法做多个不同的操作, 可以起到分发的作用
* <p>
* 调用此方法时请注意调用时 Fragment 的生命周期, 如果调用 {@link #setData(Object)} 方法时 {Fragment#onCreate(Bundle)} 还没执行
* 但在 {@link #setData(Object)} 里却调用了 Presenter 的方法, 是会报空的, 因为 Dagger 注入是在 {Fragment#onCreate(Bundle)} 方法中执行的
* 然后才创建的 Presenter, 如果要做一些初始化操作,可以不必让外部调用 {@link #setData(Object)}, 在 {@link #initData(Bundle)} 中初始化就可以了
* <p>
* Example usage:
* <pre>
* public void setData(@Nullable Object data) {
* if (data != null && data instanceof Message) {
* switch (((Message) data).what) {
* case 0:
* loadData(((Message) data).arg1);
* break;
* case 1:
* refreshUI();
* break;
* default:
* //do something
* break;
* }
* }
* }
*
* // call setData(Object):
* Message data = new Message();
* data.what = 0;
* data.arg1 = 1;
* fragment.setData(data);
* </pre>
*
* @param data 当不需要参数时 {@code data} 可以为 {@code null}
*/
@Override
public void setData(@Nullable Object data) {
}
@Override
public void showLoading(String message) {
}
@Override
public void hideLoading() {
}
@Override
public void showMessage(@NonNull String message) {
checkNotNull(message);
ArmsUtils.snackbarText(message);
}
@Override
public void launchActivity(@NonNull Intent intent) {
checkNotNull(intent);
ArmsUtils.startActivity(intent);
}
@Override
public void killMyself() {
}
@Override
public void loadBusinessInfo(Map<String, HomeTurnoverBean.DataBean> dataBeanMap) {
HomeTurnoverBean.DataBean bean = dataBeanMap.get(TimeUtils.getCurrentTimeInString(TimeUtils.DATE_FORMAT_DATE));//TimeUtils.getCurrentTimeInString(TimeUtils.DATE_FORMAT_DATE)
if (bean != null) {
tvTurnover.setText(bean.getBusiness_amount());
tvProjectAmount.setText(bean.getSales());
tvConsumersNumber.setText(bean.getPeople());
tvBillNumber.setText(bean.getNumber_bill());
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="@dimen/dp_4" />
<solid android:color="@color/white" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:angle="270"
android:endColor="#015EEA"
android:startColor="#00C0FA" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="#F0edf1"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<fragment
android:id="@+id/main_top_fragment"
android:name="com.gingersoft.gsa.cloud.main.mvp.ui.fragment.MainTopFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
android:id="@+id/layout_ordering_meals"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_10"
android:layout_marginRight="@dimen/dp_10"
android:orientation="vertical"
android:paddingTop="@dimen/dp_15"
android:paddingBottom="@dimen/dp_20">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_22"
android:text="點餐"
android:textColor="#181818"
android:textSize="@dimen/sp_17" />
<View
android:layout_width="match_parent"
android:layout_height="@dimen/dp_1"
android:layout_marginTop="@dimen/dp_11"
android:background="#F0F0F0" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_ordering_meals"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/main_recyclerview_marginLeft"
android:layout_marginTop="@dimen/dp_5"
android:layout_marginRight="@dimen/main_recyclerview_marginRight" />
</LinearLayout>
<LinearLayout
android:id="@+id/layout_management"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_10"
android:layout_marginRight="@dimen/dp_10"
android:layout_marginBottom="@dimen/dp_10"
android:background="@color/white"
android:orientation="vertical"
android:paddingTop="@dimen/dp_16"
android:paddingBottom="@dimen/dp_16">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_15"
android:text="管理"
android:textColor="#181818"
android:textSize="@dimen/sp_14" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_management"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/main_recyclerview_marginLeft"
android:layout_marginTop="@dimen/dp_20"
android:layout_marginRight="@dimen/main_recyclerview_marginRight" />
<View
android:layout_width="match_parent"
android:layout_height="@dimen/dp_1"
android:layout_marginTop="@dimen/dp_20"
android:background="#F0F0F0" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_20"
android:text="員工"
android:layout_marginLeft="@dimen/dp_15"
android:textColor="#181818"
android:textSize="@dimen/sp_14" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_staff_management"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/main_recyclerview_marginLeft"
android:layout_marginTop="@dimen/dp_20"
android:layout_marginRight="@dimen/main_recyclerview_marginRight" />
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent">
<View
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="@drawable/shape_main_top_bg"
app:layout_constraintBottom_toBottomOf="@id/tv_today_time"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_title_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/dp_20"
android:paddingTop="@dimen/dp_7"
android:paddingRight="@dimen/dp_12"
android:paddingBottom="@dimen/dp_7"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:layout_width="@dimen/dp_22"
android:layout_height="@dimen/dp_25"
android:src="@drawable/ic_personal_center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="店鋪名稱"
android:textColor="@color/white"
android:textSize="@dimen/sp_18"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="@dimen/dp_26"
android:layout_height="@dimen/dp_28"
android:src="@drawable/ic_msg"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/tv_today_amount_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_10"
android:text="今日營業額($)"
android:textColor="@color/white"
android:textSize="@dimen/sp_12"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/cl_title_layout" />
<TextView
android:id="@+id/tv_today_amount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_10"
android:includeFontPadding="false"
android:text="0"
android:textColor="@color/white"
android:textSize="@dimen/sp_40"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_today_amount_text" />
<TextView
android:id="@+id/tv_compare_today_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_10"
android:text="較昨日:"
android:textColor="@color/white"
android:textSize="@dimen/sp_12"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/tv_compare_today_state"
app:layout_constraintTop_toBottomOf="@id/tv_today_amount" />
<TextView
android:id="@+id/tv_compare_today_state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="↑"
android:textColor="@color/white"
android:textSize="@dimen/sp_12"
app:layout_constraintBottom_toBottomOf="@id/tv_compare_today_text"
app:layout_constraintLeft_toRightOf="@id/tv_compare_today_text"
app:layout_constraintRight_toLeftOf="@id/tv_compare_today_size"
app:layout_constraintTop_toTopOf="@id/tv_compare_today_text" />
<TextView
android:id="@+id/tv_compare_today_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textColor="@color/white"
android:textSize="@dimen/sp_12"
app:layout_constraintBottom_toBottomOf="@id/tv_compare_today_text"
app:layout_constraintLeft_toRightOf="@id/tv_compare_today_state"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/tv_compare_today_text" />
<View
android:id="@+id/cv_white_bg"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_250"
android:layout_marginLeft="@dimen/dp_10"
android:layout_marginTop="@dimen/dp_16"
android:layout_marginRight="@dimen/dp_10"
android:background="@color/white"
app:layout_constraintTop_toBottomOf="@id/tv_compare_today_text" />
<TextView
android:id="@+id/tv_today_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_35"
android:elevation="@dimen/dp_1"
android:paddingBottom="@dimen/dp_6"
android:textColor="#181818"
android:textSize="@dimen/sp_15"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_compare_today_text" />
<com.daivd.chart.core.LineChart
android:id="@+id/line_chart_view"
android:layout_width="0dp"
android:layout_height="160dp"
android:layout_marginLeft="@dimen/dp_10"
android:layout_marginRight="@dimen/dp_10"
android:layout_marginTop="@dimen/dp_10"
android:paddingRight="@dimen/dp_10"
app:layout_constraintLeft_toLeftOf="@id/cv_white_bg"
app:layout_constraintRight_toRightOf="@id/cv_white_bg"
app:layout_constraintTop_toBottomOf="@id/tv_today_time" />
<TextView
android:id="@+id/tv_more_report"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/dp_10"
android:elevation="@dimen/dp_1"
android:text="更多報表 >"
android:textColor="#3c3c3c"
android:textSize="@dimen/sp_12"
app:layout_constraintBottom_toBottomOf="@id/cv_white_bg"
app:layout_constraintLeft_toLeftOf="@id/cv_white_bg"
app:layout_constraintRight_toRightOf="@id/cv_white_bg" />
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:id="@+id/layout_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_10"
android:layout_marginRight="@dimen/dp_10"
android:gravity="center"
android:orientation="horizontal"
android:paddingLeft="@dimen/dp_20"
android:paddingTop="@dimen/dp_15"
android:paddingRight="@dimen/dp_20"
android:paddingBottom="@dimen/dp_15"
app:layout_constraintTop_toBottomOf="@id/cl_top">
<!-- 項目總金額-->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/tv_total_amount_project"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textColor="#3c3c3c"
android:textSize="@dimen/sp_17"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="項目總金額"
android:textColor="#3c3c3c"
android:textSize="@dimen/sp_12" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="较昨日:"
android:textColor="#3c3c3c"
android:textSize="@dimen/sp_12" />
<ImageView
android:layout_width="@dimen/dp_8"
android:layout_height="@dimen/dp_9"
android:src="@drawable/ic_rise" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#3c3c3c"
android:textSize="@dimen/sp_12" />
</LinearLayout>
</LinearLayout>
<!-- 消費人數-->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/tv_consumers_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textColor="#3c3c3c"
android:textSize="@dimen/sp_17"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="消費人數"
android:textColor="#3c3c3c"
android:textSize="@dimen/sp_12" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="平均:1000"
android:textColor="#3c3c3c"
android:textSize="@dimen/sp_12" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="较昨日:"
android:textColor="#3c3c3c"
android:textSize="@dimen/sp_12" />
<ImageView
android:layout_width="@dimen/dp_8"
android:layout_height="@dimen/dp_9"
android:src="@drawable/ic_rise" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1000"
android:textColor="#3c3c3c"
android:textSize="@dimen/sp_12" />
</LinearLayout>
</LinearLayout>
<!-- 賬單單數-->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/tv_bill_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textColor="#3c3c3c"
android:textSize="@dimen/sp_17"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="賬單單數"
android:textColor="#3c3c3c"
android:textSize="@dimen/sp_12" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="平均:1000"
android:textColor="#3c3c3c"
android:textSize="@dimen/sp_12" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="较昨日:"
android:textColor="#3c3c3c"
android:textSize="@dimen/sp_12" />
<ImageView
android:layout_width="@dimen/dp_8"
android:layout_height="@dimen/dp_9"
android:src="@drawable/ic_down" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1000"
android:textColor="#3c3c3c"
android:textSize="@dimen/sp_12" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
......@@ -3,9 +3,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:paddingTop="@dimen/dp_10"
android:paddingBottom="@dimen/dp_10">
android:orientation="vertical">
<ImageView
android:id="@+id/iv_main_home_item_function_icon"
......@@ -19,6 +17,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="餐檯模式"
android:layout_marginTop="@dimen/dp_8"
android:textColor="@color/normal_color"
android:textSize="@dimen/sp_14" />
......
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_main_home_item_function_icon"
android:layout_width="@dimen/dp_30"
android:layout_height="@dimen/dp_30"
android:src="@drawable/ic_dining_table" />
<TextView
android:id="@+id/tv_main_home_item_function_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="餐檯模式"
android:layout_marginTop="@dimen/dp_8"
android:textColor="@color/normal_color"
android:textSize="@dimen/sp_13" />
</LinearLayout>
\ No newline at end of file
<resources>
<dimen name="main_fab_margin">16dp</dimen>
<dimen name="main_recyclerview_marginLeft">@dimen/dp_15</dimen>
<dimen name="main_recyclerview_marginRight">@dimen/dp_15</dimen>
</resources>
......@@ -81,9 +81,11 @@ public class PrintActivity extends BaseActivity<PrintPresenter> implements Print
* 0:上菜紙
* 1:印單
* 2:結賬單
* 3:廚房單
*/
private int type = -1;
private Bitmap bitmap;
@Override
public void setupActivityComponent(@NonNull AppComponent appComponent) {
DaggerPrintComponent //如找不到该类,请编译一下项目
......@@ -102,16 +104,20 @@ public class PrintActivity extends BaseActivity<PrintPresenter> implements Print
@Override
public void initData(@Nullable Bundle savedInstanceState) {
type = CCUtil.getNavigateParam(this, "type", 1001);
if(type == 0){
if (type == 0) {
//上菜紙
List<Food> foods = CCUtil.getNavigateParam(this, "NewFoods", new ArrayList<>());
bitmap = PrintUtils.getPrintBitmap(mContext, foods);
} else if(type == 1){
} else if (type == 1) {
//印單
bitmap = PrintUtils.getPrintBitmap(mContext);
} else if(type == 2){
} else if (type == 2) {
//結賬單
bitmap = PrintUtils.getPrintBillBitmap(mContext);
} else if (type == 3) {
//廚房單
List<Food> foods = CCUtil.getNavigateParam(this, "NewFoods", new ArrayList<>());
bitmap = PrintUtils.getKitChenPrintBitmap(mContext,foods);
} else {
ToastUtils.show(mContext, "打印失敗");
finish();
......
package com.gingersoft.gsa.cloud.base.adapter.print;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import com.gingersoft.gsa.cloud.base.R;
import com.gingersoft.gsa.cloud.database.bean.Food;
import java.util.List;
import androidx.annotation.Nullable;
/**
* Created by Wyh on 2020/2/20.
*/
public class KitChenPrintFoodAdapter extends BaseQuickAdapter<Food, BaseViewHolder> {
public KitChenPrintFoodAdapter(@Nullable List<Food> data) {
super(R.layout.print_kitchen_item_food, data);
}
@Override
protected void convert(BaseViewHolder helper, Food item) {
helper.setText(R.id.tv_food_number, item.getNumber() +"");
helper.setText(R.id.tv_food_name, item.getFoodName());
}
}
......@@ -21,6 +21,8 @@ public class GsaCloudApplication extends BaseApplication {
public static boolean isLogin = false;
public static String userName = "";
@Override
public void onCreate() {
super.onCreate();
......@@ -32,7 +34,7 @@ public class GsaCloudApplication extends BaseApplication {
initGreenDao();
AppCrashHandler.getInstance().init(this,"test");
AppCrashHandler.getInstance().init(this, "test");
}
......@@ -55,7 +57,6 @@ public class GsaCloudApplication extends BaseApplication {
}
//获取登陆token
public static String getLoginToken(Context context) {
return (String) SPUtils.get(context, UserConstans.token, "");
......
......@@ -10,6 +10,8 @@ import com.gingersoft.gsa.cloud.base.R;
import com.gingersoft.gsa.cloud.base.adapter.print.BillAdapter;
import com.gingersoft.gsa.cloud.base.adapter.print.BillTypeAdapter;
import com.gingersoft.gsa.cloud.base.adapter.print.FoodAdapter;
import com.gingersoft.gsa.cloud.base.adapter.print.KitChenPrintFoodAdapter;
import com.gingersoft.gsa.cloud.base.application.GsaCloudApplication;
import com.gingersoft.gsa.cloud.base.common.bean.BillingBean;
import com.gingersoft.gsa.cloud.base.common.bean.TableBean;
import com.gingersoft.gsa.cloud.base.common.bean.mealManage.MyOrderManage;
......@@ -20,7 +22,9 @@ import com.gingersoft.gsa.cloud.base.utils.view.LayoutToBitmapUtils;
import com.gingersoft.gsa.cloud.database.bean.Food;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import androidx.recyclerview.widget.LinearLayoutManager;
......@@ -35,6 +39,7 @@ public class PrintUtils {
public static Bitmap getPrintBitmap(Context context) {
return getPrintBitmap(context, MyOrderManage.getInstance().getOrderFoodList());
}
/**
* 獲取"印單"圖片
*
......@@ -180,4 +185,50 @@ public class PrintUtils {
//580為程序支持的打印紙最大寬度
return ImageUtils.zoomDrawable(LayoutToBitmapUtils.loadBitmapFromView(view), 580);//将view轉bitmap
}
/**
* 獲取"廚房單"圖片
*
* @param context
* @return
*/
public static Bitmap getKitChenPrintBitmap(Context context, List<Food> foodList) {
View view = LinearLayout.inflate(context, R.layout.print_kitchen, null);
TextView tvTableNumber = view.findViewById(R.id.tv_kitchen_print_table_number);
TextView tvOpeningTime = view.findViewById(R.id.tv_opening_time);
TextView tvOrderTime = view.findViewById(R.id.tv_order_time);
TextView tvKitChenLocation = view.findViewById(R.id.tv_kitchen_location);
TextView tvPeople = view.findViewById(R.id.tv_people);
TextView tvOperator = view.findViewById(R.id.tv_operator);
RecyclerView rvFood = view.findViewById(R.id.rv_kitchen_food);
TextView tvTableNumber2 = view.findViewById(R.id.tv_kitchen_print_table_number2);
if(foodList != null && foodList.size() > 0) {
KitChenPrintFoodAdapter foodAdapter = new KitChenPrintFoodAdapter(foodList);
rvFood.setLayoutManager(new LinearLayoutManager(context));
rvFood.setAdapter(foodAdapter);
//廚房位置
tvKitChenLocation.setText(foodList.get(0).getPrintseting());
}
if (OpenTableContract.getDefault().getTableBean() != null) {
// 台號
tvTableNumber.setText(OpenTableContract.getDefault().getTableBean().getTableName() + "");
tvTableNumber2.setText(OpenTableContract.getDefault().getTableBean().getTableName() + "");
//人數
tvPeople.setText(OpenTableContract.getDefault().getTableBean().getPeopleNumber() + "");
//開台時間
Date time = new Date(OpenTableContract.getDefault().getTableBean().getCreateTime());
SimpleDateFormat sdf = new SimpleDateFormat("MM-dd HH:mm");
tvOpeningTime.setText(sdf.format(time));
}
//落單時間
tvOrderTime.setText(TimeUtils.getCurrentTimeInString(TimeUtils.DEFAULT_DATE_MDHM));
//操作人員
tvOperator.setText(GsaCloudApplication.userName);
LayoutToBitmapUtils.layoutView(context, view);//先测量view
//580為程序支持的打印紙最大寬度
return ImageUtils.zoomDrawable(LayoutToBitmapUtils.loadBitmapFromView(view), 580);//将view轉bitmap
}
}
......@@ -16,6 +16,9 @@ public class TimeUtils {
public static final SimpleDateFormat DATE_FORMAT_DATE = new SimpleDateFormat("yyyy-MM-dd");
public static final SimpleDateFormat DATE_FORMAT_DATE1 = new SimpleDateFormat(
"HH:mm");
public static final SimpleDateFormat DEFAULT_DATE_MDHM = new SimpleDateFormat("MM-dd HH:mm");//, Locale.CHINESE
/**
* 时间日期格式化到年月日.
*/
......
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!--廚房單-->
<!-- 台號-->
<TextView
android:id="@+id/tv_kitchen_print_table_number"
style="@style/Print_large_text_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="19" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/dp_10">
<TextView
android:id="@+id/tv_opening_time_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="開檯時間:"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_opening_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="01-04 18:32"
app:layout_constraintLeft_toRightOf="@id/tv_opening_time_text"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_order_time_text"
style="@style/Print_text_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="落單時間:"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_opening_time_text" />
<TextView
android:id="@+id/tv_order_time"
style="@style/Print_text_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="01-04 19:32"
app:layout_constraintLeft_toRightOf="@id/tv_order_time_text"
app:layout_constraintTop_toBottomOf="@id/tv_opening_time_text" />
<TextView
android:id="@+id/tv_kitchen_location"
style="@style/Print_text_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_10"
android:text="K1 廚房"
app:layout_constraintLeft_toRightOf="@id/tv_order_time"
app:layout_constraintTop_toTopOf="@id/tv_order_time" />
<TextView
android:id="@+id/tv_people_text"
style="@style/Print_text_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="人數:"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_order_time_text" />
<TextView
android:id="@+id/tv_people"
style="@style/Print_text_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="10"
app:layout_constraintBottom_toBottomOf="@id/tv_people_text"
app:layout_constraintLeft_toRightOf="@id/tv_people_text"
app:layout_constraintTop_toTopOf="@id/tv_people_text" />
<TextView
android:id="@+id/tv_operator"
style="@style/Print_text_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="gsadmin"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/tv_people" />
<TextView
android:id="@+id/line_kitchen_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="——————————————————————————————————————————————————"
app:layout_constraintTop_toBottomOf="@id/tv_people_text" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_kitchen_food"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_10"
app:layout_constraintTop_toBottomOf="@id/line_kitchen_info" />
<TextView
android:id="@+id/tv_kitchen_print_table_number2"
style="@style/Print_large_text_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="19"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/rv_kitchen_food" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_food_number"
style="@style/Print_large_text_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_10"
android:gravity="center"
android:text="1" />
<TextView
android:id="@+id/tv_food_name"
style="@style/Print_large_text_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_10"
android:text="秘製火鍋雞" />
</LinearLayout>
\ No newline at end of file
......@@ -13,6 +13,11 @@
</style>
<style name="Print_text_style">
<item name="android:textColor">#333</item>
<item name="android:textSize">30sp</item>
<item name="android:textSize">24sp</item>
</style>
<style name="Print_large_text_style">
<item name="android:textColor">#333</item>
<item name="android:textSize">35sp</item>
</style>
</resources>
......@@ -6,6 +6,8 @@ import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Transient;
import org.jetbrains.annotations.NotNull;
import java.io.Serializable;
/**
* 作者:ELEGANT_BIN
* 版本:1.6.0
......@@ -15,8 +17,9 @@ import org.jetbrains.annotations.NotNull;
*/
@Entity
public class Food {
public class Food implements Serializable {
private static final long serialVersionUID = -3341972316262518814L;
@Id(autoincrement = true)
private Long id;
private long orderId;
......@@ -31,6 +34,8 @@ public class Food {
private int isSold;
private String plu;
private double lunchboxPrice;
private String printseting;//打印設定,用逗號分隔,*=切紙
/**
* 自定義字段
*/
......@@ -74,9 +79,9 @@ public class Food {
this.number = 1;
}
@Generated(hash = 55791116)
@Generated(hash = 449660732)
public Food(Long id, long orderId, int parentId, String foodName, int restaurantId, double price, int totalSold,
int isSold, String plu, double lunchboxPrice) {
int isSold, String plu, double lunchboxPrice, String printseting) {
this.id = id;
this.orderId = orderId;
this.parentId = parentId;
......@@ -87,6 +92,7 @@ public class Food {
this.isSold = isSold;
this.plu = plu;
this.lunchboxPrice = lunchboxPrice;
this.printseting = printseting;
}
public double getLunchboxPrice() {
......@@ -117,6 +123,14 @@ public class Food {
return number;
}
public String getPrintseting() {
return printseting;
}
public void setPrintseting(String printseting) {
this.printseting = printseting;
}
public void setNumber(int number) {
this.number = number;
}
......
......@@ -34,6 +34,7 @@ public class FoodDao extends AbstractDao<Food, Long> {
public final static Property IsSold = new Property(7, int.class, "isSold", false, "IS_SOLD");
public final static Property Plu = new Property(8, String.class, "plu", false, "PLU");
public final static Property LunchboxPrice = new Property(9, double.class, "lunchboxPrice", false, "LUNCHBOX_PRICE");
public final static Property Printseting = new Property(10, String.class, "printseting", false, "PRINTSETING");
}
......@@ -58,7 +59,8 @@ public class FoodDao extends AbstractDao<Food, Long> {
"\"TOTAL_SOLD\" INTEGER NOT NULL ," + // 6: totalSold
"\"IS_SOLD\" INTEGER NOT NULL ," + // 7: isSold
"\"PLU\" TEXT," + // 8: plu
"\"LUNCHBOX_PRICE\" REAL NOT NULL );"); // 9: lunchboxPrice
"\"LUNCHBOX_PRICE\" REAL NOT NULL ," + // 9: lunchboxPrice
"\"PRINTSETING\" TEXT);"); // 10: printseting
}
/** Drops the underlying database table. */
......@@ -92,6 +94,11 @@ public class FoodDao extends AbstractDao<Food, Long> {
stmt.bindString(9, plu);
}
stmt.bindDouble(10, entity.getLunchboxPrice());
String printseting = entity.getPrintseting();
if (printseting != null) {
stmt.bindString(11, printseting);
}
}
@Override
......@@ -119,6 +126,11 @@ public class FoodDao extends AbstractDao<Food, Long> {
stmt.bindString(9, plu);
}
stmt.bindDouble(10, entity.getLunchboxPrice());
String printseting = entity.getPrintseting();
if (printseting != null) {
stmt.bindString(11, printseting);
}
}
@Override
......@@ -138,7 +150,8 @@ public class FoodDao extends AbstractDao<Food, Long> {
cursor.getInt(offset + 6), // totalSold
cursor.getInt(offset + 7), // isSold
cursor.isNull(offset + 8) ? null : cursor.getString(offset + 8), // plu
cursor.getDouble(offset + 9) // lunchboxPrice
cursor.getDouble(offset + 9), // lunchboxPrice
cursor.isNull(offset + 10) ? null : cursor.getString(offset + 10) // printseting
);
return entity;
}
......@@ -155,6 +168,7 @@ public class FoodDao extends AbstractDao<Food, Long> {
entity.setIsSold(cursor.getInt(offset + 7));
entity.setPlu(cursor.isNull(offset + 8) ? null : cursor.getString(offset + 8));
entity.setLunchboxPrice(cursor.getDouble(offset + 9));
entity.setPrintseting(cursor.isNull(offset + 10) ? null : cursor.getString(offset + 10));
}
@Override
......
......@@ -37,5 +37,5 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.github.hackware1993:MagicIndicator:1.5.0'
implementation 'io.github.meetsl:SCardView:1.0'
}
......@@ -11,6 +11,12 @@
<!-- <color name="theme_color">#BF1C42</color>-->
<color name="theme_color">#0099CC</color>
<color name="normal_color">#333333</color>
<!-- 首页报表“昨日”线条颜色-->
<color name="today_line">#FFCC8F</color>
<!-- 報表虛線顏色-->
<color name="report_dotted_line_color">#8CA0B3</color>
<!-- 報表左側刻度文字顏色-->
<color name="report_left_scale">#777</color>
<color name="theme_white_color">#FFFFFFFF</color>
<color name="theme_333_color">#333</color>
......
......@@ -15,5 +15,6 @@ include 'cc-register',
'table-mode',
'demo_interceptors',
'print-module',
'chart',
rootProject.name = 'GSA-Cloud' , 'pools'
......@@ -257,12 +257,14 @@ public class MealStandPresenter extends BasePresenter<MealStandContract.Model, M
/**
* 打印上菜紙
* 打印廚房單
*/
private void printSendOrder(List<Food> newFoods) {
//打印廚房單
CC.obtainBuilder("Component.Print")
.setActionName("printActivity")
.addParam("NewFoods", newFoods)
.addParam("type", 0)
.addParam("type", 3)
.build()
.callAsync((cc, result) -> {
if (result.isSuccess()) {
......@@ -270,6 +272,20 @@ public class MealStandPresenter extends BasePresenter<MealStandContract.Model, M
mRootView.killMyself();
}
});
//打印上菜紙
// CC.obtainBuilder("Component.Print")
// .setActionName("printActivity")
// .addParam("NewFoods", newFoods)
// .addParam("type", 0)
// .build()
// .callAsync((cc, result) -> {
// if (result.isSuccess()) {
// //打印成功
// mRootView.killMyself();
// }
// });
}
public void deleteFood(long id) {
......
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.gingersoft.gsa.cloud.user.login">
<uses-permission android:name="android.permission.RECEIVE_USER_PRESENT" />
......@@ -15,6 +16,7 @@
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/user_login_name"
tools:replace="android:label"
android:networkSecurityConfig="@xml/network_android"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
......
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.gingersoft.gsa.cloud.user.login">
<uses-permission android:name="android.permission.RECEIVE_USER_PRESENT" />
......@@ -14,6 +15,7 @@
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/user_login_name"
tools:replace="android:label"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
......
......@@ -92,9 +92,9 @@ public class LoginPresenter extends BasePresenter<LoginContract.Model, LoginCont
@Override
public void onNext(@NonNull LoginBean info) {
Log.e("login", "" + info);
if (info != null && info.isSuccess()) {
GsaCloudApplication.isLogin = true;
GsaCloudApplication.userName = info.getData().getUser().getUserName();
mRootView.showMessage("登陸成功");
mRootView.loginSuccess(info);
} else {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment