的
This commit is contained in:
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -0,0 +1,59 @@
|
||||
apply plugin: 'com.android.library'
|
||||
//apply plugin: 'com.novoda.bintray-release'
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
versionCode 2
|
||||
versionName "0.0.2"
|
||||
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
|
||||
}
|
||||
beta {
|
||||
|
||||
}
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
|
||||
}
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.android.support:appcompat-v7:28.0.0'
|
||||
api 'com.google.code.gson:gson:2.8.5'
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ./gradlew clean build bintrayUpload
|
||||
// -PbintrayUser=wgyscsf
|
||||
// -PbintrayKey=xxxxxxxxxxxxxxxxxxxxxx
|
||||
// -PdryRun=false
|
||||
|
||||
//添加
|
||||
//publish {
|
||||
// repoName ='maven'//和在Bintray网站创建的仓库名对应
|
||||
// userOrg = 'wgyscsf'//bintray.com用户名
|
||||
// groupId = 'com.wgyscsf'//jcenter上的路径
|
||||
// artifactId = 'financialLib'//项目名称
|
||||
// publishVersion = '0.0.2'//版本号
|
||||
// desc = 'this is the first version'//描述,不重要
|
||||
// website = 'https://github.com/scsfwgy/FinancialCustomerView'//网站,不重要;尽量模拟github上的地址,例如我这样的;当然你有地址最好了
|
||||
//}
|
||||
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -0,0 +1 @@
|
||||
<manifest package="com.tophold.trade"/>
|
||||
@@ -0,0 +1,3 @@
|
||||
#### TODO
|
||||
1. 对于默认值不显示的指标,比如ma,假如算出来的本身就和默认值一样就会导致"误判".
|
||||
2. 首页的ma边界判断应该有问题,参考vol ma
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.tophold.trade;
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
* 作 者 : wgyscsf@163.com
|
||||
* 更新时间 :2018/05/19 16:28
|
||||
* 描 述 :
|
||||
* ============================================================
|
||||
*/
|
||||
public class Constant {
|
||||
public static final String ESPECIAL_TAG="ESPECIAL_TAG";
|
||||
}
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
package com.tophold.trade.utils;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Locale;
|
||||
|
||||
|
||||
public class FormatUtil {
|
||||
|
||||
/**
|
||||
* 浮点数格式化
|
||||
*
|
||||
* @param isPercentage 是否为分数
|
||||
* @param needSign 是否需要正号
|
||||
* @param isMoney 是否使用金钱格式(每3位用","分隔)
|
||||
* @param digit 需要保留的位数
|
||||
* @param num 需要格式化的值(int、float、double、String、byte均可)
|
||||
*/
|
||||
public static String format(boolean isPercentage, boolean needSign, boolean isMoney, int digit, Object num) {
|
||||
digit = digit < 0 ? 0 : digit;
|
||||
if (num == null) num = 0;
|
||||
|
||||
double converted;//需求大多四舍五入 float保留位数会舍去后面的
|
||||
try {
|
||||
converted = Double.parseDouble(num.toString());
|
||||
} catch (Throwable e) {
|
||||
Log.e("FormatErr", "Input number is not kind number");
|
||||
converted = 0d;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder("%");
|
||||
|
||||
if (needSign && (converted > 0)) {
|
||||
sb.append("+");
|
||||
}
|
||||
|
||||
if (isMoney) {
|
||||
sb.append(",");
|
||||
}
|
||||
|
||||
sb.append(".").append(digit).append("f");
|
||||
|
||||
if (isPercentage) {
|
||||
sb.append("%%");
|
||||
}
|
||||
|
||||
return String.format(Locale.getDefault(), sb.toString(), converted);
|
||||
}
|
||||
|
||||
public static String numFormat(Object num, int digit) {
|
||||
return numFormat(false, digit, num);
|
||||
}
|
||||
|
||||
public static String numFormat(boolean needSign, int digit, Object num) {
|
||||
return format(false, needSign, false, digit, num);
|
||||
}
|
||||
|
||||
/**
|
||||
* 带%格式化
|
||||
*/
|
||||
public static String percentageFormat(Object num) {
|
||||
return percentageFormat(true, num);
|
||||
}
|
||||
|
||||
public static String percentageFormat(boolean needSign, Object num) {
|
||||
return percentageFormat(needSign, 2, num);
|
||||
}
|
||||
|
||||
public static String percentageFormat(int digit, Object num) {
|
||||
return percentageFormat(false, digit, num);
|
||||
}
|
||||
|
||||
public static String percentageFormat(boolean needSign, int digit, Object num) {
|
||||
return format(true, needSign, false, digit, num);
|
||||
}
|
||||
|
||||
/**
|
||||
* 以金钱格式表示的数字
|
||||
*/
|
||||
public static String moneyFormat(Object num) {
|
||||
return moneyFormat(true, num);
|
||||
}
|
||||
|
||||
public static String moneyFormat(boolean isPercentage, Object num) {
|
||||
return moneyFormat(isPercentage, 2, num);
|
||||
}
|
||||
|
||||
public static String moneyFormat(boolean isPercentage, int digit, Object num) {
|
||||
return format(isPercentage, false, true, digit, num);
|
||||
}
|
||||
|
||||
public static String stringAppend(Object object) {
|
||||
if (object != null)
|
||||
return String.format(Locale.getDefault(), "%s", object);
|
||||
else
|
||||
return "- -";
|
||||
}
|
||||
|
||||
/**
|
||||
* 不四舍五入取n位小数
|
||||
*/
|
||||
public static String formatBySubString(Object obj, int digit) {
|
||||
if (obj == null) return "0";
|
||||
String num = String.valueOf(obj);
|
||||
digit = digit < 0 ? 0 : digit;
|
||||
int i = num.indexOf(".");
|
||||
if (i >= 0) {
|
||||
if (num.length() - ++i > digit) {
|
||||
num = num.substring(0, i + digit);
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
* 进位处理
|
||||
*
|
||||
* @param scale 保留几位
|
||||
*/
|
||||
public static String roundUp(int scale, Object num) {
|
||||
BigDecimal decimal = new BigDecimal(num.toString());
|
||||
return decimal.setScale(scale, BigDecimal.ROUND_UP).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接舍弃多余小数
|
||||
*/
|
||||
public static String roundDown(int scale, Object num) {
|
||||
BigDecimal decimal = new BigDecimal(num.toString());
|
||||
return decimal.setScale(scale, BigDecimal.ROUND_DOWN).toString();
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
double num1 = -123456.789000;
|
||||
double num2 = 0;
|
||||
double num3 = 1.00;
|
||||
double num4 = 1444444444;
|
||||
double num5 = 123.123;
|
||||
double num6 = 123.123;
|
||||
double num7 = 123.123;
|
||||
double num8 = 123.123;
|
||||
double num9 = 123.123;
|
||||
double num10 = 123.123;
|
||||
String numStr1 = format(false, false, false, 5, num1);
|
||||
String numStr2 = format(true, false, false, 5, num1);
|
||||
String numStr3 = format(false, true, false, 5, num1);
|
||||
String numStr4 = format(false, false, true, 5, num1);
|
||||
System.out.println(numStr1);
|
||||
System.out.println(numStr2);
|
||||
System.out.println(numStr3);
|
||||
System.out.println(numStr4);
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package com.tophold.trade.utils;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>GSON工具类</p>
|
||||
*
|
||||
* @author
|
||||
* @version $Id: GsonUtil.java
|
||||
*/
|
||||
public class GsonUtil {
|
||||
|
||||
private static Gson gson = null;
|
||||
private static Gson prettyGson = null;
|
||||
|
||||
static {
|
||||
gson = new GsonBuilder()
|
||||
.setDateFormat("yyyy-MM-dd HH:mm:ss").create();
|
||||
prettyGson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss")
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* 小写下划线的格式解析JSON字符串到对象
|
||||
* <p>例如 is_success->isSuccess</p>
|
||||
*
|
||||
* @param json
|
||||
* @param classOfT
|
||||
* @return
|
||||
*/
|
||||
public static <T> T fromJsonUnderScoreStyle(String json, Class<T> classOfT) {
|
||||
return gson.fromJson(json, classOfT);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON字符串转为Map<String,String>
|
||||
*
|
||||
* @param json
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
public static <T> T fronJson2Map(String json) {
|
||||
return gson.fromJson(json, new TypeToken<Map<String, String>>() {
|
||||
}.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 小写下划线的格式将对象转换成JSON字符串
|
||||
*
|
||||
* @param src
|
||||
* @return
|
||||
*/
|
||||
public static String toJson(Object src) {
|
||||
return gson.toJson(src);
|
||||
}
|
||||
|
||||
public static String toPrettyString(Object src) {
|
||||
return prettyGson.toJson(src);
|
||||
}
|
||||
|
||||
public static <T> T fromJson2Object(String src, Class<T> t) {
|
||||
return gson.fromJson(src, t);
|
||||
}
|
||||
|
||||
public static <T> T fromJson2Object(String src, Type typeOfT) {
|
||||
return gson.fromJson(src, typeOfT);
|
||||
}
|
||||
|
||||
public static Gson getGson() {
|
||||
return gson;
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package com.tophold.trade.utils;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
* 作 者 : wgyscsf@163.com
|
||||
* 创建日期 :2017/10/25 16:08
|
||||
* 描 述 :
|
||||
* ============================================================
|
||||
**/
|
||||
public class RegxUtils {
|
||||
public static float getPureDouble(String str) {
|
||||
if (str == null || str.length() == 0) return 0;
|
||||
float result = 0;
|
||||
try {
|
||||
Pattern compile = Pattern.compile("(\\d+\\.\\d+)|(\\d+)");//如何提取带负数d ???
|
||||
Matcher matcher = compile.matcher(str);
|
||||
matcher.find();
|
||||
String string = matcher.group();//提取匹配到的结果
|
||||
result = Float.parseFloat(string);
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void main(String[] args){
|
||||
|
||||
test();
|
||||
}
|
||||
public static void test(){
|
||||
System.out.println(getPureDouble("12"));
|
||||
System.out.println(getPureDouble("wew3423.36"));
|
||||
System.out.println(getPureDouble("wewsf"));
|
||||
System.out.println(getPureDouble("000"));
|
||||
System.out.println(getPureDouble(null));
|
||||
}
|
||||
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
package com.tophold.trade.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.NinePatch;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
/**
|
||||
* ================================================
|
||||
* 作 者:JayGoo
|
||||
* 版 本:
|
||||
* 创建日期:2018/5/8
|
||||
* 描 述:
|
||||
* ================================================
|
||||
*/
|
||||
public class RenderUtils {
|
||||
|
||||
/**
|
||||
* make a drawable to a bitmap
|
||||
* @param drawable drawable you want convert
|
||||
* @return converted bitmap
|
||||
*/
|
||||
public static Bitmap drawableToBitmap(int size, Drawable drawable) {
|
||||
Bitmap bitmap = null;
|
||||
if (drawable instanceof BitmapDrawable) {
|
||||
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
|
||||
bitmap = bitmapDrawable.getBitmap();
|
||||
if (bitmap != null && bitmap.getHeight() > 0) {
|
||||
Matrix matrix = new Matrix();
|
||||
float scaleHeight = size * 1.0f / bitmapDrawable.getIntrinsicHeight();
|
||||
matrix.postScale(scaleHeight, scaleHeight);
|
||||
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||
drawable.draw(canvas);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* make a drawable to a bitmap
|
||||
*
|
||||
* @param drawable drawable you want convert
|
||||
* @return converted bitmap
|
||||
*/
|
||||
public static Bitmap drawableToBitmap(Drawable drawable) {
|
||||
return drawableToBitmap(drawable, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* make a drawable to a bitmap
|
||||
*
|
||||
* @param drawable drawable you want convert
|
||||
* @return converted bitmap
|
||||
*/
|
||||
public static Bitmap drawableToBitmap(Drawable drawable, float width, float hight) {
|
||||
Bitmap bitmap = null;
|
||||
float scaleWidth = width > 0 ? width : drawable.getIntrinsicWidth();
|
||||
float scaleHeight = hight > 0 ? hight : drawable.getIntrinsicHeight();
|
||||
if (drawable instanceof BitmapDrawable) {
|
||||
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
|
||||
bitmap = bitmapDrawable.getBitmap();
|
||||
if (bitmap != null && bitmap.getHeight() > 0) {
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.postScale(scaleWidth, scaleHeight);
|
||||
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
bitmap = Bitmap.createBitmap((int) scaleWidth, (int) scaleHeight, Bitmap.Config.ARGB_4444);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||
drawable.draw(canvas);
|
||||
return bitmap;
|
||||
}
|
||||
/**
|
||||
* draw 9Path
|
||||
*
|
||||
* @param canvas Canvas
|
||||
* @param bmp 9path bitmap
|
||||
* @param rect 9path rect
|
||||
*/
|
||||
public static void drawNinePath(Canvas canvas, Bitmap bmp, Rect rect) {
|
||||
NinePatch patch = new NinePatch(bmp, bmp.getNinePatchChunk(), null);
|
||||
patch.draw(canvas, rect);
|
||||
}
|
||||
|
||||
public static int dp2px(Context context, float dpValue) {
|
||||
if (context == null || compareFloat(0f,dpValue) == 0)return 0;
|
||||
final float scale = context.getResources().getDisplayMetrics().density;
|
||||
return (int) (dpValue * scale + 0.5f);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compare the size of two floating point numbers
|
||||
* @param a
|
||||
* @param b
|
||||
* @return 1 is a > b
|
||||
* -1 is a < b
|
||||
* 0 is a == b
|
||||
*/
|
||||
public static int compareFloat(float a, float b) {
|
||||
int ta = Math.round(a * 100000);
|
||||
int tb = Math.round(b * 100000);
|
||||
if (ta > tb) {
|
||||
return 1;
|
||||
} else if (ta < tb) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
package com.tophold.trade.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Build;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Display;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* 获取屏幕宽高等 相关工具类
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public class ScreenUtils {
|
||||
public static int screenWidth;
|
||||
public static int screenHeight;
|
||||
public static float density;
|
||||
public static float scaledDensity;
|
||||
public static int statusbarheight;
|
||||
public static int navbarheight;
|
||||
public static int realScreenHeight;
|
||||
public static int realScreenWidth;
|
||||
|
||||
static {
|
||||
init();
|
||||
}
|
||||
|
||||
private static void init() {
|
||||
DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics();
|
||||
screenWidth = displayMetrics.widthPixels;
|
||||
screenHeight = displayMetrics.heightPixels;
|
||||
density = displayMetrics.density;
|
||||
scaledDensity = displayMetrics.scaledDensity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取屏幕宽度
|
||||
*/
|
||||
public static int getScreenWidth() {
|
||||
return screenWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取屏幕高度(不包含底部虚拟按键)
|
||||
*/
|
||||
public static int getScreenHeight() {
|
||||
return screenHeight;
|
||||
}
|
||||
|
||||
public static int px2dip(float pxValue) {
|
||||
return (int) (pxValue / density + 0.5f);
|
||||
}
|
||||
|
||||
public static int dip2px(float dpValue) {
|
||||
return (int) (dpValue * density + 0.5f);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将px值转换为sp值,保证文字大小不变
|
||||
*/
|
||||
public static float px2sp(float pxValue) {
|
||||
return pxValue / scaledDensity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将sp值转换为px值,保证文字大小不变
|
||||
*/
|
||||
public static float sp2px(float spValue) {
|
||||
return spValue * scaledDensity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态栏高度
|
||||
*/
|
||||
public static int getStatusBarHight() {
|
||||
if (statusbarheight != 0) {
|
||||
return statusbarheight;
|
||||
}
|
||||
int result = 0;
|
||||
int resourceId = Resources.getSystem().getIdentifier("status_bar_height", "dimen", "android");
|
||||
if (resourceId > 0) {
|
||||
statusbarheight = result = Resources.getSystem().getDimensionPixelSize(resourceId);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取底部导航栏高度(不显示则为0)
|
||||
*/
|
||||
public static int getNavigationBarHeight(Context context) {
|
||||
if (navbarheight != 0) {
|
||||
return navbarheight;
|
||||
}
|
||||
return navbarheight = getRealScreenHeight(context) - getScreenHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取底部导航栏高度(无论显示与否)
|
||||
*/
|
||||
public static int getRealNavigationBarHeight() {
|
||||
int result = 0;
|
||||
int resourceId = Resources.getSystem().getIdentifier("navigation_bar_height", "dimen", "android");
|
||||
if (resourceId > 0) {
|
||||
result = Resources.getSystem().getDimensionPixelSize(resourceId);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 底部导航栏高度是否显示
|
||||
*/
|
||||
public static boolean hasNavigationBar(Context context) {
|
||||
return getNavigationBarHeight(context) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取真实屏幕高度(带虚拟按键)
|
||||
*/
|
||||
public static int getRealScreenHeight(Context context) {
|
||||
if (realScreenHeight != 0) {
|
||||
return realScreenHeight;
|
||||
}
|
||||
WindowManager wm = (WindowManager) context
|
||||
.getSystemService(Context.WINDOW_SERVICE);
|
||||
assert wm != null;
|
||||
Display display = wm.getDefaultDisplay();
|
||||
DisplayMetrics dm = new DisplayMetrics();
|
||||
if (Build.VERSION.SDK_INT >= 17) {//17以上可以直接获取 以下反射获取
|
||||
display.getRealMetrics(dm);
|
||||
} else {
|
||||
@SuppressWarnings("rawtypes")
|
||||
Class c;
|
||||
try {
|
||||
c = Class.forName("android.view.Display");
|
||||
@SuppressWarnings("unchecked")
|
||||
Method method = c.getMethod("getRealMetrics", DisplayMetrics.class);
|
||||
method.invoke(display, dm);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return realScreenHeight = dm.heightPixels;
|
||||
}
|
||||
|
||||
/*用于横屏时获取真实宽度*/
|
||||
public static int getRealScreenWidth(Context context) {
|
||||
if (realScreenWidth != 0) {
|
||||
return realScreenWidth;
|
||||
}
|
||||
WindowManager wm = (WindowManager) context
|
||||
.getSystemService(Context.WINDOW_SERVICE);
|
||||
assert wm != null;
|
||||
Display display = wm.getDefaultDisplay();
|
||||
DisplayMetrics dm = new DisplayMetrics();
|
||||
if (Build.VERSION.SDK_INT >= 17) {//17以上可以直接获取 以下反射获取
|
||||
display.getRealMetrics(dm);
|
||||
} else {
|
||||
@SuppressWarnings("rawtypes")
|
||||
Class c;
|
||||
try {
|
||||
c = Class.forName("android.view.Display");
|
||||
@SuppressWarnings("unchecked")
|
||||
Method method = c.getMethod("getRealMetrics", DisplayMetrics.class);
|
||||
method.invoke(display, dm);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return realScreenWidth = dm.widthPixels;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取view的矩形坐标
|
||||
*/
|
||||
public RectF getViewRectF(View view) {
|
||||
int[] location = new int[2];
|
||||
view.getLocationOnScreen(location);
|
||||
return new RectF(location[0], location[1], location[0] + view.getWidth(), location[1] + view.getHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否触摸在view上
|
||||
*/
|
||||
public boolean isViewTouched(View view, float rawX, float rawY) {
|
||||
return getViewRectF(view).contains(rawX, rawY);
|
||||
}
|
||||
|
||||
public boolean isViewTouched(View view, MotionEvent event) {
|
||||
// event.getX(); 获取相对于控件自身左上角的 x 坐标值
|
||||
// event.getY(); 获取相对于控件自身左上角的 y 坐标值
|
||||
float rawX = event.getRawX(); // 获取相对于屏幕左上角的 x 坐标值
|
||||
float rawY = event.getRawY();// 获取相对于屏幕左上角的 y 坐标值
|
||||
return isViewTouched(view, rawX, rawY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断一个view的矩形是否包含另一view的矩形
|
||||
*/
|
||||
public boolean containsRectF(View view1, View view2) {
|
||||
return getViewRectF(view1).contains(getViewRectF(view2));
|
||||
}
|
||||
|
||||
public boolean isInRectF(RectF rectF, float rawX, float rawY) {
|
||||
return rectF.contains(rawX, rawY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取软键盘高度(不显示则为0)
|
||||
*/
|
||||
public static int getSoftInputHeight(Activity activity) {
|
||||
if (activity == null || activity.isFinishing()) return 0;
|
||||
//获取显示区域
|
||||
Rect rect = new Rect();
|
||||
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
|
||||
//rect.bottom为显示区域底部高度(不包含键盘) 屏幕高度减去显示区域高度 即为软键盘高度
|
||||
return screenHeight - rect.bottom;
|
||||
}
|
||||
|
||||
/**
|
||||
* 软件盘是否显示
|
||||
*/
|
||||
public static boolean isSoftInputShown(Activity activity) {
|
||||
return getSoftInputHeight(activity) > 0;
|
||||
}
|
||||
|
||||
public static int getImageMaxEdge() {
|
||||
return (int) (165.0 / 320.0 * screenWidth);
|
||||
}
|
||||
|
||||
public static int getImageMinEdge() {
|
||||
return (int) (76.0 / 320.0 * screenWidth);
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
package com.tophold.trade.utils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
* 作 者 : wgyscsf@163.com
|
||||
* 创建日期 :2017/11/23 10:25
|
||||
* 描 述 :
|
||||
* ============================================================
|
||||
**/
|
||||
public class StringUtils {
|
||||
|
||||
public static boolean isEmptyString(String str) {
|
||||
if (str == null || str.isEmpty()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isNotEmptyString(String str) {
|
||||
return !isEmptyString(str);
|
||||
}
|
||||
|
||||
public static boolean isEmptyList(List list) {
|
||||
if (list == null || list.isEmpty()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isNotEmptyList(List list) {
|
||||
return !isEmptyList(list);
|
||||
}
|
||||
|
||||
|
||||
public static boolean isBlank(String... strs) {
|
||||
for (String str : strs) {
|
||||
if (str == null || str.equals("")) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isTrimBlank(String... strs) {
|
||||
for (String str : strs) {
|
||||
if (str == null || str.trim().equals("")) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isEmpty(List list) {
|
||||
if (list == null || list.size() == 0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机获取[m,n]之间的一个数字
|
||||
*
|
||||
* @param min
|
||||
* @param max
|
||||
* @return
|
||||
*/
|
||||
public static int getRadomNum(int min, int max) {
|
||||
Random rdm = new Random();
|
||||
return rdm.nextInt(max - min + 1) + min;
|
||||
}
|
||||
public static String getString(){
|
||||
return getRadomNum(0,1)==0?null:"sdaas";
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package com.tophold.trade.utils;
|
||||
|
||||
import android.support.annotation.IntDef;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* author: Blankj
|
||||
* blog : http://blankj.com
|
||||
* time : 2017/03/13
|
||||
* desc : 时间相关常量
|
||||
* </pre>
|
||||
*/
|
||||
public final class TimeConstants {
|
||||
|
||||
/**
|
||||
* 毫秒与毫秒的倍数
|
||||
*/
|
||||
public static final int MSEC = 1;
|
||||
/**
|
||||
* 秒与毫秒的倍数
|
||||
*/
|
||||
public static final int SEC = 1000;
|
||||
/**
|
||||
* 分与毫秒的倍数
|
||||
*/
|
||||
public static final int MIN = 60000;
|
||||
/**
|
||||
* 时与毫秒的倍数
|
||||
*/
|
||||
public static final int HOUR = 3600000;
|
||||
/**
|
||||
* 天与毫秒的倍数
|
||||
*/
|
||||
public static final int DAY = 86400000;
|
||||
|
||||
@IntDef({MSEC, SEC, MIN, HOUR, DAY})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Unit {
|
||||
}
|
||||
}
|
||||
+1622
File diff suppressed because it is too large
Load Diff
+178
@@ -0,0 +1,178 @@
|
||||
package com.tophold.trade.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.annotation.ColorRes;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import static android.view.View.MeasureSpec.AT_MOST;
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
* 作 者 : wgyscsf@163.com
|
||||
* 更新时间 : 20180404
|
||||
* 描 述 :整个自定义view最基础的类,在这里做一些基础重复的操作。
|
||||
* ============================================================
|
||||
*/
|
||||
public abstract class BaseView extends View {
|
||||
|
||||
protected String TAG;
|
||||
|
||||
protected Context mContext;
|
||||
|
||||
//长按阀值,默认多长时间算长按(ms)。不再设置为final,允许用户修改。
|
||||
protected long def_longpress_length = 700;
|
||||
//单击阀值
|
||||
protected long def_clickpress_length = 100;
|
||||
//移动阀值。手指移动多远算移动的阀值(单位:sp)
|
||||
protected long def_pull_length = 5;
|
||||
//onFling的阀值
|
||||
protected float def_onfling = 5;
|
||||
|
||||
//控件默认宽高。当控件的宽高设置为wrap_content时会采用该参数进行默认的设置(单位:sp)。
|
||||
//不允许用户修改,想要修改宽高,使用mWidth、mBaseHeight。
|
||||
protected final float DEF_WIDTH = 650;
|
||||
protected final float DEF_HIGHT = 400;
|
||||
|
||||
//测量的控件宽高,会在onMeasure中进行测量。
|
||||
protected int mBaseWidth;
|
||||
protected int mBaseHeight;
|
||||
|
||||
|
||||
public BaseView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public BaseView(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public BaseView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
TAG = this.getClass().getSimpleName();
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
|
||||
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
|
||||
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
|
||||
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
|
||||
if (widthSpecMode == AT_MOST && heightSpecMode == AT_MOST) {
|
||||
setMeasuredDimension((int) DEF_WIDTH, (int) DEF_HIGHT);
|
||||
} else if (widthSpecMode == AT_MOST) {
|
||||
setMeasuredDimension((int) DEF_WIDTH, heightSpecSize);
|
||||
} else if (heightSpecMode == AT_MOST) {
|
||||
setMeasuredDimension(widthSpecSize, (int) DEF_HIGHT);
|
||||
} else {
|
||||
setMeasuredDimension(widthSpecSize, heightSpecSize);
|
||||
}
|
||||
mBaseWidth = getMeasuredWidth();
|
||||
mBaseHeight = getMeasuredHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据颜色id获取颜色
|
||||
*
|
||||
* @param colorId
|
||||
* @return
|
||||
*/
|
||||
protected int getColor(@ColorRes int colorId) {
|
||||
return ContextCompat.getColor(mContext, colorId);
|
||||
}
|
||||
|
||||
protected String getString(@StringRes int stringId) {
|
||||
return getResources().getString(stringId);
|
||||
}
|
||||
|
||||
protected Drawable getDrawable(@DrawableRes int drawableId) {
|
||||
return ContextCompat.getDrawable(mContext, drawableId);
|
||||
}
|
||||
|
||||
protected int dp2px(float dp) {
|
||||
final float scale = mContext.getResources().getDisplayMetrics().density;
|
||||
return (int) (dp * scale + 0.5f);
|
||||
}
|
||||
|
||||
protected int sp2px(float sp) {
|
||||
final float fontScale = mContext.getResources().getDisplayMetrics().scaledDensity;
|
||||
return (int) (sp * fontScale + 0.5f);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测量指定画笔的文字的高度
|
||||
*
|
||||
* @param fontSize
|
||||
* @param paint
|
||||
* @return
|
||||
*/
|
||||
protected float getFontHeight(float fontSize, Paint paint) {
|
||||
paint.setTextSize(fontSize);
|
||||
Paint.FontMetrics fm = paint.getFontMetrics();
|
||||
return (float) (Math.ceil(fm.descent - fm.top) + 2f);
|
||||
}
|
||||
|
||||
|
||||
//----------------------对用户暴露可以修改的参数------------------
|
||||
|
||||
public long getDef_longpress_length() {
|
||||
return def_longpress_length;
|
||||
}
|
||||
|
||||
public void setDef_longpress_length(long def_longpress_length) {
|
||||
this.def_longpress_length = def_longpress_length;
|
||||
}
|
||||
|
||||
public long getDef_clickpress_length() {
|
||||
return def_clickpress_length;
|
||||
}
|
||||
|
||||
public void setDef_clickpress_length(long def_clickpress_length) {
|
||||
this.def_clickpress_length = def_clickpress_length;
|
||||
}
|
||||
|
||||
public long getDef_pull_length() {
|
||||
return def_pull_length;
|
||||
}
|
||||
|
||||
public void setDef_pull_length(long def_pull_length) {
|
||||
this.def_pull_length = def_pull_length;
|
||||
}
|
||||
|
||||
public float getDEF_WIDTH() {
|
||||
return DEF_WIDTH;
|
||||
}
|
||||
|
||||
public float getDEF_HIGHT() {
|
||||
return DEF_HIGHT;
|
||||
}
|
||||
|
||||
public int getBaseWidth() {
|
||||
return mBaseWidth;
|
||||
}
|
||||
|
||||
public void setBaseWidth(int baseWidth) {
|
||||
mBaseWidth = baseWidth;
|
||||
}
|
||||
|
||||
public int getBaseHeight() {
|
||||
return mBaseHeight;
|
||||
}
|
||||
|
||||
public void setBaseHeight(int baseHeight) {
|
||||
mBaseHeight = baseHeight;
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.tophold.trade.view.fund;
|
||||
|
||||
|
||||
import com.tophold.trade.utils.RegxUtils;
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
* 作 者 : wgyscsf@163.com
|
||||
* 创建日期 :2017/10/25 14:50
|
||||
* 描 述 :
|
||||
* ============================================================
|
||||
**/
|
||||
public class FundMode {
|
||||
//x轴原始时间数据,ms
|
||||
public long datetime;
|
||||
//y轴的原始数据
|
||||
public String originDataY;
|
||||
//y轴的转换后的数据
|
||||
public float dataY;
|
||||
|
||||
//在自定义view:FundView中的位置坐标
|
||||
public float floatX;
|
||||
public float floatY;
|
||||
|
||||
public FundMode(long timestamp, String actual) {
|
||||
this.datetime = timestamp;
|
||||
this.originDataY = actual;
|
||||
this.dataY = RegxUtils.getPureDouble(originDataY);//提取后的Y周的值
|
||||
}
|
||||
}
|
||||
+789
@@ -0,0 +1,789 @@
|
||||
package com.tophold.trade.view.fund;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.DashPathEffect;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PathEffect;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.List;
|
||||
|
||||
import com.tophold.trade.R;
|
||||
import com.tophold.trade.view.BaseView;
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
* 作 者 : wgyscsf@163.com
|
||||
* 创建日期 :2017/10/25 14:47,update:2018/04/05
|
||||
* 描 述 :蚂蚁财富基金收益折线图。
|
||||
* 想要定制:哪些字段可以修改?哪些字段不能修改?
|
||||
* 可以修改:各种画笔的状态(颜色、样式、粗细、文字大小)、边界padding、宽高等;
|
||||
* 不可以修改:计算出来的最大值最小值、boolean类型的临时状态、长按所赋的值、单元大小等
|
||||
* (随便修改不建议修改的字段可能会发生不可预料的问题你,但是所有字段都会暴露出去(尽量),给更大的灵活性去定制)。
|
||||
* 注意:对于Paint的子属性请直接调用Piant然后再进行设置,
|
||||
* 不要(程序已经限制,因为会和外部调用Paint去设置属性混乱)使用该内部的Paint的设置子属性的方法。
|
||||
* ============================================================
|
||||
**/
|
||||
public class FundView extends BaseView {
|
||||
|
||||
//数据源
|
||||
List<FundMode> mFundModeList;
|
||||
|
||||
//上下左右padding,允许修改
|
||||
protected float mBasePaddingTop = 100;
|
||||
protected float mBasePaddingBottom = 70;
|
||||
protected float mBasePaddingLeft = 50;
|
||||
protected float mBasePaddingRight = 50;
|
||||
|
||||
//Y轴对应的最大值和最小值,注意,这里存的是对象。原则上不允许修改。
|
||||
protected FundMode mMinFundMode;
|
||||
protected FundMode mMaxFundMode;
|
||||
|
||||
//X、Y轴每一个data对应的大小。原则上不允许修改。
|
||||
protected float mPerX;
|
||||
protected float mPerY;
|
||||
|
||||
//正在加载中,允许修改
|
||||
protected Paint mLoadingPaint;
|
||||
protected float mLoadingTextSize = 20;
|
||||
protected String mLoadingText = "数据加载,请稍后";
|
||||
//原则上不允许修改。
|
||||
protected boolean mDrawLoadingPaint = true;
|
||||
|
||||
|
||||
//外围X、Y轴线文字。允许修改。
|
||||
protected Paint mXYPaint;
|
||||
//x、y轴指示文字字体的大小
|
||||
protected float mXYTextSize = 14;
|
||||
//左侧文字距离左边线线的距离
|
||||
protected float mLeftTxtPadding = 16;
|
||||
//底部文字距离底部线的距离
|
||||
protected float mBottomTxtPadding = 20;
|
||||
|
||||
|
||||
//内部X轴虚线。允许修改。
|
||||
protected Paint mInnerXPaint;
|
||||
protected float mInnerXStrokeWidth = 1;
|
||||
|
||||
//折线。允许修改。
|
||||
protected Paint mBrokenPaint;
|
||||
//单位:sp.。允许修改。
|
||||
protected float mBrokenStrokeWidth = 1;
|
||||
|
||||
//长按的十字线,允许修改。
|
||||
protected Paint mLongPressPaint;
|
||||
//原则上不允许修改。
|
||||
protected boolean mDrawLongPressPaint = false;
|
||||
//长按处理,原则上不允许修改。
|
||||
protected long mPressTime;
|
||||
protected float mPressX;
|
||||
protected float mPressY;
|
||||
|
||||
//最上面默认显示累计收益金额,允许修改。
|
||||
protected Paint mDefAllIncomePaint;
|
||||
protected float mDefAllIncomeTextSize = 20;
|
||||
|
||||
|
||||
//长按情况下x轴和y轴要显示的文字,允许修改。
|
||||
protected Paint mLongPressTxtPaint;
|
||||
protected float mLongPressTextSize = 20;
|
||||
|
||||
public FundView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public FundView(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public FundView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initAttrs();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
//setDefAttrs();
|
||||
|
||||
//默认加载loading界面
|
||||
showLoadingPaint(canvas);
|
||||
if (mFundModeList == null || mFundModeList.size() == 0) return;
|
||||
|
||||
//加载三个核心Paint
|
||||
drawInnerXPaint(canvas);
|
||||
drawBrokenPaint(canvas);
|
||||
drawXYPaint(canvas);
|
||||
|
||||
drawTopTxtPaint(canvas);
|
||||
|
||||
drawLongPress(canvas);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mPressTime = event.getDownTime();
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
if (event.getEventTime() - mPressTime > def_longpress_length) {
|
||||
Log.d(TAG, "onTouchEvent: 长按了。。。");
|
||||
mPressX = event.getX();
|
||||
mPressY = event.getY();
|
||||
//处理长按后的逻辑
|
||||
showLongPressView();
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
//处理松手后的逻辑
|
||||
hiddenLongPressView();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void initAttrs() {
|
||||
initLoadingPaint();
|
||||
initInnerXPaint();
|
||||
initXYPaint();
|
||||
initBrokenPaint();
|
||||
initLongPressPaint();
|
||||
initTopTxt();
|
||||
}
|
||||
|
||||
private void initLoadingPaint() {
|
||||
mLoadingPaint = new Paint();
|
||||
mLoadingPaint.setColor(getColor(R.color.color_fundView_xyTxtColor));
|
||||
mLoadingPaint.setTextSize(mLoadingTextSize);
|
||||
mLoadingPaint.setAntiAlias(true);
|
||||
}
|
||||
|
||||
//初始化绘制虚线的画笔
|
||||
private void initInnerXPaint() {
|
||||
mInnerXPaint = new Paint();
|
||||
mInnerXPaint.setColor(getColor(R.color.color_fundView_xLineColor));
|
||||
mInnerXPaint.setStrokeWidth(mInnerXStrokeWidth);
|
||||
mInnerXPaint.setStyle(Paint.Style.STROKE);
|
||||
setLayerType(LAYER_TYPE_SOFTWARE, null);//禁用硬件加速
|
||||
PathEffect effects = new DashPathEffect(new float[]{5, 5, 5, 5}, 1);
|
||||
mInnerXPaint.setPathEffect(effects);
|
||||
}
|
||||
|
||||
private void initXYPaint() {
|
||||
mXYPaint = new Paint();
|
||||
mXYPaint.setColor(getColor(R.color.color_fundView_xyTxtColor));
|
||||
mXYPaint.setTextSize(mXYTextSize);
|
||||
mXYPaint.setAntiAlias(true);
|
||||
}
|
||||
|
||||
private void initBrokenPaint() {
|
||||
mBrokenPaint = new Paint();
|
||||
mBrokenPaint.setColor(getColor(R.color.color_fundView_brokenLineColor));
|
||||
mBrokenPaint.setStyle(Paint.Style.STROKE);
|
||||
mBrokenPaint.setAntiAlias(true);
|
||||
mBrokenPaint.setStrokeWidth(mBrokenStrokeWidth);
|
||||
}
|
||||
|
||||
private void initLongPressPaint() {
|
||||
mLongPressPaint = new Paint();
|
||||
mLongPressPaint.setColor(getColor(R.color.color_fundView_longPressLineColor));
|
||||
mLongPressPaint.setStyle(Paint.Style.FILL);
|
||||
mLongPressPaint.setAntiAlias(true);
|
||||
mLongPressPaint.setTextSize(mLongPressTextSize);
|
||||
}
|
||||
|
||||
//折线上面显示文字信息
|
||||
private void initTopTxt() {
|
||||
mDefAllIncomePaint = new Paint();
|
||||
mDefAllIncomePaint.setColor(getColor(R.color.color_fundView_defIncomeTxt));
|
||||
mDefAllIncomePaint.setTextSize(mLongPressTextSize);
|
||||
mDefAllIncomePaint.setAntiAlias(true);
|
||||
|
||||
mLongPressTxtPaint = new Paint();
|
||||
mLongPressTxtPaint.setColor(getColor(R.color.color_fundView_longPressLineColor));
|
||||
mLongPressTxtPaint.setTextSize(mLongPressTextSize);
|
||||
mLongPressTxtPaint.setAntiAlias(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将画笔使用的属性在这里设置。
|
||||
* 主要是为了覆盖用户动态设置的属性,
|
||||
* 因为在构造方法中设置的会无效(用户设置的在构造方法之后)。
|
||||
* 注意:这个方法不能使用,因为会覆盖Paint内部的设置属性的方法
|
||||
*/
|
||||
@Deprecated
|
||||
private void setDefAttrs() {
|
||||
mLoadingPaint.setTextSize(mLoadingTextSize);
|
||||
mInnerXPaint.setStrokeWidth(mInnerXStrokeWidth);
|
||||
mXYPaint.setTextSize(mXYTextSize);
|
||||
mBrokenPaint.setStrokeWidth(mBrokenStrokeWidth);
|
||||
mLongPressPaint.setTextSize(mLongPressTextSize);
|
||||
mDefAllIncomePaint.setTextSize(mLongPressTextSize);
|
||||
mLongPressTxtPaint.setTextSize(mLongPressTextSize);
|
||||
}
|
||||
|
||||
private void showLoadingPaint(Canvas canvas) {
|
||||
if (!mDrawLoadingPaint) return;
|
||||
//这里特别注意,x轴的起始点要减去文字宽度的一半
|
||||
canvas.drawText(mLoadingText, mBaseWidth / 2 - mLoadingPaint.measureText(mLoadingText) / 2, mBaseHeight / 2, mLoadingPaint);
|
||||
}
|
||||
|
||||
private void drawInnerXPaint(Canvas canvas) {
|
||||
//画5条横轴的虚线
|
||||
//首先确定最大值和最小值的位置
|
||||
float perHight = (mBaseHeight - mBasePaddingBottom - mBasePaddingTop) / 4;
|
||||
|
||||
canvas.drawLine(0 + mBasePaddingLeft, mBasePaddingTop,
|
||||
mBaseWidth - mBasePaddingRight, mBasePaddingTop, mInnerXPaint);//最上面的那一条
|
||||
|
||||
canvas.drawLine(0 + mBasePaddingLeft, mBasePaddingTop + perHight * 1,
|
||||
mBaseWidth - mBasePaddingRight, mBasePaddingTop + perHight * 1, mInnerXPaint);//2
|
||||
|
||||
canvas.drawLine(0 + mBasePaddingLeft, mBasePaddingTop + perHight * 2,
|
||||
mBaseWidth - mBasePaddingRight, mBasePaddingTop + perHight * 2, mInnerXPaint);//3
|
||||
|
||||
canvas.drawLine(0 + mBasePaddingLeft, mBasePaddingTop + perHight * 3,
|
||||
mBaseWidth - mBasePaddingRight, mBasePaddingTop + perHight * 3, mInnerXPaint);//4
|
||||
|
||||
canvas.drawLine(0 + mBasePaddingLeft, mBaseHeight - mBasePaddingBottom,
|
||||
mBaseWidth - mBasePaddingRight, mBaseHeight - mBasePaddingBottom, mInnerXPaint);//最下面的那一条
|
||||
|
||||
}
|
||||
|
||||
private void drawBrokenPaint(Canvas canvas) {
|
||||
//先画第一个点
|
||||
FundMode fundMode = mFundModeList.get(0);
|
||||
Path path = new Path();
|
||||
//这里需要说明一下,x轴的起始点,其实需要加上mPerX,但是加上之后不是从起始位置开始,不好看。
|
||||
// 同理,for循环内x轴其实需要(i+1)。现在这样处理,最后会留一点空隙,其实挺好看的。
|
||||
float floatY = mBaseHeight - mBasePaddingBottom - mPerY * (fundMode.dataY - mMinFundMode.dataY);
|
||||
fundMode.floatX = mBasePaddingLeft;
|
||||
fundMode.floatY = floatY;
|
||||
path.moveTo(mBasePaddingLeft, floatY);
|
||||
for (int i = 1; i < mFundModeList.size(); i++) {
|
||||
FundMode fm = mFundModeList.get(i);
|
||||
float floatX2 = mBasePaddingLeft + mPerX * i;
|
||||
float floatY2 = mBaseHeight - mBasePaddingBottom - mPerY * (fm.dataY - mMinFundMode.dataY);
|
||||
fm.floatX = floatX2;
|
||||
fm.floatY = floatY2;
|
||||
path.lineTo(floatX2, floatY2);
|
||||
//Log.e(TAG, "drawBrokenPaint: " + mBasePaddingLeft + mPerX * i + "-----" + (mBaseHeight - mClosePerY * (mFundModeList.get(i).dataY - mMinFundMode.dataY) - mBasePaddingBottom));
|
||||
}
|
||||
|
||||
canvas.drawPath(path, mBrokenPaint);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void drawXYPaint(Canvas canvas) {
|
||||
//先处理y轴方向文字
|
||||
drawYPaint(canvas);
|
||||
|
||||
//处理x轴方向文字
|
||||
drawXPaint(canvas);
|
||||
}
|
||||
|
||||
private void drawTopTxtPaint(Canvas canvas) {
|
||||
//先画默认情况下的top文字
|
||||
drawDefTopTxtpaint(canvas);
|
||||
//按下的文字信息在按下之后处理,see:drawLongPress(Canvas canvas)
|
||||
}
|
||||
|
||||
/**
|
||||
* 这里处理画十字的逻辑:这里的十字不是手指按下的位置,这样没有意义。
|
||||
* 而是当前按下的距离x轴最近的时间(注意:并不一定按下对应的x轴就是有时间的,如果没有取最近的)。
|
||||
* 当取到x轴的值,之后算出来对应的y轴的值,这个才是十字对应的位置坐标。
|
||||
* 如何获取x轴最近的时间?我们可以在FundMode中定义x\y的位置参数,遍历对比找到最小即可。
|
||||
* (see: drawBrokenPaint(canvas);)
|
||||
*
|
||||
* @param canvas
|
||||
*/
|
||||
private void drawLongPress(Canvas canvas) {
|
||||
if (!mDrawLongPressPaint) return;
|
||||
|
||||
//获取距离最近按下的位置的model
|
||||
float pressX = mPressX;
|
||||
//循环遍历,找到距离最短的x轴的mode
|
||||
FundMode finalFundMode = mFundModeList.get(0);
|
||||
float minXLen = Integer.MAX_VALUE;
|
||||
for (int i = 0; i < mFundModeList.size(); i++) {
|
||||
FundMode currFunMode = mFundModeList.get(i);
|
||||
float abs = Math.abs(pressX - currFunMode.floatX);
|
||||
if (abs < minXLen) {
|
||||
finalFundMode = currFunMode;
|
||||
minXLen = abs;
|
||||
}
|
||||
}
|
||||
|
||||
//x
|
||||
canvas.drawLine(mBasePaddingLeft, finalFundMode.floatY, mBaseWidth - mBasePaddingRight, finalFundMode.floatY, mLongPressPaint);
|
||||
//y
|
||||
canvas.drawLine(finalFundMode.floatX, mBasePaddingTop, finalFundMode.floatX, mBaseHeight - mBasePaddingBottom, mLongPressPaint);
|
||||
|
||||
//开始处理按下之后top的文字信息
|
||||
//先画背景
|
||||
float hight = mBasePaddingTop - 30;
|
||||
Paint bgColor = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
bgColor.setColor(getColor(R.color.color_fundView_pressIncomeTxtBg));
|
||||
canvas.drawRect(0, 0, mBaseWidth, hight, bgColor);
|
||||
|
||||
//开始画按下之后左边的日期文字
|
||||
Paint timePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
timePaint.setTextSize(mLongPressTextSize);
|
||||
timePaint.setColor(getColor(R.color.color_fundView_xyTxtColor));
|
||||
canvas.drawText(processDateTime(finalFundMode.datetime) + "",
|
||||
10, hight / 2 + getFontHeight(mLongPressTextSize, timePaint) / 2, timePaint);
|
||||
|
||||
//右边红色收益文字
|
||||
canvas.drawText(finalFundMode.dataY + "",
|
||||
mBaseWidth - mBasePaddingRight - mLongPressPaint.measureText(finalFundMode.dataY + ""),
|
||||
hight / 2 + getFontHeight(mLongPressTextSize, timePaint) / 2, mLongPressPaint);
|
||||
|
||||
//右边的左边的提示文字
|
||||
Paint hintPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
hintPaint.setTextSize(mLongPressTextSize);
|
||||
hintPaint.setColor(getColor(R.color.color_fundView_xyTxtColor));
|
||||
canvas.drawText(getString(R.string.string_fundView_pressHintTxt),
|
||||
mBaseWidth - mBasePaddingRight - mLongPressPaint.measureText(finalFundMode.dataY + "")
|
||||
- hintPaint.measureText(getString(R.string.string_fundView_pressHintTxt)),
|
||||
hight / 2 + getFontHeight(mLongPressTextSize, timePaint) / 2, hintPaint);
|
||||
|
||||
|
||||
}
|
||||
|
||||
//找到最大时间、最小时间和中间时间显示即可
|
||||
private void drawXPaint(Canvas canvas) {
|
||||
long beginTime = mFundModeList.get(0).datetime;
|
||||
long midTime = mFundModeList.get((mFundModeList.size() - 1) / 2).datetime;
|
||||
long endTime = mFundModeList.get(mFundModeList.size() - 1).datetime;
|
||||
String bengin = processDateTime(beginTime);
|
||||
String mid = processDateTime(midTime);
|
||||
String end = processDateTime(endTime);
|
||||
|
||||
//x轴文字的高度
|
||||
float hight = mBaseHeight - mBasePaddingBottom + mBottomTxtPadding;
|
||||
|
||||
canvas.drawText(bengin,
|
||||
mBasePaddingLeft,
|
||||
hight, mXYPaint);
|
||||
|
||||
canvas.drawText(mid,
|
||||
mBasePaddingLeft + (mBaseWidth - mBasePaddingLeft - mBasePaddingRight) / 2,
|
||||
hight, mXYPaint);
|
||||
|
||||
canvas.drawText(end,
|
||||
mBaseWidth - mBasePaddingRight - mXYPaint.measureText(end),
|
||||
hight, mXYPaint);//特别注意x轴的处理:- mXYPaint.measureText(end)
|
||||
|
||||
}
|
||||
|
||||
private void drawYPaint(Canvas canvas) {
|
||||
//现将最小值、最大值画好
|
||||
//draw min
|
||||
float txtWigth = mXYPaint.measureText(mMinFundMode.originDataY) + mLeftTxtPadding;
|
||||
canvas.drawText(mMinFundMode.originDataY + "",
|
||||
mBasePaddingLeft - txtWigth,
|
||||
mBaseHeight - mBasePaddingBottom, mXYPaint);
|
||||
//draw max
|
||||
canvas.drawText(mMaxFundMode.dataY + "",
|
||||
mBasePaddingLeft - txtWigth,
|
||||
mBasePaddingTop, mXYPaint);
|
||||
//因为横线是均分的,所以只要取到最大值最小值的差值,均分即可。
|
||||
float perYValues = (mMaxFundMode.dataY - mMinFundMode.dataY) / 4;
|
||||
float perYWidth = (mBaseHeight - mBasePaddingBottom - mBasePaddingTop) / 4;
|
||||
//从下到上依次画
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
canvas.drawText(mMinFundMode.dataY + perYValues * i + "",
|
||||
mBasePaddingLeft - txtWigth,
|
||||
mBaseHeight - mBasePaddingBottom - perYWidth * i, mXYPaint);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawDefTopTxtpaint(Canvas canvas) {
|
||||
//画默认情况下前面的蓝色小圆点
|
||||
Paint buleDotPaint = new Paint();
|
||||
buleDotPaint.setColor(getColor(R.color.color_fundView_brokenLineColor));
|
||||
buleDotPaint.setAntiAlias(true);
|
||||
float r = 6;
|
||||
buleDotPaint.setStyle(Paint.Style.FILL);
|
||||
canvas.drawCircle(mBasePaddingLeft + r / 2, mBasePaddingTop / 2 + r, r, buleDotPaint);
|
||||
|
||||
float txtHight = getFontHeight(mDefAllIncomeTextSize, mDefAllIncomePaint);
|
||||
|
||||
//先画hint文字
|
||||
Paint hintPaint = new Paint();
|
||||
hintPaint.setColor(getColor(R.color.color_fundView_xyTxtColor));
|
||||
hintPaint.setAntiAlias(true);
|
||||
hintPaint.setTextSize(mDefAllIncomeTextSize);
|
||||
String hintTxt = getString(R.string.string_fundView_defHintTxt);
|
||||
canvas.drawText(hintTxt, mBasePaddingLeft + r + 10, mBasePaddingTop / 2 + txtHight / 2,
|
||||
mDefAllIncomePaint);
|
||||
|
||||
|
||||
if (mFundModeList == null || mFundModeList.isEmpty()) return;
|
||||
canvas.drawText(mFundModeList.get(mFundModeList.size() - 1).dataY + "",
|
||||
mBasePaddingLeft + r + 10 + hintPaint.measureText(getString(R.string.string_fundView_defHintTxt)) + 5,
|
||||
mBasePaddingTop / 2 + txtHight / 2, mDefAllIncomePaint);
|
||||
}
|
||||
|
||||
private void showLongPressView() {
|
||||
mDrawLongPressPaint = true;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private void hiddenLongPressView() {
|
||||
//实现蚂蚁金服延迟消失十字线
|
||||
postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mDrawLongPressPaint = false;
|
||||
invalidate();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// 只需要把画笔颜色置为透明即可
|
||||
private void hiddenLoadingPaint() {
|
||||
mLoadingPaint.setColor(0x00000000);
|
||||
mDrawLoadingPaint = false;
|
||||
}
|
||||
|
||||
private String processDateTime(long beginTime) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
||||
return sdf.format(beginTime);
|
||||
}
|
||||
|
||||
public float getFontHeight(float fontSize, Paint paint) {
|
||||
paint.setTextSize(fontSize);
|
||||
Paint.FontMetrics fm = paint.getFontMetrics();
|
||||
return (float) (Math.ceil(fm.descent - fm.top) + 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 程序入口,设置数据
|
||||
*/
|
||||
public void setDataList(List<FundMode> fundModeList) {
|
||||
if (fundModeList == null || fundModeList.size() == 0) return;
|
||||
this.mFundModeList = fundModeList;
|
||||
|
||||
//开始获取最大值最小值;单个数据尺寸等
|
||||
mMinFundMode = mFundModeList.get(0);
|
||||
mMaxFundMode = mFundModeList.get(0);
|
||||
for (FundMode fundMode : mFundModeList) {
|
||||
if (fundMode.dataY < mMinFundMode.dataY) {
|
||||
mMinFundMode = fundMode;
|
||||
}
|
||||
if (fundMode.dataY > mMaxFundMode.dataY) {
|
||||
mMaxFundMode = fundMode;
|
||||
}
|
||||
}
|
||||
//获取单个数据X/y轴的大小
|
||||
mPerX = (mBaseWidth - mBasePaddingLeft - mBasePaddingRight) / mFundModeList.size();
|
||||
mPerY = ((mBaseHeight - mBasePaddingTop - mBasePaddingBottom) / (mMaxFundMode.dataY - mMinFundMode.dataY));
|
||||
Log.e(TAG, "setDataList: " + mMinFundMode + "," + mMaxFundMode + "..." + mPerX + "," + mPerY);
|
||||
|
||||
//数据过来,隐藏加载更多
|
||||
hiddenLoadingPaint();
|
||||
|
||||
//刷新界面
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------对开发者暴露可以修改的参数-------
|
||||
|
||||
|
||||
public List<FundMode> getFundModeList() {
|
||||
return mFundModeList;
|
||||
}
|
||||
|
||||
public FundView setFundModeList(List<FundMode> fundModeList) {
|
||||
mFundModeList = fundModeList;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getBasePaddingTop() {
|
||||
return mBasePaddingTop;
|
||||
}
|
||||
|
||||
public FundView setBasePaddingTop(float basePaddingTop) {
|
||||
mBasePaddingTop = basePaddingTop;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getBasePaddingBottom() {
|
||||
return mBasePaddingBottom;
|
||||
}
|
||||
|
||||
public FundView setBasePaddingBottom(float basePaddingBottom) {
|
||||
mBasePaddingBottom = basePaddingBottom;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getBasePaddingLeft() {
|
||||
return mBasePaddingLeft;
|
||||
}
|
||||
|
||||
public FundView setBasePaddingLeft(float basePaddingLeft) {
|
||||
mBasePaddingLeft = basePaddingLeft;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getBasePaddingRight() {
|
||||
return mBasePaddingRight;
|
||||
}
|
||||
|
||||
public FundView setBasePaddingRight(float basePaddingRight) {
|
||||
mBasePaddingRight = basePaddingRight;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FundMode getMinFundMode() {
|
||||
return mMinFundMode;
|
||||
}
|
||||
|
||||
public FundView setMinFundMode(FundMode minFundMode) {
|
||||
mMinFundMode = minFundMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FundMode getMaxFundMode() {
|
||||
return mMaxFundMode;
|
||||
}
|
||||
|
||||
public FundView setMaxFundMode(FundMode maxFundMode) {
|
||||
mMaxFundMode = maxFundMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getPerX() {
|
||||
return mPerX;
|
||||
}
|
||||
|
||||
public FundView setPerX(float perX) {
|
||||
mPerX = perX;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getPerY() {
|
||||
return mPerY;
|
||||
}
|
||||
|
||||
public FundView setPerY(float perY) {
|
||||
mPerY = perY;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Paint getLoadingPaint() {
|
||||
return mLoadingPaint;
|
||||
}
|
||||
|
||||
public FundView setLoadingPaint(Paint loadingPaint) {
|
||||
mLoadingPaint = loadingPaint;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getLoadingTextSize() {
|
||||
return mLoadingTextSize;
|
||||
}
|
||||
|
||||
private FundView setLoadingTextSize(float loadingTextSize) {
|
||||
mLoadingTextSize = loadingTextSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getLoadingText() {
|
||||
return mLoadingText;
|
||||
}
|
||||
|
||||
public FundView setLoadingText(String loadingText) {
|
||||
mLoadingText = loadingText;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isDrawLoadingPaint() {
|
||||
return mDrawLoadingPaint;
|
||||
}
|
||||
|
||||
public FundView setDrawLoadingPaint(boolean drawLoadingPaint) {
|
||||
mDrawLoadingPaint = drawLoadingPaint;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Paint getXYPaint() {
|
||||
return mXYPaint;
|
||||
}
|
||||
|
||||
public FundView setXYPaint(Paint XYPaint) {
|
||||
mXYPaint = XYPaint;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getXYTextSize() {
|
||||
return mXYTextSize;
|
||||
}
|
||||
|
||||
private FundView setXYTextSize(float XYTextSize) {
|
||||
mXYTextSize = XYTextSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getLeftTxtPadding() {
|
||||
return mLeftTxtPadding;
|
||||
}
|
||||
|
||||
public FundView setLeftTxtPadding(float leftTxtPadding) {
|
||||
mLeftTxtPadding = leftTxtPadding;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getBottomTxtPadding() {
|
||||
return mBottomTxtPadding;
|
||||
}
|
||||
|
||||
public FundView setBottomTxtPadding(float bottomTxtPadding) {
|
||||
mBottomTxtPadding = bottomTxtPadding;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Paint getInnerXPaint() {
|
||||
return mInnerXPaint;
|
||||
}
|
||||
|
||||
public FundView setInnerXPaint(Paint innerXPaint) {
|
||||
mInnerXPaint = innerXPaint;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getInnerXStrokeWidth() {
|
||||
return mInnerXStrokeWidth;
|
||||
}
|
||||
|
||||
private FundView setInnerXStrokeWidth(float innerXStrokeWidth) {
|
||||
mInnerXStrokeWidth = innerXStrokeWidth;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Paint getBrokenPaint() {
|
||||
return mBrokenPaint;
|
||||
}
|
||||
|
||||
public FundView setBrokenPaint(Paint brokenPaint) {
|
||||
mBrokenPaint = brokenPaint;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getBrokenStrokeWidth() {
|
||||
return mBrokenStrokeWidth;
|
||||
}
|
||||
|
||||
private FundView setBrokenStrokeWidth(float brokenStrokeWidth) {
|
||||
mBrokenStrokeWidth = brokenStrokeWidth;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Paint getLongPressPaint() {
|
||||
return mLongPressPaint;
|
||||
}
|
||||
|
||||
public FundView setLongPressPaint(Paint longPressPaint) {
|
||||
mLongPressPaint = longPressPaint;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isDrawLongPressPaint() {
|
||||
return mDrawLongPressPaint;
|
||||
}
|
||||
|
||||
public FundView setDrawLongPressPaint(boolean drawLongPressPaint) {
|
||||
mDrawLongPressPaint = drawLongPressPaint;
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getPressTime() {
|
||||
return mPressTime;
|
||||
}
|
||||
|
||||
public FundView setPressTime(long pressTime) {
|
||||
mPressTime = pressTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getPressX() {
|
||||
return mPressX;
|
||||
}
|
||||
|
||||
public FundView setPressX(float pressX) {
|
||||
mPressX = pressX;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getPressY() {
|
||||
return mPressY;
|
||||
}
|
||||
|
||||
public FundView setPressY(float pressY) {
|
||||
mPressY = pressY;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Paint getDefAllIncomePaint() {
|
||||
return mDefAllIncomePaint;
|
||||
}
|
||||
|
||||
public FundView setDefAllIncomePaint(Paint defAllIncomePaint) {
|
||||
mDefAllIncomePaint = defAllIncomePaint;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getDefAllIncomeTextSize() {
|
||||
return mDefAllIncomeTextSize;
|
||||
}
|
||||
|
||||
public FundView setDefAllIncomeTextSize(float defAllIncomeTextSize) {
|
||||
mDefAllIncomeTextSize = defAllIncomeTextSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Paint getLongPressTxtPaint() {
|
||||
return mLongPressTxtPaint;
|
||||
}
|
||||
|
||||
public FundView setLongPressTxtPaint(Paint longPressTxtPaint) {
|
||||
mLongPressTxtPaint = longPressTxtPaint;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getLongPressTextSize() {
|
||||
return mLongPressTextSize;
|
||||
}
|
||||
|
||||
private FundView setLongPressTextSize(float longPressTextSize) {
|
||||
mLongPressTextSize = longPressTextSize;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+573
@@ -0,0 +1,573 @@
|
||||
package com.tophold.trade.view.kview;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
* 作 者 : wgyscsf@163.com
|
||||
* 创建日期 :2017/12/18 11:24
|
||||
* 描 述 :所有金融相关算法全部在这里。算法参考【资料】包中的:常用股票指标计算公式及简单应用.pdf
|
||||
* ============================================================
|
||||
**/
|
||||
// TODO: 2017/12/18 这里的算法需要核实!包括异常情况的处理和边界的处理是否合适。
|
||||
public class FinancialAlgorithm {
|
||||
final static String TAG = FinancialAlgorithm.class.getSimpleName();
|
||||
|
||||
public static void calculateKDJ(List<Quotes> quotesList) {
|
||||
calculateKDJ(quotesList, 9, 3, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* 【该算法核对过,但是和线上的APP有细微的差距,不知道是否有错!】计算数据集合的kdj。
|
||||
*
|
||||
* @param quotesList 对应的数据集合
|
||||
* @param kPeriod k所对应的周期,可以是:分钟、小时、天等
|
||||
* @param dPeriod d所对应的周期,可以是:分钟、小时、天等
|
||||
* @param jPeriod j所对应的周期,可以是:分钟、小时、天等
|
||||
*/
|
||||
public static void calculateKDJ(List<Quotes> quotesList, int kPeriod, int dPeriod, int jPeriod) {
|
||||
//容错
|
||||
if (quotesList == null || quotesList.isEmpty()) return;
|
||||
if (kPeriod <= 0) kPeriod = 9;
|
||||
if (dPeriod <= 0) dPeriod = 3;
|
||||
if (jPeriod <= 0) jPeriod = 3;
|
||||
|
||||
//转换程序员的序号
|
||||
kPeriod--;
|
||||
dPeriod--;
|
||||
jPeriod--;
|
||||
double kRsv = 0;
|
||||
//double dRsv=0;
|
||||
double k = 0;
|
||||
double d = 0;
|
||||
double j = 0;
|
||||
for (int i = 0; i < quotesList.size(); i++) {
|
||||
Quotes quotes = quotesList.get(i);
|
||||
|
||||
//计算k
|
||||
if (i < kPeriod) {
|
||||
k = 50;
|
||||
} else {
|
||||
double c = 0;
|
||||
double l = 0;
|
||||
double h = 0;
|
||||
double tempL = Integer.MAX_VALUE;
|
||||
double tempH = Integer.MIN_VALUE;
|
||||
for (int i1 = i - kPeriod; i1 <= i; i1++) {
|
||||
Quotes kQuotes = quotesList.get(i1);
|
||||
if (kQuotes.c < tempL) {
|
||||
tempL = kQuotes.c;
|
||||
l = tempL;
|
||||
}
|
||||
if (kQuotes.c > tempH) {
|
||||
tempH = kQuotes.c;
|
||||
h = tempH;
|
||||
}
|
||||
if (i1 == i) {
|
||||
c = kQuotes.c;
|
||||
}
|
||||
}
|
||||
kRsv = (c - l) / (h - l) * 100;
|
||||
k = 2 / 3.0 * k + 1 / 3.0 * kRsv;
|
||||
}
|
||||
quotes.k = k;
|
||||
|
||||
//计算d
|
||||
if (i < dPeriod) {
|
||||
d = 50;
|
||||
} else {
|
||||
d = 2 / 3.0 * d + 1 / 3.0 * quotes.k;
|
||||
}
|
||||
quotes.d = d;
|
||||
|
||||
//计算j
|
||||
j = 3 * quotes.k - 2 * quotes.d;
|
||||
quotes.j = j;
|
||||
|
||||
//计算结束
|
||||
//打印测试
|
||||
//Log.e(TAG, "calculateKDJ: k:" + quotes.k + ",d:" + quotes.d + ",j:" + quotes.j);
|
||||
}
|
||||
}
|
||||
|
||||
public static void calculateMACD(List<Quotes> quotesList) {
|
||||
calculateMACD(quotesList, 12, 26, 9);
|
||||
}
|
||||
|
||||
/**
|
||||
* MACD(x,y,z),一般取MACD(12,26,9)。
|
||||
* MACD(x,y,z),x、y为平滑指数。z暂时不知道用处(不影响算法)。
|
||||
* `EMAx=((x-1)/(x+1.0)*前一日EMA)+2.0/(x+1)*今日收盘价`;其中第一日的EMA是当日的收盘价。
|
||||
* `EMA12=(11/13.0)*[前一日EMA12]+2.0/13*[今日quotes.c]`
|
||||
* `EMA26=(25/27.0)*[前一日EMA26]+2.0/27*[今日quotes.c]`
|
||||
* DIF:`DIF=EMA12-EMA26`
|
||||
* DEA:`DEA=8/10.0*(前一日的DEA)+2/10.0*今日DIF`
|
||||
* MACD:`2*(DIF-DEA)`
|
||||
*
|
||||
* @param quotesList 数据集合
|
||||
* @param d1 平滑指数,一般为12
|
||||
* @param d2 平滑指数,一般为26
|
||||
* @param z 暂时未知
|
||||
*/
|
||||
public static void calculateMACD(List<Quotes> quotesList, int d1, int d2, int z) {
|
||||
//容错
|
||||
if (quotesList == null || quotesList.isEmpty()) return;
|
||||
|
||||
double ema12 = 0;
|
||||
double ema26 = 0;
|
||||
double dif = 0;
|
||||
double dea = 0;
|
||||
double macd = 0;
|
||||
|
||||
for (int i = 0; i < quotesList.size(); i++) {
|
||||
Quotes quotes = quotesList.get(i);
|
||||
if (i == 0) {
|
||||
ema12 = quotes.c;
|
||||
ema26 = quotes.c;
|
||||
} else {
|
||||
ema12 = (d1 - 1) / (d1 + 1.0) * ema12 + 2.0 / (d1 + 1) * quotes.c;
|
||||
ema26 = (d2 - 1) / (d2 + 1.0) * ema26 + 2.0 / (d2 + 1) * quotes.c;
|
||||
}
|
||||
//计算dif
|
||||
dif = ema12 - ema26;
|
||||
|
||||
//计算dea
|
||||
if (i == 0) {
|
||||
dea = 0;
|
||||
} else {
|
||||
dea = dea * 8 / 10.0 + dif * 2 / 10.0;
|
||||
}
|
||||
|
||||
//计算macd
|
||||
macd = 2 * (dif - dea);
|
||||
|
||||
quotes.dif = dif;
|
||||
quotes.dea = dea;
|
||||
quotes.macd = macd;
|
||||
|
||||
//计算结束
|
||||
//打印日志
|
||||
//Log.e(TAG, "calculateMACD: dif:" + quotes.dif + ",dea:" + quotes.dea + ",macd:" + macd);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void calculateRSI(List<Quotes> quotesList) {
|
||||
calculateRSI(quotesList, 6);
|
||||
calculateRSI(quotesList, 12);
|
||||
calculateRSI(quotesList, 24);
|
||||
}
|
||||
|
||||
/**
|
||||
* 【该算法已核实】计算RSI。RSI(x,y,z),一般取RSI(6,12,24)。
|
||||
* RSI(x,y,z),x、y、z均为周期单位,计算算法一致,只是周期不同。
|
||||
* RSIx,在周期x内,upSum="在周期x内的上涨总点数",downSum="在周期x内的下跌总点数";`RSIx=upSum/(upSum+downSum)*100`;
|
||||
* 注意:RSIx对于最开始的x+1周期内,不存在对应RSI,在图像上表示就是不显示对应RSIx即可。
|
||||
*
|
||||
* @param quotes 对应的数据集合
|
||||
* @param period 周期
|
||||
*/
|
||||
public static void calculateRSI(List<Quotes> quotes, int period) {
|
||||
//容错
|
||||
if (quotes == null || quotes.isEmpty()) return;
|
||||
if (period <= 0) period = 6;
|
||||
//period单位的上涨点数
|
||||
double upSum = 0f;
|
||||
//period单位的下跌点数
|
||||
double downSum = 0f;
|
||||
//差值
|
||||
double dis;
|
||||
//最后计算的值
|
||||
double rsi;
|
||||
for (int i = 0; i < quotes.size(); i++) {
|
||||
Quotes q = quotes.get(i);
|
||||
if (i > 0) {
|
||||
dis = q.c - quotes.get(i - 1).c;
|
||||
if (dis >= 0) {
|
||||
upSum += dis;
|
||||
} else {
|
||||
downSum -= dis;
|
||||
}
|
||||
|
||||
//上面加,这里减。要保证累计的和周期为:period
|
||||
if (i + 1 > period) {
|
||||
dis = quotes.get(i - period + 1).c - quotes.get(i - period).c;
|
||||
if (dis >= 0) {
|
||||
upSum -= dis;
|
||||
} else {
|
||||
downSum += dis;
|
||||
}
|
||||
rsi = upSum / (upSum + downSum) * 100;
|
||||
if (period == 6) q.rsi6 = rsi;
|
||||
else if (period == 12) q.rsi12 = rsi;
|
||||
else if (period == 24) q.rsi24 = rsi;
|
||||
else Log.e(TAG, "calculateRSI: 不存在该周期:" + period);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 【该算法已核实】计算公式:MA =(C1+C2+C3+C4+C5+...+Cn)/n,其中C为收盘价n为移动平均周期数。
|
||||
* 例如现货黄金的5日移动平均价格计算方法为:MA5=(前四天收盘价+前三天收盘价+前天收盘价+昨天收盘价+今天收盘价)/5。
|
||||
* 特殊的,假如数据集合中最开始的n个数据,是没法计算MAn的。这里的处理方式是不计算,绘制时直接不绘制对应MA即可。
|
||||
*
|
||||
* @param quotesList 数据集合
|
||||
* @param period MAn中的n,周期,一般是:5、10、20、30、60。
|
||||
*/
|
||||
public static void calculateMA(List<Quotes> quotesList, int period, KViewType.MaType maType) {
|
||||
boolean isMaster = true;
|
||||
if (maType == KViewType.MaType.volMa5 || maType == KViewType.MaType.volMa10)
|
||||
isMaster = false;
|
||||
|
||||
if (quotesList == null || quotesList.isEmpty()) return;
|
||||
|
||||
if (!isMaster && quotesList.get(0).vol <= 0)
|
||||
throw new IllegalArgumentException("请确保设置vol参数");
|
||||
|
||||
if (period < 0 || period > quotesList.size() - 1) return;
|
||||
|
||||
//包含今日的n日和
|
||||
double sum = 0;
|
||||
for (int i = 0; i < quotesList.size(); i++) {
|
||||
//计算和
|
||||
Quotes quotes = quotesList.get(i);
|
||||
sum += isMaster ? quotes.c : quotes.vol;
|
||||
if (i > period - 1) {
|
||||
Quotes q = quotesList.get(i - period);
|
||||
sum -= isMaster ? q.c : q.vol;
|
||||
}
|
||||
//边界
|
||||
if (i < period - 1) {
|
||||
continue;
|
||||
}
|
||||
double result = sum / period;
|
||||
if (period == 5) {
|
||||
if (isMaster) quotes.ma5 = result;
|
||||
else quotes.volMa5 = result;
|
||||
} else if (period == 10) {
|
||||
if (isMaster) quotes.ma10 = result;
|
||||
else quotes.volMa10 = result;
|
||||
} else if (period == 20) {
|
||||
if (isMaster) quotes.ma20 = result;
|
||||
else {
|
||||
Log.e(TAG, "calculateMA: 没有该种period:" + period + "," + maType);
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "calculateMA: 没有该种period:" + period + "," + maType);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 【该算法已核实】BOLL(n)计算公式:
|
||||
* MA=n日内的收盘价之和÷n。
|
||||
* MD=n日的平方根(C-MA)的两次方之和除以n
|
||||
* MB=(n-1)日的MA
|
||||
* UP=MB+k×MD
|
||||
* DN=MB-k×MD
|
||||
* K为参数,可根据股票的特性来做相应的调整,一般默认为2
|
||||
*
|
||||
* @param quotesList 数据集合
|
||||
* @param period 周期,一般为26
|
||||
* @param k 参数,可根据股票的特性来做相应的调整,一般默认为2
|
||||
*/
|
||||
public static void calculateBOLL(List<Quotes> quotesList, int period, int k) {
|
||||
if (quotesList == null || quotesList.isEmpty()) return;
|
||||
if (period < 0 || period > quotesList.size() - 1) return;
|
||||
|
||||
double up;//上轨线
|
||||
double mb;//中轨线
|
||||
double dn;//下轨线
|
||||
|
||||
//n日
|
||||
double sum = 0;
|
||||
//n-1日
|
||||
double sum2 = 0;
|
||||
for (int i = 0; i < quotesList.size(); i++) {
|
||||
Quotes quotes = quotesList.get(i);
|
||||
sum += quotes.c;
|
||||
sum2 += quotes.c;
|
||||
if (i > period - 1)
|
||||
sum -= quotesList.get(i - period).c;
|
||||
if (i > period - 2)
|
||||
sum2 -= quotesList.get(i - period + 1).c;
|
||||
|
||||
//这个范围不计算,在View上的反应就是不显示这个范围的boll线
|
||||
if (i < period - 1)
|
||||
continue;
|
||||
|
||||
//n日MA
|
||||
double ma = sum / period;
|
||||
//n-1日MA
|
||||
double ma2 = sum2 / (period - 1);
|
||||
double md = 0;
|
||||
for (int j = i + 1 - period; j <= i; j++) {
|
||||
//n日
|
||||
md += Math.pow(quotesList.get(j).c - ma, 2);
|
||||
}
|
||||
md = Math.sqrt(md / period);
|
||||
//(n-1)日的MA
|
||||
mb = ma2;
|
||||
up = mb + k * md;
|
||||
dn = mb - k * md;
|
||||
|
||||
quotes.up = up;
|
||||
quotes.mb = mb;
|
||||
quotes.dn = dn;
|
||||
}
|
||||
}
|
||||
|
||||
public static void calculateBOLL(List<Quotes> quotesList) {
|
||||
calculateBOLL(quotesList, 26, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 找到单个报价中的最小值
|
||||
*
|
||||
* @param quotes
|
||||
* @param masterType
|
||||
* @return
|
||||
*/
|
||||
public static double getMasterMinY(Quotes quotes, KViewType.MasterIndicatrixType masterType) {
|
||||
double min = Integer.MAX_VALUE;
|
||||
//ma
|
||||
if (masterType == KViewType.MasterIndicatrixType.MA || masterType == KViewType.MasterIndicatrixType.MA_BOLL) {
|
||||
if (quotes.ma5 != 0 && quotes.ma5 < min) {
|
||||
min = quotes.ma5;
|
||||
}
|
||||
if (quotes.ma10 != 0 && quotes.ma10 < min) {
|
||||
min = quotes.ma10;
|
||||
}
|
||||
if (quotes.ma20 != 0 && quotes.ma20 < min) {
|
||||
min = quotes.ma20;
|
||||
}
|
||||
}
|
||||
//boll
|
||||
if (masterType == KViewType.MasterIndicatrixType.BOLL || masterType == KViewType.MasterIndicatrixType.MA_BOLL) {
|
||||
//boll
|
||||
if (quotes.mb != 0 && quotes.mb < min) {
|
||||
min = quotes.mb;
|
||||
}
|
||||
if (quotes.up != 0 && quotes.up < min) {
|
||||
min = quotes.up;
|
||||
}
|
||||
if (quotes.dn != 0 && quotes.dn < min) {
|
||||
min = quotes.dn;
|
||||
}
|
||||
}
|
||||
//quotes
|
||||
if (quotes.l != 0 && quotes.l < min) {
|
||||
min = quotes.l;
|
||||
}
|
||||
//没有找到
|
||||
if (min == Integer.MAX_VALUE) {
|
||||
min = 0;
|
||||
}
|
||||
return min;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 找到单个报价中的最大值
|
||||
*
|
||||
* @param quotes
|
||||
* @param masterType
|
||||
* @return
|
||||
*/
|
||||
public static double getMasterMaxY(Quotes quotes, KViewType.MasterIndicatrixType masterType) {
|
||||
double max = Integer.MIN_VALUE;
|
||||
//ma
|
||||
//只有在存在ma的情况下才计算
|
||||
if (masterType == KViewType.MasterIndicatrixType.MA || masterType == KViewType.MasterIndicatrixType.MA_BOLL) {
|
||||
if (quotes.ma5 != 0 && quotes.ma5 > max) {
|
||||
max = quotes.ma5;
|
||||
}
|
||||
if (quotes.ma10 != 0 && quotes.ma10 > max) {
|
||||
max = quotes.ma10;
|
||||
}
|
||||
if (quotes.ma20 != 0 && quotes.ma20 > max) {
|
||||
max = quotes.ma20;
|
||||
}
|
||||
}
|
||||
|
||||
//boll
|
||||
if (masterType == KViewType.MasterIndicatrixType.BOLL || masterType == KViewType.MasterIndicatrixType.MA_BOLL) {
|
||||
if (quotes.mb != 0 && quotes.mb > max) {
|
||||
max = quotes.mb;
|
||||
}
|
||||
if (quotes.up != 0 && quotes.up > max) {
|
||||
max = quotes.up;
|
||||
}
|
||||
if (quotes.dn != 0 && quotes.dn > max) {
|
||||
max = quotes.dn;
|
||||
}
|
||||
}
|
||||
//quotes
|
||||
if (quotes.h != 0 && quotes.h > max) {
|
||||
max = quotes.h;
|
||||
}
|
||||
//没有找到
|
||||
if (max == Integer.MIN_VALUE) {
|
||||
max = 0;
|
||||
}
|
||||
return max;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 副图:找到单个报价中的最小值
|
||||
*
|
||||
* @param quotes
|
||||
* @param minorType
|
||||
* @return
|
||||
*/
|
||||
public static double getMinorMinY(Quotes quotes, KViewType.MinorIndicatrixType minorType) {
|
||||
double min = Integer.MAX_VALUE;
|
||||
//macd
|
||||
if (minorType == KViewType.MinorIndicatrixType.MACD) {
|
||||
if (quotes.dif != 0 && quotes.dif < min) {
|
||||
min = quotes.dif;
|
||||
}
|
||||
if (quotes.dea != 0 && quotes.dea < min) {
|
||||
min = quotes.dea;
|
||||
}
|
||||
if (quotes.macd != 0 && quotes.macd < min) {
|
||||
min = quotes.macd;
|
||||
}
|
||||
}
|
||||
//RSI
|
||||
if (minorType == KViewType.MinorIndicatrixType.RSI) {
|
||||
if (quotes.rsi6 != 0 && quotes.rsi6 < min) {
|
||||
min = quotes.rsi6;
|
||||
}
|
||||
if (quotes.rsi12 != 0 && quotes.rsi12 < min) {
|
||||
min = quotes.rsi12;
|
||||
}
|
||||
if (quotes.rsi24 != 0 && quotes.rsi24 < min) {
|
||||
min = quotes.rsi24;
|
||||
}
|
||||
}
|
||||
//KDJ
|
||||
if (minorType == KViewType.MinorIndicatrixType.KDJ) {
|
||||
if (quotes.k != 0 && quotes.k < min) {
|
||||
min = quotes.k;
|
||||
}
|
||||
if (quotes.d != 0 && quotes.d < min) {
|
||||
min = quotes.d;
|
||||
}
|
||||
if (quotes.j != 0 && quotes.j < min) {
|
||||
min = quotes.j;
|
||||
}
|
||||
}
|
||||
//没有找到
|
||||
if (min == Integer.MAX_VALUE) {
|
||||
min = 0;
|
||||
}
|
||||
return min;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 副图:找到单个报价中的最大值
|
||||
*
|
||||
* @param quotes
|
||||
* @param minorType
|
||||
* @return
|
||||
*/
|
||||
public static double getMinorMaxY(Quotes quotes, KViewType.MinorIndicatrixType minorType) {
|
||||
double max = Integer.MIN_VALUE;
|
||||
//macd
|
||||
if (minorType == KViewType.MinorIndicatrixType.MACD) {
|
||||
if (quotes.dif != 0 && quotes.dif > max) {
|
||||
max = quotes.dif;
|
||||
}
|
||||
if (quotes.dea != 0 && quotes.dea > max) {
|
||||
max = quotes.dea;
|
||||
}
|
||||
if (quotes.macd != 0 && quotes.macd > max) {
|
||||
max = quotes.macd;
|
||||
}
|
||||
}
|
||||
//RSI
|
||||
if (minorType == KViewType.MinorIndicatrixType.RSI) {
|
||||
if (quotes.rsi6 != 0 && quotes.rsi6 > max) {
|
||||
max = quotes.rsi6;
|
||||
}
|
||||
if (quotes.rsi12 != 0 && quotes.rsi12 > max) {
|
||||
max = quotes.rsi12;
|
||||
}
|
||||
if (quotes.rsi24 != 0 && quotes.rsi24 > max) {
|
||||
max = quotes.rsi24;
|
||||
}
|
||||
}
|
||||
//KDJ
|
||||
if (minorType == KViewType.MinorIndicatrixType.KDJ) {
|
||||
if (quotes.k != 0 && quotes.k > max) {
|
||||
max = quotes.k;
|
||||
}
|
||||
if (quotes.d != 0 && quotes.d > max) {
|
||||
max = quotes.d;
|
||||
}
|
||||
if (quotes.j != 0 && quotes.j > max) {
|
||||
max = quotes.j;
|
||||
}
|
||||
}
|
||||
//没有找到
|
||||
if (max == Integer.MIN_VALUE) {
|
||||
max = 0;
|
||||
}
|
||||
return max;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 量图:找到最大值
|
||||
*
|
||||
* @param quotes
|
||||
* @return
|
||||
*/
|
||||
public static double getVolMaxY(Quotes quotes) {
|
||||
double max = Integer.MIN_VALUE;
|
||||
//vol、volma5,volma10
|
||||
if (quotes.vol != 0 && quotes.vol > max) {
|
||||
max = quotes.vol;
|
||||
}
|
||||
if (quotes.volMa5 != 0 && quotes.volMa5 > max) {
|
||||
max = quotes.volMa5;
|
||||
}
|
||||
if (quotes.volMa10 != 0 && quotes.volMa10 > max) {
|
||||
max = quotes.volMa10;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* 量图:找到最小值
|
||||
*
|
||||
* @param quotes
|
||||
* @return
|
||||
*/
|
||||
public static double getVolMinY(Quotes quotes) {
|
||||
double min = Integer.MAX_VALUE;
|
||||
//vol、volma5,volma10
|
||||
if (quotes.vol != 0 && quotes.vol < min) {
|
||||
min = quotes.vol;
|
||||
}
|
||||
if (quotes.volMa5 != 0 && quotes.volMa5 < min) {
|
||||
min = quotes.volMa5;
|
||||
}
|
||||
if (quotes.volMa10 != 0 && quotes.volMa10 < min) {
|
||||
min = quotes.volMa10;
|
||||
}
|
||||
return min;
|
||||
}
|
||||
}
|
||||
+1126
File diff suppressed because it is too large
Load Diff
+204
@@ -0,0 +1,204 @@
|
||||
package com.tophold.trade.view.kview;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
* 作 者 : wgyscsf@163.com
|
||||
* 创建日期 :2018/03/03 15:38
|
||||
* 描 述 :KView,包含主图和副图,包括手势、加载数据等
|
||||
* ============================================================
|
||||
**/
|
||||
public class KLayoutView extends LinearLayout {
|
||||
public static String TAG;
|
||||
public static final float DEF_MINORHRATIO = 0.25f;
|
||||
public static final float DEF_VOLHRATIO = 0.25f;
|
||||
protected MasterView mMasterView;
|
||||
protected MinorView mMinorView;
|
||||
protected VolView mVolView;
|
||||
//副图高度占全部高度比
|
||||
protected float mMinorHRatio = DEF_MINORHRATIO;
|
||||
//量图高度占全部高度比
|
||||
protected float mVolHRatio = DEF_VOLHRATIO;
|
||||
//是否展示副图
|
||||
protected boolean isShowMinor = true;
|
||||
//是否展示量图
|
||||
protected boolean isShowVol = false;
|
||||
|
||||
public KLayoutView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public KLayoutView(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public KLayoutView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
TAG = this.getClass().getSimpleName();
|
||||
layoutViews();
|
||||
initDefAttrs();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
super.onLayout(changed, l, t, r, b);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void initDefAttrs() {
|
||||
setShowMinor(true);
|
||||
setShowVol(false);
|
||||
}
|
||||
|
||||
private void layoutViews() {
|
||||
setOrientation(VERTICAL);
|
||||
|
||||
mMasterView = new MasterView(getContext());
|
||||
// mMasterView.setBackgroundColor(getResources().getColor(R.color.color_fundView_brokenLineColor));
|
||||
mMinorView = new MinorView(getContext());
|
||||
// mMinorView.setBackgroundColor(getResources().getColor(R.color.color_fundView_xLineColor));
|
||||
|
||||
mVolView = new VolView(getContext());
|
||||
|
||||
//测量高度
|
||||
measureHeight();
|
||||
|
||||
addView(mMasterView);
|
||||
addView(mMinorView);
|
||||
addView(mVolView);
|
||||
|
||||
mMasterView.setMinorListener(mMinorView.getMinorListener());
|
||||
mMasterView.setVolListener(mVolView.getVolListener());
|
||||
}
|
||||
|
||||
private void measureHeight() {
|
||||
LayoutParams params = new LayoutParams(
|
||||
LayoutParams.MATCH_PARENT, 0);
|
||||
params.weight = 1 - mMinorHRatio - mVolHRatio;
|
||||
mMasterView.setLayoutParams(params);
|
||||
|
||||
|
||||
LayoutParams params2 = new LayoutParams(
|
||||
LayoutParams.MATCH_PARENT, 0);
|
||||
params2.weight = mMinorHRatio;
|
||||
mMinorView.setLayoutParams(params2);
|
||||
|
||||
LayoutParams params3 = new LayoutParams(
|
||||
LayoutParams.MATCH_PARENT, 0);
|
||||
params3.weight = mVolHRatio;
|
||||
mVolView.setLayoutParams(params3);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------对开发者暴露可以修改的参数-------
|
||||
public MasterView getMasterView() {
|
||||
return mMasterView;
|
||||
}
|
||||
|
||||
public KLayoutView setMasterView(MasterView masterView) {
|
||||
mMasterView = masterView;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MinorView getMinorView() {
|
||||
return mMinorView;
|
||||
}
|
||||
|
||||
public KLayoutView setMinorView(MinorView minorView) {
|
||||
mMinorView = minorView;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getMinorHRatio() {
|
||||
return mMinorHRatio;
|
||||
}
|
||||
|
||||
public void setMinorHRatio(float minorHRatio) {
|
||||
mMinorHRatio = minorHRatio;
|
||||
}
|
||||
|
||||
|
||||
public boolean isShowMinor() {
|
||||
return isShowMinor;
|
||||
}
|
||||
|
||||
public VolView getVolView() {
|
||||
return mVolView;
|
||||
}
|
||||
|
||||
public KLayoutView setVolView(VolView volView) {
|
||||
mVolView = volView;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getVolHRatio() {
|
||||
return mVolHRatio;
|
||||
}
|
||||
|
||||
public KLayoutView setVolHRatio(float volHRatio) {
|
||||
mVolHRatio = volHRatio;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isShowVol() {
|
||||
return isShowVol;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private KLayoutView reloadHeight() {
|
||||
//为什么要这样刷新?先重新测量主图和副图的高度,然后再去测量各自的seekAndCalculateCellData
|
||||
measureHeight();
|
||||
|
||||
postDelayed(() -> {
|
||||
mMasterView.seekAndCalculateCellData();
|
||||
mMinorView.seekAndCalculateCellData();
|
||||
mVolView.seekAndCalculateCellData();
|
||||
}, 300);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 这个问题比较奇葩。主要原因是:整个View重新布局了大小,等于重新走了生命周期,导致各种问题。
|
||||
* 之前一般都是只是走onDraw()方法,所以基本不会有什么问题。
|
||||
* 现在是高度变了,因此必须重新测量。
|
||||
* 现在下面这种实现方式仍然会有视觉上的延迟,体验不太好,暂时没有好评的解决方案。
|
||||
* 另外:平时基本不会这样直接的主图副图显示不显示来回切换。如果在初始化的时候就设置好,不会有任何问题。
|
||||
*
|
||||
* @param showMinor
|
||||
*/
|
||||
public KLayoutView setShowMinor(boolean showMinor) {
|
||||
isShowMinor = showMinor;
|
||||
if (!isShowMinor) {
|
||||
mMinorHRatio = 0;
|
||||
} else {
|
||||
mMinorHRatio = DEF_MINORHRATIO;
|
||||
}
|
||||
return reloadHeight();
|
||||
}
|
||||
|
||||
public KLayoutView setShowVol(boolean showVol) {
|
||||
isShowVol = showVol;
|
||||
if (!isShowVol) {
|
||||
mVolHRatio = 0;
|
||||
} else {
|
||||
mVolHRatio = DEF_VOLHRATIO;
|
||||
}
|
||||
Log.d(TAG, "setShowVol: " + isShowVol);
|
||||
mVolView.setShowVol(isShowVol);
|
||||
return reloadHeight();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
package com.tophold.trade.view.kview;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.tophold.trade.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
* 作 者 : wgyscsf@163.com
|
||||
* 创建日期 :2018/03/04 11:06
|
||||
* 描 述 :KView入口
|
||||
* ============================================================
|
||||
**/
|
||||
public final class KView extends KLayoutView {
|
||||
|
||||
//主图展示的是蜡烛图还是分时图
|
||||
protected boolean isShowTimSharing = true;
|
||||
//设置数据精度
|
||||
protected int mDigit = 4;
|
||||
|
||||
public KView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public KView(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public KView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initListener();
|
||||
}
|
||||
|
||||
|
||||
private void initListener() {
|
||||
mMasterView.setMaxPostionListener((quotes, x, y) -> {
|
||||
Log.d(TAG, "initListener1: " + x + "," + y);
|
||||
});
|
||||
mMasterView.setMinPostionListener((quotes, x, y) -> {
|
||||
Log.d(TAG, "initListener2: " + x + "," + y);
|
||||
|
||||
});
|
||||
mMasterView.setLastPostionListener((quotes, x, y) -> {
|
||||
Log.d(TAG, "initListener3: " + x + "," + y);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据设置入口
|
||||
*
|
||||
* @param quotesList
|
||||
*/
|
||||
public void setKViewData(List<Quotes> quotesList, KViewListener.MasterTouchListener masterTouchListener) {
|
||||
if (quotesList == null || quotesList.isEmpty()) {
|
||||
Toast.makeText(getContext(), "数据异常1111", Toast.LENGTH_SHORT).show();
|
||||
Log.e(TAG, "setKViewData: 数据异常111");
|
||||
return;
|
||||
}
|
||||
mMasterView.setTimeSharingData(quotesList, masterTouchListener);
|
||||
mMinorView.setTimeSharingData(quotesList, masterTouchListener);
|
||||
mVolView.setTimeSharingData(quotesList, masterTouchListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* 实时推送过来的数据,实时更新。
|
||||
* 这个地方可以优化:因为用户不知道什么时候可以Push过来数据,如果不处理。可能存在一种情况:数据还没加载完毕,push就过来了就会出现异常。
|
||||
*
|
||||
* @param quotes
|
||||
*/
|
||||
public void pushKViewData(Quotes quotes, long period) {
|
||||
if (quotes == null) {
|
||||
//Toast.makeText(getContext(), "数据异常", Toast.LENGTH_SHORT).show();
|
||||
//Log.e(TAG, "setKViewData: 数据异常");
|
||||
return;
|
||||
}
|
||||
mMasterView.pushingTimeSharingData(quotes, period);
|
||||
mMinorView.pushingTimeSharingData(quotes, period);
|
||||
mVolView.pushingTimeSharingData(quotes, period);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载更多数据
|
||||
*
|
||||
* @param quotesList
|
||||
*/
|
||||
public void loadKViewData(List<Quotes> quotesList) {
|
||||
if (quotesList == null || quotesList.isEmpty()) {
|
||||
Toast.makeText(getContext(), "数据异常", Toast.LENGTH_SHORT).show();
|
||||
Log.e(TAG, "setKViewData: 数据异常");
|
||||
return;
|
||||
}
|
||||
mMasterView.loadMoreTimeSharingData(quotesList);
|
||||
mMinorView.loadMoreTimeSharingData(quotesList);
|
||||
mVolView.loadMoreTimeSharingData(quotesList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载更多失败,在这里添加逻辑
|
||||
*/
|
||||
public void loadMoreError() {
|
||||
mMasterView.loadMoreError();
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载更多成功,在这里添加逻辑
|
||||
*/
|
||||
public void loadMoreSuccess() {
|
||||
mMasterView.loadMoreSuccess();
|
||||
}
|
||||
|
||||
/**
|
||||
* 正在加载更多,在这里添加逻辑
|
||||
*/
|
||||
public void loadMoreIng() {
|
||||
mMasterView.loadMoreIng();
|
||||
}
|
||||
|
||||
/**
|
||||
* 没有更多数据,在这里添加逻辑
|
||||
*/
|
||||
public void loadMoreNoData() {
|
||||
mMasterView.loadMoreNoData();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------对开发者暴露可以修改的参数-------
|
||||
|
||||
public boolean isShowTimSharing() {
|
||||
return isShowTimSharing;
|
||||
}
|
||||
|
||||
public void setShowTimSharing(boolean showTimSharing) {
|
||||
isShowTimSharing = showTimSharing;
|
||||
mMasterView.setViewType(showTimSharing ? KViewType.MasterViewType.TIMESHARING : KViewType.MasterViewType.CANDLE);
|
||||
}
|
||||
|
||||
public int getDigit() {
|
||||
return mDigit;
|
||||
}
|
||||
|
||||
public void setDigit(int digit) {
|
||||
mDigit = digit;
|
||||
mMasterView.setDigits(mDigit);
|
||||
mMinorView.setDigits(mDigit);
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package com.tophold.trade.view.kview;
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
* 作 者 : wgyscsf@163.com
|
||||
* 更新时间 :2018/04/04 18:12
|
||||
* 描 述 :
|
||||
* ============================================================
|
||||
*/
|
||||
public class KViewListener {
|
||||
/**
|
||||
* 主图的长按、加载更多回调
|
||||
*/
|
||||
public interface MasterTouchListener {
|
||||
void onLongTouch(Quotes preQuotes, Quotes currentQuotes);
|
||||
|
||||
void onUnLongTouch();
|
||||
|
||||
void needLoadMore();
|
||||
}
|
||||
|
||||
/**
|
||||
* 主图的监听,主要供副图使用,把这些数据回调给副图,避免副图再做复杂重复的操作。
|
||||
*/
|
||||
public interface MinorListener {
|
||||
/**
|
||||
* 长按操作
|
||||
*
|
||||
* @param pressIndex 按下所对应的索引
|
||||
* @param currQuotes 按下所对应的点
|
||||
*/
|
||||
void masterLongPressListener(int pressIndex, Quotes currQuotes);
|
||||
|
||||
/**
|
||||
* 不再长按回调
|
||||
*/
|
||||
void masterNoLongPressListener();
|
||||
|
||||
/**
|
||||
* 缩放
|
||||
*
|
||||
* @param beginIndex 缩放后的起始位置索引
|
||||
* @param endIndex 缩放后的结束索引
|
||||
* @param shownMaxCount 可见数据总条数
|
||||
*/
|
||||
void masteZoomlNewIndex(int beginIndex, int endIndex, int shownMaxCount);
|
||||
|
||||
/**
|
||||
* 左右滑动
|
||||
*
|
||||
* @param endIndex 滑动后的结束索引
|
||||
* @param currPullType 当前PullType类型
|
||||
* @param shownMaxCount
|
||||
* @param shownMaxCount 可见数据总条数
|
||||
*/
|
||||
void mastelPullmNewIndex(int beginIndex, int endIndex, KViewType.PullType currPullType, int shownMaxCount);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 关键点的监听:可视范围内最大值点、可视范围内最小值点、最后一个点监听(数据集合的最后一个点)
|
||||
*/
|
||||
public interface PostionListner {
|
||||
void postion(Quotes quotes, float x, float y);
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package com.tophold.trade.view.kview;
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
* 作 者 : wgyscsf@163.com
|
||||
* 更新时间 :2018/04/04 17:04
|
||||
* 描 述 :
|
||||
* ============================================================
|
||||
*/
|
||||
public class KViewType {
|
||||
/**
|
||||
* 主图类型:分时图还是蜡烛图
|
||||
*/
|
||||
public enum MasterViewType {
|
||||
TIMESHARING,
|
||||
CANDLE
|
||||
}
|
||||
|
||||
/**
|
||||
* 拖拽类型
|
||||
*/
|
||||
protected enum PullType {
|
||||
PULL_RIGHT,//向右滑动
|
||||
PULL_LEFT,//向左滑动
|
||||
PULL_RIGHT_STOP,//滑动到最右边
|
||||
PULL_LEFT_STOP,//滑动到最左边
|
||||
}
|
||||
|
||||
/**
|
||||
* 主图上面的技术指标类型
|
||||
*/
|
||||
public enum MasterIndicatrixType {
|
||||
NONE,//无
|
||||
MA,//MA5、10、20
|
||||
BOLL,//BOLL(26)
|
||||
MA_BOLL//MA5、10、20和BOLL(26)同时展示
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 主图上面的详细技术指标类型,主要用于判断何种具体的线,进行相应的处理
|
||||
*/
|
||||
public enum MasterIndicatrixDetailType {
|
||||
MA5,
|
||||
MA10,
|
||||
MA20,
|
||||
BOLLUP,
|
||||
BOLLMB,
|
||||
BOLLDN
|
||||
}
|
||||
|
||||
/**
|
||||
* 副图上面的技术指标类型
|
||||
*/
|
||||
public enum MinorIndicatrixType {
|
||||
MACD,
|
||||
RSI,
|
||||
KDJ
|
||||
}
|
||||
|
||||
/**
|
||||
* 滑动的类型
|
||||
*/
|
||||
public enum MoveType {
|
||||
STEP,//一点一点移动
|
||||
ONFLING//具有onfling效果
|
||||
}
|
||||
|
||||
/**
|
||||
* MA类型:主图:ma5,ma10,ma20;量图:ma5,ma10
|
||||
*/
|
||||
public enum MaType {
|
||||
ma5,//主图ma5
|
||||
ma10,//主图ma10
|
||||
ma20,//主图ma20
|
||||
volMa5,//量图ma5
|
||||
volMa10//量图ma10
|
||||
}
|
||||
}
|
||||
+1850
File diff suppressed because it is too large
Load Diff
+889
@@ -0,0 +1,889 @@
|
||||
package com.tophold.trade.view.kview;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
|
||||
import com.tophold.trade.R;
|
||||
import com.tophold.trade.utils.FormatUtil;
|
||||
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
* 作 者 : wgyscsf@163.com
|
||||
* 创建日期 :2017/12/14 17:56
|
||||
* 描 述 :副图。该view提供各种副图指标。
|
||||
* ============================================================
|
||||
**/
|
||||
public class MinorView extends KBaseView {
|
||||
|
||||
|
||||
/**
|
||||
* 初始化所有需要的颜色资源
|
||||
*/
|
||||
//共有的
|
||||
int mOuterStrokeColor;
|
||||
int mInnerXyDashColor;
|
||||
int mXyTxtColor;
|
||||
int mLegendTxtColor;
|
||||
int mLongPressTxtColor;
|
||||
|
||||
//macd
|
||||
int mMacdBuyColor;
|
||||
int mMacdSellColor;
|
||||
int mMacdDifColor;
|
||||
int mMacdDeaColor;
|
||||
int mMacdMacdColor;
|
||||
Paint mMacdPaint;
|
||||
float mMacdLineWidth = 1;
|
||||
|
||||
|
||||
//rsi
|
||||
int mRsi6Color;
|
||||
int mRsi12Color;
|
||||
int mRsi24Color;
|
||||
Paint mRsiPaint;
|
||||
float mRsiLineWidth = 1;
|
||||
|
||||
//kdj
|
||||
int mKColor;
|
||||
int mDColor;
|
||||
int mJColor;
|
||||
Paint mKdjPaint;
|
||||
float mKdjLineWidth = 1;
|
||||
|
||||
//当前显示的指标
|
||||
KViewType.MinorIndicatrixType mMinorType = KViewType.MinorIndicatrixType.MACD;
|
||||
|
||||
//y轴上最大值和最小值
|
||||
protected double mMinY;
|
||||
protected double mMaxY;
|
||||
//蜡烛图间隙,大小以单个蜡烛图的宽度的比例算。可修改。
|
||||
protected float mCandleDiverWidthRatio = 0.1f;
|
||||
|
||||
//监听主图的长按事件
|
||||
private KViewListener.MinorListener mMinorListener;
|
||||
|
||||
public MinorView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public MinorView(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public MinorView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initAttrs();
|
||||
initListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
Log.d(TAG, "onDraw: " + mBaseHeight + "," + mBaseWidth);
|
||||
if (mQuotesList == null || mQuotesList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
//绘制右侧文字
|
||||
drawYRightTxt(canvas);
|
||||
//绘制图例
|
||||
drawLegend(canvas);
|
||||
//绘制核心指标线
|
||||
drawMinorIndicatrix(canvas);
|
||||
//绘制长按线
|
||||
drawLongPress(canvas);
|
||||
}
|
||||
|
||||
private void initAttrs() {
|
||||
initDefAttrs();
|
||||
initColorRes();
|
||||
initMacdPaint();
|
||||
initRsiPaint();
|
||||
initKdjPaint();
|
||||
|
||||
}
|
||||
|
||||
private void initKdjPaint() {
|
||||
mKdjPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mKdjPaint.setColor(mKColor);
|
||||
mKdjPaint.setAntiAlias(true);
|
||||
mKdjPaint.setStrokeWidth(mKdjLineWidth);
|
||||
mKdjPaint.setStyle(Paint.Style.STROKE);
|
||||
mKdjPaint.setTextSize(mLegendTxtSize);
|
||||
}
|
||||
|
||||
private void initRsiPaint() {
|
||||
mRsiPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mRsiPaint.setColor(mRsi6Color);
|
||||
mRsiPaint.setAntiAlias(true);
|
||||
mRsiPaint.setStrokeWidth(mRsiLineWidth);
|
||||
mRsiPaint.setStyle(Paint.Style.STROKE);
|
||||
mRsiPaint.setTextSize(mLegendTxtSize);
|
||||
}
|
||||
|
||||
private void initMacdPaint() {
|
||||
mMacdPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mMacdPaint.setColor(mMacdBuyColor);
|
||||
mMacdPaint.setAntiAlias(true);
|
||||
mMacdPaint.setStrokeWidth(mMacdLineWidth);
|
||||
mMacdPaint.setTextSize(mLegendTxtSize);
|
||||
}
|
||||
|
||||
private void initDefAttrs() {
|
||||
//重写内边距大小
|
||||
mInnerTopBlankPadding = 8;
|
||||
mInnerBottomBlankPadding = 8;
|
||||
|
||||
//重写Legend padding
|
||||
mLegendPaddingTop = 0;
|
||||
mLegendPaddingRight = 4;
|
||||
mLegendPaddingLeft = 4;
|
||||
|
||||
|
||||
setShowInnerX(false);
|
||||
setShowInnerY(true);
|
||||
}
|
||||
|
||||
private void initColorRes() {
|
||||
//颜色
|
||||
mOuterStrokeColor = getColor(R.color.color_minorView_outerStrokeColor);
|
||||
mInnerXyDashColor = getColor(R.color.color_minorView_innerXyDashColor);
|
||||
mXyTxtColor = getColor(R.color.color_minorView_xYTxtColor);
|
||||
mLegendTxtColor = getColor(R.color.color_minorView_legendTxtColor);
|
||||
mLongPressTxtColor = getColor(R.color.color_minorView_longPressTxtColor);
|
||||
mMacdBuyColor = getColor(R.color.color_minorView_macdBuyColor);
|
||||
mMacdSellColor = getColor(R.color.color_minorView_macdSellColor);
|
||||
mMacdDifColor = getColor(R.color.color_minorView_macdDifColor);
|
||||
mMacdDeaColor = getColor(R.color.color_minorView_macdDeaColor);
|
||||
mMacdMacdColor = getColor(R.color.color_minorView_macdMacdColor);
|
||||
mRsi6Color = getColor(R.color.color_minorView_rsi6Color);
|
||||
mRsi12Color = getColor(R.color.color_minorView_rsi12Color);
|
||||
mRsi24Color = getColor(R.color.color_minorView_rsi24Color);
|
||||
mKColor = getColor(R.color.color_minorView_kColor);
|
||||
mDColor = getColor(R.color.color_minorView_dColor);
|
||||
mJColor = getColor(R.color.color_minorView_jColor);
|
||||
}
|
||||
|
||||
private void initListener() {
|
||||
mMinorListener = new KViewListener.MinorListener() {
|
||||
@Override
|
||||
public void masterLongPressListener(int pressIndex, Quotes currQuotes) {
|
||||
mDrawLongPress = true;
|
||||
mCurrLongPressQuotes = mQuotesList.get(pressIndex);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void masterNoLongPressListener() {
|
||||
mDrawLongPress = false;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void masteZoomlNewIndex(int beginIndex, int endIndex, int shownMaxCount) {
|
||||
mBeginIndex = beginIndex;
|
||||
mEndIndex = endIndex;
|
||||
mShownMaxCount = shownMaxCount;
|
||||
seekAndCalculateCellData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mastelPullmNewIndex(int beginIndex, int endIndex, KViewType.PullType currPullType, int shownMaxCount) {
|
||||
mBeginIndex = beginIndex;
|
||||
mEndIndex = endIndex;
|
||||
mShownMaxCount = shownMaxCount;
|
||||
mPullType = currPullType;
|
||||
|
||||
//处理右侧内边距
|
||||
if (currPullType == KViewType.PullType.PULL_RIGHT_STOP) {
|
||||
//重置到之前的状态
|
||||
mInnerRightBlankPadding = DEF_INNER_RIGHT_BLANK_PADDING;
|
||||
} else {
|
||||
mInnerRightBlankPadding = 0;
|
||||
}
|
||||
|
||||
seekAndCalculateCellData();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void drawLegend(Canvas canvas) {
|
||||
//绘制非按下情况下图例
|
||||
drawNoPressLegend(canvas);
|
||||
//绘制按下的图例
|
||||
drawPressLegend(canvas);
|
||||
}
|
||||
|
||||
private void drawMinorIndicatrix(Canvas canvas) {
|
||||
mMacdPaint.setStyle(Paint.Style.STROKE);
|
||||
mRsiPaint.setStyle(Paint.Style.STROKE);
|
||||
mKdjPaint.setStyle(Paint.Style.STROKE);
|
||||
if (mMinorType == KViewType.MinorIndicatrixType.MACD) {
|
||||
drawMACD(canvas);
|
||||
} else if (mMinorType == KViewType.MinorIndicatrixType.RSI) {
|
||||
drawRSI(canvas);
|
||||
} else if (mMinorType == KViewType.MinorIndicatrixType.KDJ) {
|
||||
drawKDJ(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawLongPress(Canvas canvas) {
|
||||
if (!mDrawLongPress) return;
|
||||
if (mCurrLongPressQuotes == null) return;
|
||||
|
||||
//y轴线
|
||||
canvas.drawLine(mCurrLongPressQuotes.floatX, mBasePaddingTop, mCurrLongPressQuotes.floatX,
|
||||
mBaseHeight - mBasePaddingBottom, mLongPressPaint);
|
||||
}
|
||||
|
||||
private void drawPressLegend(Canvas canvas) {
|
||||
if (!mDrawLongPress) return;
|
||||
|
||||
mMacdPaint.setStyle(Paint.Style.FILL);
|
||||
mRsiPaint.setStyle(Paint.Style.FILL);
|
||||
mKdjPaint.setStyle(Paint.Style.FILL);
|
||||
|
||||
float x = (float) (mLegendPaddingLeft + mBasePaddingLeft);
|
||||
float y = (float) (mLegendPaddingTop + mBasePaddingTop) + getFontHeight(mLegendTxtSize, mMacdPaint);
|
||||
|
||||
String showTxt;
|
||||
switch (mMinorType) {
|
||||
case MACD:
|
||||
showTxt = "DIFF:" + FormatUtil.numFormat(mCurrLongPressQuotes.dif, mDigits) + " ";
|
||||
mMacdPaint.setColor(mMacdDifColor);
|
||||
canvas.drawText(showTxt, x,
|
||||
y, mMacdPaint);
|
||||
|
||||
float leftWidth11 = mMacdPaint.measureText(showTxt);
|
||||
showTxt = "DEA:" + FormatUtil.numFormat(mCurrLongPressQuotes.dea, mDigits) + " ";
|
||||
mMacdPaint.setColor(mMacdDeaColor);
|
||||
canvas.drawText(showTxt, x + leftWidth11, y, mMacdPaint);
|
||||
|
||||
float leftWidth12 = mMacdPaint.measureText(showTxt);
|
||||
showTxt = "MACD:" + FormatUtil.numFormat(mCurrLongPressQuotes.macd, mDigits) + " ";
|
||||
mMacdPaint.setColor(mMacdMacdColor);
|
||||
canvas.drawText(showTxt, (x + leftWidth11 + leftWidth12),
|
||||
y, mMacdPaint);
|
||||
break;
|
||||
case RSI:
|
||||
showTxt = "RSI6:" + FormatUtil.numFormat(mCurrLongPressQuotes.rsi6, mDigits) + " ";
|
||||
mRsiPaint.setColor(mRsi6Color);
|
||||
canvas.drawText(showTxt, (x),
|
||||
y, mRsiPaint);
|
||||
|
||||
float leftWidth21 = mRsiPaint.measureText(showTxt);
|
||||
showTxt = "RSI12:" + FormatUtil.numFormat(mCurrLongPressQuotes.rsi12, mDigits) + " ";
|
||||
mRsiPaint.setColor(mRsi12Color);
|
||||
canvas.drawText(showTxt, (x + leftWidth21),
|
||||
y, mRsiPaint);
|
||||
|
||||
float leftWidth22 = mRsiPaint.measureText(showTxt);
|
||||
showTxt = "RSI24:" + FormatUtil.numFormat(mCurrLongPressQuotes.rsi24, mDigits) + " ";
|
||||
mRsiPaint.setColor(mRsi24Color);
|
||||
canvas.drawText(showTxt, (x + leftWidth21 + leftWidth22),
|
||||
y, mRsiPaint);
|
||||
break;
|
||||
case KDJ:
|
||||
showTxt = "K:" + FormatUtil.numFormat(mCurrLongPressQuotes.k, mDigits) + " ";
|
||||
mKdjPaint.setColor(mKColor);
|
||||
canvas.drawText(showTxt, (x),
|
||||
y, mKdjPaint);
|
||||
|
||||
float leftWidth = mKdjPaint.measureText(showTxt);
|
||||
showTxt = "D:" + FormatUtil.numFormat(mCurrLongPressQuotes.d, mDigits) + " ";
|
||||
mKdjPaint.setColor(mDColor);
|
||||
canvas.drawText(showTxt, (x + leftWidth),
|
||||
y, mKdjPaint);
|
||||
|
||||
float leftWidth2 = mKdjPaint.measureText(showTxt);
|
||||
showTxt = "J:" + FormatUtil.numFormat(mCurrLongPressQuotes.j, mDigits) + " ";
|
||||
mKdjPaint.setColor(mJColor);
|
||||
canvas.drawText(showTxt, (x + leftWidth + leftWidth2),
|
||||
y, mKdjPaint);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void drawNoPressLegend(Canvas canvas) {
|
||||
if (mDrawLongPress) return;
|
||||
String showTxt = "";
|
||||
if (mMinorType == KViewType.MinorIndicatrixType.MACD) {
|
||||
showTxt = "MACD(12,26,9)";
|
||||
} else if (mMinorType == KViewType.MinorIndicatrixType.RSI) {
|
||||
showTxt = "RSI(6,12,24)";
|
||||
} else if (mMinorType == KViewType.MinorIndicatrixType.KDJ) {
|
||||
showTxt = "KDJ(9,3,3)";
|
||||
}
|
||||
canvas.drawText(showTxt,
|
||||
(float) (mBaseWidth - mLegendPaddingRight - mBasePaddingRight - mLegendPaint.measureText(showTxt)),
|
||||
(float) (mLegendPaddingTop + mBasePaddingTop + getFontHeight(mLegendTxtSize, mLegendPaint)), mLegendPaint);
|
||||
}
|
||||
|
||||
private void drawYRightTxt(Canvas canvas) {
|
||||
//绘制右侧的y轴文字
|
||||
//现将最小值、最大值画好
|
||||
float halfTxtHight = getFontHeight(mXYTxtSize, mXYTxtPaint) / 2;//应该/2的,但是不准确,原因不明
|
||||
float x = mBaseWidth - mBasePaddingRight + mRightTxtPadding;
|
||||
float maxY = mBasePaddingTop + halfTxtHight + mInnerTopBlankPadding;
|
||||
float minY = mBaseHeight - mBasePaddingBottom - halfTxtHight - mInnerBottomBlankPadding;
|
||||
//draw min
|
||||
canvas.drawText(FormatUtil.numFormat(mMinY, mDigits),
|
||||
x,
|
||||
minY, mXYTxtPaint);
|
||||
//draw max
|
||||
canvas.drawText(FormatUtil.numFormat(mMaxY, mDigits),
|
||||
x,
|
||||
maxY, mXYTxtPaint);
|
||||
//draw middle
|
||||
canvas.drawText(FormatUtil.numFormat((mMaxY + mMinY) / 2.0, mDigits),
|
||||
x, (minY + maxY) / 2.0f,
|
||||
mXYTxtPaint);
|
||||
}
|
||||
|
||||
private void drawMACD(Canvas canvas) {
|
||||
|
||||
//macd
|
||||
//首先寻找"0"点,这个点是正负macd的分界点
|
||||
float v = mBaseHeight - mBasePaddingBottom - mInnerBottomBlankPadding;
|
||||
double zeroY = v - mPerY * (0 - mMinY);
|
||||
float startX, startY, stopX, stopY;
|
||||
|
||||
//dif
|
||||
float difX, difY;
|
||||
Path difPath = new Path();
|
||||
|
||||
//dea
|
||||
float deaX, deaY;
|
||||
Path deaPath = new Path();
|
||||
|
||||
for (int i = mBeginIndex; i < mEndIndex; i++) {
|
||||
Quotes quotes = mQuotesList.get(i);
|
||||
|
||||
/*macd*/
|
||||
//找另外一个y点
|
||||
double y = v - mPerY * (quotes.macd - mMinY);
|
||||
startX = mBasePaddingLeft + (i - mBeginIndex) * mPerX + mCandleDiverWidthRatio * mPerX / 2;
|
||||
stopX = mBasePaddingLeft + (i - mBeginIndex + 1) * mPerX - mCandleDiverWidthRatio * mPerX / 2;
|
||||
startY = (float) zeroY;
|
||||
stopY = (float) y;
|
||||
if (quotes.macd > 0) {
|
||||
mMacdPaint.setColor(mMacdBuyColor);
|
||||
} else {
|
||||
mMacdPaint.setColor(mMacdSellColor);
|
||||
}
|
||||
// Log.e(TAG, "drawMACD: "+startY+","+stopY +
|
||||
// ","+(mBasePaddingTop+mInnerTopBlankPadding)+","+
|
||||
// (mBaseHeight-mBasePaddingBottom-mInnerBottomBlankPadding));
|
||||
mMacdPaint.setStyle(Paint.Style.FILL);
|
||||
canvas.drawRect(startX, startY, stopX, stopY, mMacdPaint);
|
||||
|
||||
|
||||
/*dif*/
|
||||
difX = mBasePaddingLeft + (i - mBeginIndex) * mPerX + mPerX / 2;
|
||||
difY = (float) (v - mPerY * (quotes.dif - mMinY));
|
||||
if (i == mBeginIndex) {
|
||||
difPath.moveTo(difX - mPerX / 2, difY);//第一个点特殊处理
|
||||
} else {
|
||||
if (i == mEndIndex - 1) {
|
||||
difX += mPerX / 2;//最后一个点特殊处理
|
||||
}
|
||||
difPath.lineTo(difX, difY);
|
||||
}
|
||||
mMacdPaint.setStyle(Paint.Style.STROKE);
|
||||
mMacdPaint.setColor(mMacdDifColor);
|
||||
canvas.drawPath(difPath, mMacdPaint);
|
||||
|
||||
|
||||
/*dea*/
|
||||
deaX = mBasePaddingLeft + (i - mBeginIndex) * mPerX + mPerX / 2;
|
||||
deaY = (float) (v - mPerY * (quotes.dea - mMinY));
|
||||
if (i == mBeginIndex) {
|
||||
deaPath.moveTo(deaX - mPerX / 2, deaY);//第一个点特殊处理
|
||||
} else {
|
||||
if (i == mEndIndex - 1) {
|
||||
deaX += mPerX / 2;//最后一个点特殊处理
|
||||
}
|
||||
deaPath.lineTo(deaX, deaY);
|
||||
}
|
||||
mMacdPaint.setStyle(Paint.Style.STROKE);
|
||||
mMacdPaint.setColor(mMacdDeaColor);
|
||||
canvas.drawPath(deaPath, mMacdPaint);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void drawRSI(Canvas canvas) {
|
||||
|
||||
float rsiX;
|
||||
|
||||
//rsi6
|
||||
float rsi6Y;
|
||||
Path rsi6Path = new Path();
|
||||
|
||||
//rsi12
|
||||
float rsi12Y;
|
||||
Path rsi12Path = new Path();
|
||||
|
||||
//rsi24
|
||||
float rsi24Y;
|
||||
Path rsi24Path = new Path();
|
||||
|
||||
float v = mBaseHeight - mBasePaddingBottom - mInnerBottomBlankPadding;
|
||||
for (int i = mBeginIndex; i < mEndIndex; i++) {
|
||||
Quotes quotes = mQuotesList.get(i);
|
||||
|
||||
/*rsi6*/
|
||||
rsiX = mBasePaddingLeft + (i - mBeginIndex) * mPerX + mPerX / 2;
|
||||
rsi6Y = (float) (v - mPerY * (quotes.rsi6 - mMinY));
|
||||
if (i == mBeginIndex) {
|
||||
rsi6Path.moveTo(rsiX - mPerX / 2, rsi6Y);//第一个点特殊处理
|
||||
} else {
|
||||
if (i == mEndIndex - 1) {
|
||||
rsiX += mPerX / 2;//最后一个点特殊处理
|
||||
}
|
||||
rsi6Path.lineTo(rsiX, rsi6Y);
|
||||
}
|
||||
mRsiPaint.setColor(mRsi6Color);
|
||||
canvas.drawPath(rsi6Path, mRsiPaint);
|
||||
|
||||
/*rsi12*/
|
||||
//为什么这里重复再赋一遍值?因为下面有一个"rsiX +="操作
|
||||
rsiX = mBasePaddingLeft + (i - mBeginIndex) * mPerX + mPerX / 2;
|
||||
rsi12Y = (float) (v - mPerY * (quotes.rsi12 - mMinY));
|
||||
if (i == mBeginIndex) {
|
||||
rsi12Path.moveTo(rsiX - mPerX / 2, rsi12Y);//第一个点特殊处理
|
||||
} else {
|
||||
if (i == mEndIndex - 1) {
|
||||
rsiX += mPerX / 2;//最后一个点特殊处理
|
||||
}
|
||||
rsi12Path.lineTo(rsiX, rsi12Y);
|
||||
}
|
||||
mRsiPaint.setColor(mRsi12Color);
|
||||
canvas.drawPath(rsi12Path, mRsiPaint);
|
||||
|
||||
/*rsi24*/
|
||||
//为什么这里重复再赋一遍值?因为下面有一个"rsiX +="操作
|
||||
rsiX = mBasePaddingLeft + (i - mBeginIndex) * mPerX + mPerX / 2;
|
||||
rsi24Y = (float) (v - mPerY * (quotes.rsi24 - mMinY));
|
||||
if (i == mBeginIndex) {
|
||||
rsi24Path.moveTo(rsiX - mPerX / 2, rsi24Y);//第一个点特殊处理
|
||||
} else {
|
||||
if (i == mEndIndex - 1) {
|
||||
rsiX += mPerX / 2;//最后一个点特殊处理
|
||||
}
|
||||
rsi24Path.lineTo(rsiX, rsi24Y);
|
||||
}
|
||||
mRsiPaint.setColor(mRsi24Color);
|
||||
canvas.drawPath(rsi24Path, mRsiPaint);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void drawKDJ(Canvas canvas) {
|
||||
|
||||
float kdjX;
|
||||
|
||||
//k
|
||||
float kY;
|
||||
Path kPath = new Path();
|
||||
|
||||
//d
|
||||
float dY;
|
||||
Path dPath = new Path();
|
||||
|
||||
//j
|
||||
float jY;
|
||||
Path jPath = new Path();
|
||||
|
||||
float v = mBaseHeight - mBasePaddingBottom - mInnerBottomBlankPadding;
|
||||
for (int i = mBeginIndex; i < mEndIndex; i++) {
|
||||
Quotes quotes = mQuotesList.get(i);
|
||||
|
||||
/*k*/
|
||||
kdjX = mBasePaddingLeft + (i - mBeginIndex) * mPerX + mPerX / 2;
|
||||
kY = (float) (v - mPerY * (quotes.k - mMinY));
|
||||
if (i == mBeginIndex) {
|
||||
kPath.moveTo(kdjX - mPerX / 2, kY);//第一个点特殊处理
|
||||
} else {
|
||||
if (i == mEndIndex - 1) {
|
||||
kdjX += mPerX / 2;//最后一个点特殊处理
|
||||
}
|
||||
kPath.lineTo(kdjX, kY);
|
||||
}
|
||||
mKdjPaint.setColor(mKColor);
|
||||
canvas.drawPath(kPath, mKdjPaint);
|
||||
|
||||
/*d*/
|
||||
kdjX = mBasePaddingLeft + (i - mBeginIndex) * mPerX + mPerX / 2;
|
||||
dY = (float) (v - mPerY * (quotes.d - mMinY));
|
||||
if (i == mBeginIndex) {
|
||||
dPath.moveTo(kdjX - mPerX / 2, dY);//第一个点特殊处理
|
||||
} else {
|
||||
if (i == mEndIndex - 1) {
|
||||
kdjX += mPerX / 2;//最后一个点特殊处理
|
||||
}
|
||||
dPath.lineTo(kdjX, dY);
|
||||
}
|
||||
mKdjPaint.setColor(mDColor);
|
||||
canvas.drawPath(dPath, mKdjPaint);
|
||||
|
||||
/*j*/
|
||||
kdjX = mBasePaddingLeft + (i - mBeginIndex) * mPerX + mPerX / 2;
|
||||
jY = (float) (v - mPerY * (quotes.j - mMinY));
|
||||
if (i == mBeginIndex) {
|
||||
jPath.moveTo(kdjX - mPerX / 2, jY);//第一个点特殊处理
|
||||
} else {
|
||||
if (i == mEndIndex - 1) {
|
||||
kdjX += mPerX / 2;//最后一个点特殊处理
|
||||
}
|
||||
jPath.lineTo(kdjX, jY);
|
||||
}
|
||||
mKdjPaint.setColor(mJColor);
|
||||
canvas.drawPath(jPath, mKdjPaint);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void seekAndCalculateCellData() {
|
||||
if (mQuotesList == null || mQuotesList.isEmpty()) return;
|
||||
|
||||
if (mMinorType == KViewType.MinorIndicatrixType.MACD) {
|
||||
FinancialAlgorithm.calculateMACD(mQuotesList);
|
||||
} else if (mMinorType == KViewType.MinorIndicatrixType.RSI) {
|
||||
FinancialAlgorithm.calculateRSI(mQuotesList);
|
||||
} else if (mMinorType == KViewType.MinorIndicatrixType.KDJ) {
|
||||
FinancialAlgorithm.calculateKDJ(mQuotesList);
|
||||
}
|
||||
|
||||
//找到close最大值和最小值
|
||||
mMinY = Integer.MAX_VALUE;
|
||||
mMaxY = Integer.MIN_VALUE;
|
||||
|
||||
|
||||
for (int i = mBeginIndex; i < mEndIndex; i++) {
|
||||
Quotes quotes = mQuotesList.get(i);
|
||||
if (i == mBeginIndex) {
|
||||
mBeginQuotes = quotes;
|
||||
}
|
||||
if (i == mEndIndex - 1) {
|
||||
mEndQuotes = quotes;
|
||||
}
|
||||
double min = FinancialAlgorithm.getMinorMinY(quotes, mMinorType);
|
||||
double max = FinancialAlgorithm.getMinorMaxY(quotes, mMinorType);
|
||||
|
||||
if (min <= mMinY) {
|
||||
mMinY = min;
|
||||
}
|
||||
if (max >= mMaxY) {
|
||||
mMaxY = max;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
mPerX = (mBaseWidth - mBasePaddingLeft - mBasePaddingRight - mInnerRightBlankPadding)
|
||||
/ (mShownMaxCount);
|
||||
//不要忘了减去内部的上下Padding
|
||||
mPerY = (float) ((mBaseHeight - mBasePaddingTop - mBasePaddingBottom - mInnerTopBlankPadding
|
||||
- mInnerBottomBlankPadding) / (mMaxY - mMinY));
|
||||
Log.e(TAG, "seekAndCalculateCellData: mMinY:" + mMinY + ",mMaxY:" + mMaxY);
|
||||
//重绘
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void innerClickListener() {
|
||||
super.innerClickListener();
|
||||
if (mMinorType == KViewType.MinorIndicatrixType.MACD) {
|
||||
mMinorType = KViewType.MinorIndicatrixType.RSI;
|
||||
} else if (mMinorType == KViewType.MinorIndicatrixType.RSI) {
|
||||
mMinorType = KViewType.MinorIndicatrixType.KDJ;
|
||||
} else if (mMinorType == KViewType.MinorIndicatrixType.KDJ) {
|
||||
mMinorType = KViewType.MinorIndicatrixType.MACD;
|
||||
}
|
||||
setMinorType(mMinorType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void innerLongClickListener(float x, float y) {
|
||||
super.innerLongClickListener(x, y);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void innerHiddenLongClick() {
|
||||
super.innerHiddenLongClick();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void innerMoveViewListener(float moveXLen) {
|
||||
super.innerMoveViewListener(moveXLen);
|
||||
|
||||
}
|
||||
|
||||
|
||||
//-----------------------对开发者暴露可以修改的参数-------
|
||||
|
||||
public void setMinorType(KViewType.MinorIndicatrixType minorType) {
|
||||
mMinorType = minorType;
|
||||
|
||||
seekAndCalculateCellData();
|
||||
}
|
||||
|
||||
public int getOuterStrokeColor() {
|
||||
return mOuterStrokeColor;
|
||||
}
|
||||
|
||||
public MinorView setOuterStrokeColor(int outerStrokeColor) {
|
||||
mOuterStrokeColor = outerStrokeColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getInnerXyDashColor() {
|
||||
return mInnerXyDashColor;
|
||||
}
|
||||
|
||||
public MinorView setInnerXyDashColor(int innerXyDashColor) {
|
||||
mInnerXyDashColor = innerXyDashColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getXyTxtColor() {
|
||||
return mXyTxtColor;
|
||||
}
|
||||
|
||||
public MinorView setXyTxtColor(int xyTxtColor) {
|
||||
mXyTxtColor = xyTxtColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getLegendTxtColor() {
|
||||
return mLegendTxtColor;
|
||||
}
|
||||
|
||||
public MinorView setLegendTxtColor(int legendTxtColor) {
|
||||
mLegendTxtColor = legendTxtColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getLongPressTxtColor() {
|
||||
return mLongPressTxtColor;
|
||||
}
|
||||
|
||||
public MinorView setLongPressTxtColor(int longPressTxtColor) {
|
||||
mLongPressTxtColor = longPressTxtColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getMacdBuyColor() {
|
||||
return mMacdBuyColor;
|
||||
}
|
||||
|
||||
public MinorView setMacdBuyColor(int macdBuyColor) {
|
||||
mMacdBuyColor = macdBuyColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getMacdSellColor() {
|
||||
return mMacdSellColor;
|
||||
}
|
||||
|
||||
public MinorView setMacdSellColor(int macdSellColor) {
|
||||
mMacdSellColor = macdSellColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getMacdDifColor() {
|
||||
return mMacdDifColor;
|
||||
}
|
||||
|
||||
public MinorView setMacdDifColor(int macdDifColor) {
|
||||
mMacdDifColor = macdDifColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getMacdDeaColor() {
|
||||
return mMacdDeaColor;
|
||||
}
|
||||
|
||||
public MinorView setMacdDeaColor(int macdDeaColor) {
|
||||
mMacdDeaColor = macdDeaColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getMacdMacdColor() {
|
||||
return mMacdMacdColor;
|
||||
}
|
||||
|
||||
public MinorView setMacdMacdColor(int macdMacdColor) {
|
||||
mMacdMacdColor = macdMacdColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Paint getMacdPaint() {
|
||||
return mMacdPaint;
|
||||
}
|
||||
|
||||
public MinorView setMacdPaint(Paint macdPaint) {
|
||||
mMacdPaint = macdPaint;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getMacdLineWidth() {
|
||||
return mMacdLineWidth;
|
||||
}
|
||||
|
||||
public MinorView setMacdLineWidth(float macdLineWidth) {
|
||||
mMacdLineWidth = macdLineWidth;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getRsi6Color() {
|
||||
return mRsi6Color;
|
||||
}
|
||||
|
||||
public MinorView setRsi6Color(int rsi6Color) {
|
||||
mRsi6Color = rsi6Color;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getRsi12Color() {
|
||||
return mRsi12Color;
|
||||
}
|
||||
|
||||
public MinorView setRsi12Color(int rsi12Color) {
|
||||
mRsi12Color = rsi12Color;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getRsi24Color() {
|
||||
return mRsi24Color;
|
||||
}
|
||||
|
||||
public MinorView setRsi24Color(int rsi24Color) {
|
||||
mRsi24Color = rsi24Color;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Paint getRsiPaint() {
|
||||
return mRsiPaint;
|
||||
}
|
||||
|
||||
public MinorView setRsiPaint(Paint rsiPaint) {
|
||||
mRsiPaint = rsiPaint;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getRsiLineWidth() {
|
||||
return mRsiLineWidth;
|
||||
}
|
||||
|
||||
public MinorView setRsiLineWidth(float rsiLineWidth) {
|
||||
mRsiLineWidth = rsiLineWidth;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getKColor() {
|
||||
return mKColor;
|
||||
}
|
||||
|
||||
public MinorView setKColor(int KColor) {
|
||||
mKColor = KColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getDColor() {
|
||||
return mDColor;
|
||||
}
|
||||
|
||||
public MinorView setDColor(int DColor) {
|
||||
mDColor = DColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getJColor() {
|
||||
return mJColor;
|
||||
}
|
||||
|
||||
public MinorView setJColor(int JColor) {
|
||||
mJColor = JColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Paint getKdjPaint() {
|
||||
return mKdjPaint;
|
||||
}
|
||||
|
||||
public MinorView setKdjPaint(Paint kdjPaint) {
|
||||
mKdjPaint = kdjPaint;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getKdjLineWidth() {
|
||||
return mKdjLineWidth;
|
||||
}
|
||||
|
||||
public MinorView setKdjLineWidth(float kdjLineWidth) {
|
||||
mKdjLineWidth = kdjLineWidth;
|
||||
return this;
|
||||
}
|
||||
|
||||
public KViewType.MinorIndicatrixType getMinorType() {
|
||||
return mMinorType;
|
||||
}
|
||||
|
||||
public double getMinY() {
|
||||
return mMinY;
|
||||
}
|
||||
|
||||
public MinorView setMinY(double minY) {
|
||||
mMinY = minY;
|
||||
return this;
|
||||
}
|
||||
|
||||
public double getMaxY() {
|
||||
return mMaxY;
|
||||
}
|
||||
|
||||
public MinorView setMaxY(double maxY) {
|
||||
mMaxY = maxY;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getCandleDiverWidthRatio() {
|
||||
return mCandleDiverWidthRatio;
|
||||
}
|
||||
|
||||
public MinorView setCandleDiverWidthRatio(float candleDiverWidthRatio) {
|
||||
mCandleDiverWidthRatio = candleDiverWidthRatio;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MinorView setMinorListener(KViewListener.MinorListener minorListener) {
|
||||
mMinorListener = minorListener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public KViewListener.MinorListener getMinorListener() {
|
||||
return mMinorListener;
|
||||
}
|
||||
}
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
package com.tophold.trade.view.kview;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import com.tophold.trade.utils.TimeUtils;
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
* 作 者 : wgyscsf@163.com
|
||||
* 创建日期 :2017/11/21 11:12
|
||||
* 描 述 :包装后的Quotes,实际使用的Quotes
|
||||
* ============================================================
|
||||
**/
|
||||
public class Quotes implements Serializable {
|
||||
|
||||
/**
|
||||
* 原始数据
|
||||
*/
|
||||
public long t;//开始时间
|
||||
public double o;
|
||||
public double h;
|
||||
public double l;
|
||||
public double c;
|
||||
|
||||
//扩展一个结束时间
|
||||
public long e;
|
||||
|
||||
//vol 量,可选
|
||||
public double vol;
|
||||
|
||||
/**
|
||||
* 适配数据的构造方法,包括五个参数,全部必须。价格格式为String类型
|
||||
*
|
||||
* @param o 闭盘价
|
||||
* @param h 最高价
|
||||
* @param l 最低价
|
||||
* @param c 收盘价
|
||||
* @param t 时间
|
||||
*/
|
||||
public Quotes(String o, String h, String l, String c, String t) {
|
||||
this(Double.parseDouble(o), Double.parseDouble(h), Double.parseDouble(l), Double.parseDouble(c), TimeUtils.date2Millis(new Date(t)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 价格格式是double类型
|
||||
*
|
||||
* @param o
|
||||
* @param h
|
||||
* @param l
|
||||
* @param c
|
||||
* @param t
|
||||
*/
|
||||
public Quotes(double o, double h, double l, double c, long t) {
|
||||
this(o, h, l, c, t, -1, -1);
|
||||
}
|
||||
|
||||
public Quotes(double o, double h, double l, double c, long t, double vol) {
|
||||
this(o, h, l, c, t, -1, vol);
|
||||
}
|
||||
|
||||
/**
|
||||
* 最原始构造方法
|
||||
*
|
||||
* @param o
|
||||
* @param h
|
||||
* @param l
|
||||
* @param c
|
||||
* @param t
|
||||
* @param e
|
||||
*/
|
||||
public Quotes(double o, double h, double l, double c, long t, long e, double vol) {
|
||||
this.o = o;
|
||||
this.h = h;
|
||||
this.l = l;
|
||||
this.c = c;
|
||||
this.t = t;
|
||||
this.e = e;
|
||||
this.vol = vol;
|
||||
}
|
||||
|
||||
/**
|
||||
* 多添加一个时间,包括两个格式的时间,一个开始时间,一个结束时间,标准以开始时间为准。
|
||||
*
|
||||
* @param o
|
||||
* @param h
|
||||
* @param l
|
||||
* @param c
|
||||
* @param s
|
||||
* @param e
|
||||
*/
|
||||
public Quotes(String o, String h, String l, String c, long s, long e) {
|
||||
this(Double.parseDouble(o), Double.parseDouble(h), Double.parseDouble(l), Double.parseDouble(c), s, e, -1);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 扩展的数据
|
||||
*/
|
||||
//实际中展示的时间
|
||||
private String showTime;
|
||||
|
||||
public String getShowTime() {
|
||||
showTime = TimeUtils.millis2String(t);
|
||||
return showTime;
|
||||
}
|
||||
|
||||
//在自定义view:FundView中的位置坐标
|
||||
public float floatX;
|
||||
public float floatY;
|
||||
|
||||
//MA
|
||||
public double ma5;
|
||||
public double ma10;
|
||||
public double ma20;
|
||||
|
||||
//BOLL
|
||||
public double up;//上轨线
|
||||
public double mb;//中轨线
|
||||
public double dn;//下轨线
|
||||
|
||||
//KDJ
|
||||
public double k;
|
||||
public double d;
|
||||
public double j;
|
||||
|
||||
//macd
|
||||
public double dif;
|
||||
public double dea;
|
||||
public double macd;
|
||||
|
||||
//rsi
|
||||
public double rsi6;
|
||||
public double rsi12;
|
||||
public double rsi24;
|
||||
|
||||
//vol
|
||||
public double volMa5;
|
||||
public double volMa10;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Quotes{" +
|
||||
"t=" + t +
|
||||
", o=" + o +
|
||||
", h=" + h +
|
||||
", l=" + l +
|
||||
", c=" + c +
|
||||
", e=" + e +
|
||||
", vol=" + vol +
|
||||
", showTime='" + showTime + '\'' +
|
||||
", floatX=" + floatX +
|
||||
", floatY=" + floatY +
|
||||
", ma5=" + ma5 +
|
||||
", ma10=" + ma10 +
|
||||
", ma20=" + ma20 +
|
||||
", up=" + up +
|
||||
", mb=" + mb +
|
||||
", dn=" + dn +
|
||||
", k=" + k +
|
||||
", d=" + d +
|
||||
", j=" + j +
|
||||
", dif=" + dif +
|
||||
", dea=" + dea +
|
||||
", macd=" + macd +
|
||||
", rsi6=" + rsi6 +
|
||||
", rsi12=" + rsi12 +
|
||||
", rsi24=" + rsi24 +
|
||||
", volMa5=" + volMa5 +
|
||||
", volMa10=" + volMa10 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.tophold.trade.view.kview;
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
* 作 者 : wgyscsf@163.com
|
||||
* 更新时间 :2018/07/08 21:24
|
||||
* 描 述 :量
|
||||
* ============================================================
|
||||
*/
|
||||
public class VolModel {
|
||||
public boolean cUp;//当前收盘价是否大于等于前一天收盘价
|
||||
public double vol;//量
|
||||
public double ma5;
|
||||
public double ma10;
|
||||
|
||||
public VolModel(boolean cUp, double vol, double ma5, double ma10) {
|
||||
this.cUp = cUp;
|
||||
this.vol = vol;
|
||||
this.ma5 = ma5;
|
||||
this.ma10 = ma10;
|
||||
}
|
||||
}
|
||||
+863
@@ -0,0 +1,863 @@
|
||||
package com.tophold.trade.view.kview;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
|
||||
import com.tophold.trade.R;
|
||||
import com.tophold.trade.utils.FormatUtil;
|
||||
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
* 作 者 : wgyscsf@163.com
|
||||
* 创建日期 :2017/12/14 17:56
|
||||
* 描 述 :量图,从本质上来说基本和副图一致。
|
||||
* ============================================================
|
||||
**/
|
||||
public class VolView extends KBaseView {
|
||||
|
||||
|
||||
/**
|
||||
* 初始化所有需要的颜色资源
|
||||
*/
|
||||
//共有的
|
||||
int mOuterStrokeColor;
|
||||
int mInnerXyDashColor;
|
||||
int mXyTxtColor;
|
||||
int mLegendTxtColor;
|
||||
int mLongPressTxtColor;
|
||||
|
||||
//macd
|
||||
int mMacdBuyColor;
|
||||
int mMacdSellColor;
|
||||
int mMacdDifColor;
|
||||
int mMacdDeaColor;
|
||||
int mMacdMacdColor;
|
||||
Paint mMacdPaint;
|
||||
float mMacdLineWidth = 1;
|
||||
|
||||
|
||||
//rsi
|
||||
int mRsi6Color;
|
||||
int mRsi12Color;
|
||||
int mRsi24Color;
|
||||
Paint mRsiPaint;
|
||||
float mRsiLineWidth = 1;
|
||||
|
||||
//kdj
|
||||
int mKColor;
|
||||
int mDColor;
|
||||
int mJColor;
|
||||
Paint mKdjPaint;
|
||||
float mKdjLineWidth = 1;
|
||||
|
||||
//当前显示的指标
|
||||
KViewType.MinorIndicatrixType mMinorType = KViewType.MinorIndicatrixType.MACD;
|
||||
|
||||
//y轴上最大值和最小值
|
||||
protected double mMinY;
|
||||
protected double mMaxY;
|
||||
//蜡烛图间隙,大小以单个蜡烛图的宽度的比例算。可修改。
|
||||
protected float mCandleDiverWidthRatio = 0.1f;
|
||||
|
||||
//监听主图的长按事件
|
||||
private KViewListener.MinorListener mVolListener;
|
||||
|
||||
//是否展示量图
|
||||
private boolean mShowVol = false;
|
||||
|
||||
public VolView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public VolView(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public VolView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initAttrs();
|
||||
initListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
Log.d(TAG, "onDraw: " + mBaseHeight + "," + mBaseWidth);
|
||||
if (mQuotesList == null || mQuotesList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
//绘制右侧文字
|
||||
drawYRightTxt(canvas);
|
||||
//绘制图例
|
||||
drawLegend(canvas);
|
||||
//绘制核心指标线
|
||||
drawMinorIndicatrix(canvas);
|
||||
//绘制长按线
|
||||
drawLongPress(canvas);
|
||||
}
|
||||
|
||||
private void initAttrs() {
|
||||
initDefAttrs();
|
||||
initColorRes();
|
||||
initMacdPaint();
|
||||
initRsiPaint();
|
||||
initKdjPaint();
|
||||
|
||||
}
|
||||
|
||||
private void initKdjPaint() {
|
||||
mKdjPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mKdjPaint.setColor(mKColor);
|
||||
mKdjPaint.setAntiAlias(true);
|
||||
mKdjPaint.setStrokeWidth(mKdjLineWidth);
|
||||
mKdjPaint.setStyle(Paint.Style.STROKE);
|
||||
mKdjPaint.setTextSize(mLegendTxtSize);
|
||||
}
|
||||
|
||||
private void initRsiPaint() {
|
||||
mRsiPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mRsiPaint.setColor(mRsi6Color);
|
||||
mRsiPaint.setAntiAlias(true);
|
||||
mRsiPaint.setStrokeWidth(mRsiLineWidth);
|
||||
mRsiPaint.setStyle(Paint.Style.STROKE);
|
||||
mRsiPaint.setTextSize(mLegendTxtSize);
|
||||
}
|
||||
|
||||
private void initMacdPaint() {
|
||||
mMacdPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mMacdPaint.setColor(mMacdBuyColor);
|
||||
mMacdPaint.setAntiAlias(true);
|
||||
mMacdPaint.setStrokeWidth(mMacdLineWidth);
|
||||
mMacdPaint.setTextSize(mLegendTxtSize);
|
||||
}
|
||||
|
||||
private void initDefAttrs() {
|
||||
//重写内边距大小
|
||||
mInnerTopBlankPadding = 8;
|
||||
mInnerBottomBlankPadding = 8;
|
||||
|
||||
//重写Legend padding
|
||||
mLegendPaddingTop = 0;
|
||||
mLegendPaddingRight = 4;
|
||||
mLegendPaddingLeft = 4;
|
||||
|
||||
|
||||
setShowInnerX(false);
|
||||
setShowInnerY(true);
|
||||
}
|
||||
|
||||
private void initColorRes() {
|
||||
//颜色
|
||||
mOuterStrokeColor = getColor(R.color.color_minorView_outerStrokeColor);
|
||||
mInnerXyDashColor = getColor(R.color.color_minorView_innerXyDashColor);
|
||||
mXyTxtColor = getColor(R.color.color_minorView_xYTxtColor);
|
||||
mLegendTxtColor = getColor(R.color.color_minorView_legendTxtColor);
|
||||
mLongPressTxtColor = getColor(R.color.color_minorView_longPressTxtColor);
|
||||
mMacdBuyColor = getColor(R.color.color_minorView_macdBuyColor);
|
||||
mMacdSellColor = getColor(R.color.color_minorView_macdSellColor);
|
||||
mMacdDifColor = getColor(R.color.color_minorView_macdDifColor);
|
||||
mMacdDeaColor = getColor(R.color.color_minorView_macdDeaColor);
|
||||
mMacdMacdColor = getColor(R.color.color_minorView_macdMacdColor);
|
||||
mRsi6Color = getColor(R.color.color_minorView_rsi6Color);
|
||||
mRsi12Color = getColor(R.color.color_minorView_rsi12Color);
|
||||
mRsi24Color = getColor(R.color.color_minorView_rsi24Color);
|
||||
mKColor = getColor(R.color.color_minorView_kColor);
|
||||
mDColor = getColor(R.color.color_minorView_dColor);
|
||||
mJColor = getColor(R.color.color_minorView_jColor);
|
||||
}
|
||||
|
||||
private void initListener() {
|
||||
mVolListener = new KViewListener.MinorListener() {
|
||||
@Override
|
||||
public void masterLongPressListener(int pressIndex, Quotes currQuotes) {
|
||||
mDrawLongPress = true;
|
||||
mCurrLongPressQuotes = mQuotesList.get(pressIndex);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void masterNoLongPressListener() {
|
||||
mDrawLongPress = false;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void masteZoomlNewIndex(int beginIndex, int endIndex, int shownMaxCount) {
|
||||
mBeginIndex = beginIndex;
|
||||
mEndIndex = endIndex;
|
||||
mShownMaxCount = shownMaxCount;
|
||||
seekAndCalculateCellData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mastelPullmNewIndex(int beginIndex, int endIndex, KViewType.PullType currPullType, int shownMaxCount) {
|
||||
mBeginIndex = beginIndex;
|
||||
mEndIndex = endIndex;
|
||||
mShownMaxCount = shownMaxCount;
|
||||
mPullType = currPullType;
|
||||
|
||||
//处理右侧内边距
|
||||
if (currPullType == KViewType.PullType.PULL_RIGHT_STOP) {
|
||||
//重置到之前的状态
|
||||
mInnerRightBlankPadding = DEF_INNER_RIGHT_BLANK_PADDING;
|
||||
} else {
|
||||
mInnerRightBlankPadding = 0;
|
||||
}
|
||||
|
||||
seekAndCalculateCellData();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void drawLegend(Canvas canvas) {
|
||||
//绘制非按下情况下图例
|
||||
drawNoPressLegend(canvas);
|
||||
//绘制按下的图例
|
||||
drawPressLegend(canvas);
|
||||
}
|
||||
|
||||
private void drawMinorIndicatrix(Canvas canvas) {
|
||||
mMacdPaint.setStyle(Paint.Style.STROKE);
|
||||
drawMACD(canvas);
|
||||
}
|
||||
|
||||
private void drawLongPress(Canvas canvas) {
|
||||
if (!mDrawLongPress) return;
|
||||
if (mCurrLongPressQuotes == null) return;
|
||||
|
||||
//y轴线
|
||||
canvas.drawLine(mCurrLongPressQuotes.floatX, mBasePaddingTop, mCurrLongPressQuotes.floatX,
|
||||
mBaseHeight - mBasePaddingBottom, mLongPressPaint);
|
||||
}
|
||||
|
||||
private void drawPressLegend(Canvas canvas) {
|
||||
if (!mDrawLongPress) return;
|
||||
|
||||
mMacdPaint.setStyle(Paint.Style.FILL);
|
||||
float x = (float) (mLegendPaddingLeft + mBasePaddingLeft);
|
||||
float y = (float) (mLegendPaddingTop + mBasePaddingTop) + getFontHeight(mLegendTxtSize, mMacdPaint);
|
||||
|
||||
String showTxt = "VOL:" + FormatUtil.numFormat(mCurrLongPressQuotes.vol, mDigits) + " ";
|
||||
mMacdPaint.setColor(mMacdDifColor);
|
||||
canvas.drawText(showTxt, x,
|
||||
y, mMacdPaint);
|
||||
|
||||
float leftWidth11 = mMacdPaint.measureText(showTxt);
|
||||
showTxt = "MA5:" + FormatUtil.numFormat(mCurrLongPressQuotes.volMa5, mDigits) + " ";
|
||||
mMacdPaint.setColor(mMacdDeaColor);
|
||||
canvas.drawText(showTxt, x + leftWidth11, y, mMacdPaint);
|
||||
|
||||
float leftWidth12 = mMacdPaint.measureText(showTxt);
|
||||
showTxt = "MA10:" + FormatUtil.numFormat(mCurrLongPressQuotes.volMa10, mDigits) + " ";
|
||||
mMacdPaint.setColor(mMacdMacdColor);
|
||||
canvas.drawText(showTxt, (x + leftWidth11 + leftWidth12),
|
||||
y, mMacdPaint);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void drawNoPressLegend(Canvas canvas) {
|
||||
if (mDrawLongPress) return;
|
||||
String showTxt = "MA(5,10)";
|
||||
canvas.drawText(showTxt,
|
||||
(float) (mBaseWidth - mLegendPaddingRight - mBasePaddingRight - mLegendPaint.measureText(showTxt)),
|
||||
(float) (mLegendPaddingTop + mBasePaddingTop + getFontHeight(mLegendTxtSize, mLegendPaint)), mLegendPaint);
|
||||
}
|
||||
|
||||
private void drawYRightTxt(Canvas canvas) {
|
||||
//绘制右侧的y轴文字
|
||||
//现将最小值、最大值画好
|
||||
float halfTxtHight = getFontHeight(mXYTxtSize, mXYTxtPaint) / 2;//应该/2的,但是不准确,原因不明
|
||||
float x = mBaseWidth - mBasePaddingRight + mRightTxtPadding;
|
||||
float maxY = mBasePaddingTop + halfTxtHight + mInnerTopBlankPadding;
|
||||
float minY = mBaseHeight - mBasePaddingBottom - halfTxtHight - mInnerBottomBlankPadding;
|
||||
//draw min
|
||||
canvas.drawText(FormatUtil.numFormat(mMinY, mDigits),
|
||||
x,
|
||||
minY, mXYTxtPaint);
|
||||
//draw max
|
||||
canvas.drawText(FormatUtil.numFormat(mMaxY, mDigits),
|
||||
x,
|
||||
maxY, mXYTxtPaint);
|
||||
//draw middle
|
||||
canvas.drawText(FormatUtil.numFormat((mMaxY + mMinY) / 2.0, mDigits),
|
||||
x, (minY + maxY) / 2.0f,
|
||||
mXYTxtPaint);
|
||||
}
|
||||
|
||||
private void drawMACD(Canvas canvas) {
|
||||
|
||||
//macd
|
||||
//首先寻找"0"点,这个点是正负macd的分界点
|
||||
float v = mBaseHeight - mBasePaddingBottom - mInnerBottomBlankPadding;
|
||||
float startX, startY, stopX, stopY;
|
||||
|
||||
//dif
|
||||
float difX, difY;
|
||||
Path difPath = new Path();
|
||||
//dea
|
||||
float deaX, deaY;
|
||||
Path deaPath = new Path();
|
||||
|
||||
boolean isFirstMa5 = true;
|
||||
boolean isFirstMa10 = true;
|
||||
for (int i = mBeginIndex; i < mEndIndex; i++) {
|
||||
Quotes quotes = mQuotesList.get(i);
|
||||
|
||||
/*macd*/
|
||||
//找另外一个y点
|
||||
double y = v - mPerY * (quotes.vol - mMinY);
|
||||
startX = mBasePaddingLeft + (i - mBeginIndex) * mPerX + mCandleDiverWidthRatio * mPerX / 2;
|
||||
stopX = mBasePaddingLeft + (i - mBeginIndex + 1) * mPerX - mCandleDiverWidthRatio * mPerX / 2;
|
||||
startY = v;
|
||||
stopY = (float) y;
|
||||
if (i > 0 && mQuotesList.get(i - 1).c < quotes.c) {
|
||||
mMacdPaint.setColor(mMacdBuyColor);
|
||||
} else {
|
||||
mMacdPaint.setColor(mMacdSellColor);
|
||||
}
|
||||
// Log.e(TAG, "drawMACD: "+startY+","+stopY +
|
||||
// ","+(mBasePaddingTop+mInnerTopBlankPadding)+","+
|
||||
// (mBaseHeight-mBasePaddingBottom-mInnerBottomBlankPadding));
|
||||
mMacdPaint.setStyle(Paint.Style.FILL);
|
||||
canvas.drawRect(startX, startY, stopX, stopY, mMacdPaint);
|
||||
|
||||
|
||||
/*dif*/
|
||||
difX = mBasePaddingLeft + (i - mBeginIndex) * mPerX + mPerX / 2;
|
||||
difY = getMasterDetailFloatY(quotes, KViewType.MaType.volMa5);
|
||||
if (difY != -1) {
|
||||
if (isFirstMa5) {
|
||||
isFirstMa5 = false;
|
||||
if (quotes.volMa5 != 0) difX -= mPerX / 2;//第一个点特殊处理
|
||||
difPath.moveTo(difX, difY);
|
||||
} else {
|
||||
if (i == mEndIndex - 1) difX += mPerX / 2;//最后一个点特殊处理
|
||||
difPath.lineTo(difX, difY);
|
||||
}
|
||||
mMacdPaint.setStyle(Paint.Style.STROKE);
|
||||
mMacdPaint.setColor(mMacdDifColor);
|
||||
canvas.drawPath(difPath, mMacdPaint);
|
||||
}
|
||||
|
||||
|
||||
/*dea*/
|
||||
deaX = mBasePaddingLeft + (i - mBeginIndex) * mPerX + mPerX / 2;
|
||||
deaY = getMasterDetailFloatY(quotes, KViewType.MaType.volMa10);
|
||||
if (deaY != -1) {
|
||||
if (isFirstMa10) {
|
||||
isFirstMa10 = false;
|
||||
if (quotes.volMa10 != 0) deaX -= mPerX / 2;//第一个点特殊处理
|
||||
deaPath.moveTo(deaX, deaY);
|
||||
} else {
|
||||
if (i == mEndIndex - 1) deaX += mPerX / 2;//最后一个点特殊处理
|
||||
deaPath.lineTo(deaX, deaY);
|
||||
}
|
||||
mMacdPaint.setStyle(Paint.Style.STROKE);
|
||||
mMacdPaint.setColor(mMacdDeaColor);
|
||||
canvas.drawPath(deaPath, mMacdPaint);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private float getMasterDetailFloatY(Quotes quotes, KViewType.MaType maType) {
|
||||
double v = 0;
|
||||
//ma
|
||||
if (maType == KViewType.MaType.volMa5) {
|
||||
v = quotes.volMa5 - mMinY;
|
||||
} else if (maType == KViewType.MaType.volMa10) {
|
||||
v = quotes.volMa10 - mMinY;
|
||||
}
|
||||
//异常,当不存在ma值时的处理.也就是up、mb、dn为0时,这样判断其实有问题,比如算出来的值就是0???
|
||||
if (v + mMinY == 0) return -1;
|
||||
|
||||
double h = v * mPerY;
|
||||
float y = (float) (mBaseHeight - h - mBasePaddingBottom - mInnerBottomBlankPadding);
|
||||
|
||||
//这里的y,存在一种情况,y超过了View的上边界或者超过了下边界,当出现这一种情况时,不显示,当作异常情况
|
||||
if (y < mBasePaddingTop || y > mBaseHeight - mBasePaddingBottom)
|
||||
return -1;
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
private void drawRSI(Canvas canvas) {
|
||||
|
||||
float rsiX;
|
||||
|
||||
//rsi6
|
||||
float rsi6Y;
|
||||
Path rsi6Path = new Path();
|
||||
|
||||
//rsi12
|
||||
float rsi12Y;
|
||||
Path rsi12Path = new Path();
|
||||
|
||||
//rsi24
|
||||
float rsi24Y;
|
||||
Path rsi24Path = new Path();
|
||||
|
||||
float v = mBaseHeight - mBasePaddingBottom - mInnerBottomBlankPadding;
|
||||
for (int i = mBeginIndex; i < mEndIndex; i++) {
|
||||
Quotes quotes = mQuotesList.get(i);
|
||||
|
||||
/*rsi6*/
|
||||
rsiX = mBasePaddingLeft + (i - mBeginIndex) * mPerX + mPerX / 2;
|
||||
rsi6Y = (float) (v - mPerY * (quotes.rsi6 - mMinY));
|
||||
if (i == mBeginIndex) {
|
||||
rsi6Path.moveTo(rsiX - mPerX / 2, rsi6Y);//第一个点特殊处理
|
||||
} else {
|
||||
if (i == mEndIndex - 1) {
|
||||
rsiX += mPerX / 2;//最后一个点特殊处理
|
||||
}
|
||||
rsi6Path.lineTo(rsiX, rsi6Y);
|
||||
}
|
||||
mRsiPaint.setColor(mRsi6Color);
|
||||
canvas.drawPath(rsi6Path, mRsiPaint);
|
||||
|
||||
/*rsi12*/
|
||||
//为什么这里重复再赋一遍值?因为下面有一个"rsiX +="操作
|
||||
rsiX = mBasePaddingLeft + (i - mBeginIndex) * mPerX + mPerX / 2;
|
||||
rsi12Y = (float) (v - mPerY * (quotes.rsi12 - mMinY));
|
||||
if (i == mBeginIndex) {
|
||||
rsi12Path.moveTo(rsiX - mPerX / 2, rsi12Y);//第一个点特殊处理
|
||||
} else {
|
||||
if (i == mEndIndex - 1) {
|
||||
rsiX += mPerX / 2;//最后一个点特殊处理
|
||||
}
|
||||
rsi12Path.lineTo(rsiX, rsi12Y);
|
||||
}
|
||||
mRsiPaint.setColor(mRsi12Color);
|
||||
canvas.drawPath(rsi12Path, mRsiPaint);
|
||||
|
||||
/*rsi24*/
|
||||
//为什么这里重复再赋一遍值?因为下面有一个"rsiX +="操作
|
||||
rsiX = mBasePaddingLeft + (i - mBeginIndex) * mPerX + mPerX / 2;
|
||||
rsi24Y = (float) (v - mPerY * (quotes.rsi24 - mMinY));
|
||||
if (i == mBeginIndex) {
|
||||
rsi24Path.moveTo(rsiX - mPerX / 2, rsi24Y);//第一个点特殊处理
|
||||
} else {
|
||||
if (i == mEndIndex - 1) {
|
||||
rsiX += mPerX / 2;//最后一个点特殊处理
|
||||
}
|
||||
rsi24Path.lineTo(rsiX, rsi24Y);
|
||||
}
|
||||
mRsiPaint.setColor(mRsi24Color);
|
||||
canvas.drawPath(rsi24Path, mRsiPaint);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void drawKDJ(Canvas canvas) {
|
||||
|
||||
float kdjX;
|
||||
|
||||
//k
|
||||
float kY;
|
||||
Path kPath = new Path();
|
||||
|
||||
//d
|
||||
float dY;
|
||||
Path dPath = new Path();
|
||||
|
||||
//j
|
||||
float jY;
|
||||
Path jPath = new Path();
|
||||
|
||||
float v = mBaseHeight - mBasePaddingBottom - mInnerBottomBlankPadding;
|
||||
for (int i = mBeginIndex; i < mEndIndex; i++) {
|
||||
Quotes quotes = mQuotesList.get(i);
|
||||
|
||||
/*k*/
|
||||
kdjX = mBasePaddingLeft + (i - mBeginIndex) * mPerX + mPerX / 2;
|
||||
kY = (float) (v - mPerY * (quotes.k - mMinY));
|
||||
if (i == mBeginIndex) {
|
||||
kPath.moveTo(kdjX - mPerX / 2, kY);//第一个点特殊处理
|
||||
} else {
|
||||
if (i == mEndIndex - 1) {
|
||||
kdjX += mPerX / 2;//最后一个点特殊处理
|
||||
}
|
||||
kPath.lineTo(kdjX, kY);
|
||||
}
|
||||
mKdjPaint.setColor(mKColor);
|
||||
canvas.drawPath(kPath, mKdjPaint);
|
||||
|
||||
/*d*/
|
||||
kdjX = mBasePaddingLeft + (i - mBeginIndex) * mPerX + mPerX / 2;
|
||||
dY = (float) (v - mPerY * (quotes.d - mMinY));
|
||||
if (i == mBeginIndex) {
|
||||
dPath.moveTo(kdjX - mPerX / 2, dY);//第一个点特殊处理
|
||||
} else {
|
||||
if (i == mEndIndex - 1) {
|
||||
kdjX += mPerX / 2;//最后一个点特殊处理
|
||||
}
|
||||
dPath.lineTo(kdjX, dY);
|
||||
}
|
||||
mKdjPaint.setColor(mDColor);
|
||||
canvas.drawPath(dPath, mKdjPaint);
|
||||
|
||||
/*j*/
|
||||
kdjX = mBasePaddingLeft + (i - mBeginIndex) * mPerX + mPerX / 2;
|
||||
jY = (float) (v - mPerY * (quotes.j - mMinY));
|
||||
if (i == mBeginIndex) {
|
||||
jPath.moveTo(kdjX - mPerX / 2, jY);//第一个点特殊处理
|
||||
} else {
|
||||
if (i == mEndIndex - 1) {
|
||||
kdjX += mPerX / 2;//最后一个点特殊处理
|
||||
}
|
||||
jPath.lineTo(kdjX, jY);
|
||||
}
|
||||
mKdjPaint.setColor(mJColor);
|
||||
canvas.drawPath(jPath, mKdjPaint);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void seekAndCalculateCellData() {
|
||||
if (mQuotesList == null || mQuotesList.isEmpty()) return;
|
||||
if (!isShowVol()) return;
|
||||
|
||||
FinancialAlgorithm.calculateMA(mQuotesList, 5, KViewType.MaType.volMa5);
|
||||
FinancialAlgorithm.calculateMA(mQuotesList, 10, KViewType.MaType.volMa10);
|
||||
|
||||
|
||||
//找到close最大值和最小值
|
||||
mMinY = Integer.MAX_VALUE;
|
||||
mMaxY = Integer.MIN_VALUE;
|
||||
|
||||
|
||||
for (int i = mBeginIndex; i < mEndIndex; i++) {
|
||||
Quotes quotes = mQuotesList.get(i);
|
||||
if (i == mBeginIndex) {
|
||||
mBeginQuotes = quotes;
|
||||
}
|
||||
if (i == mEndIndex - 1) {
|
||||
mEndQuotes = quotes;
|
||||
}
|
||||
double min = FinancialAlgorithm.getVolMinY(quotes);
|
||||
double max = FinancialAlgorithm.getVolMaxY(quotes);
|
||||
|
||||
if (min <= mMinY) {
|
||||
mMinY = min;
|
||||
}
|
||||
if (max >= mMaxY) {
|
||||
mMaxY = max;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
mPerX = (mBaseWidth - mBasePaddingLeft - mBasePaddingRight - mInnerRightBlankPadding)
|
||||
/ (mShownMaxCount);
|
||||
//不要忘了减去内部的上下Padding
|
||||
mPerY = (float) ((mBaseHeight - mBasePaddingTop - mBasePaddingBottom - mInnerTopBlankPadding
|
||||
- mInnerBottomBlankPadding) / (mMaxY - mMinY));
|
||||
Log.e(TAG, "seekAndCalculateCellData: mMinY:" + mMinY + ",mMaxY:" + mMaxY);
|
||||
//重绘
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void innerClickListener() {
|
||||
super.innerClickListener();
|
||||
if (mMinorType == KViewType.MinorIndicatrixType.MACD) {
|
||||
mMinorType = KViewType.MinorIndicatrixType.RSI;
|
||||
} else if (mMinorType == KViewType.MinorIndicatrixType.RSI) {
|
||||
mMinorType = KViewType.MinorIndicatrixType.KDJ;
|
||||
} else if (mMinorType == KViewType.MinorIndicatrixType.KDJ) {
|
||||
mMinorType = KViewType.MinorIndicatrixType.MACD;
|
||||
}
|
||||
setMinorType(mMinorType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void innerLongClickListener(float x, float y) {
|
||||
super.innerLongClickListener(x, y);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void innerHiddenLongClick() {
|
||||
super.innerHiddenLongClick();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void innerMoveViewListener(float moveXLen) {
|
||||
super.innerMoveViewListener(moveXLen);
|
||||
|
||||
}
|
||||
|
||||
|
||||
//-----------------------对开发者暴露可以修改的参数-------
|
||||
|
||||
public void setMinorType(KViewType.MinorIndicatrixType minorType) {
|
||||
mMinorType = minorType;
|
||||
|
||||
seekAndCalculateCellData();
|
||||
}
|
||||
|
||||
public int getOuterStrokeColor() {
|
||||
return mOuterStrokeColor;
|
||||
}
|
||||
|
||||
public VolView setOuterStrokeColor(int outerStrokeColor) {
|
||||
mOuterStrokeColor = outerStrokeColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getInnerXyDashColor() {
|
||||
return mInnerXyDashColor;
|
||||
}
|
||||
|
||||
public VolView setInnerXyDashColor(int innerXyDashColor) {
|
||||
mInnerXyDashColor = innerXyDashColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getXyTxtColor() {
|
||||
return mXyTxtColor;
|
||||
}
|
||||
|
||||
public VolView setXyTxtColor(int xyTxtColor) {
|
||||
mXyTxtColor = xyTxtColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getLegendTxtColor() {
|
||||
return mLegendTxtColor;
|
||||
}
|
||||
|
||||
public VolView setLegendTxtColor(int legendTxtColor) {
|
||||
mLegendTxtColor = legendTxtColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getLongPressTxtColor() {
|
||||
return mLongPressTxtColor;
|
||||
}
|
||||
|
||||
public VolView setLongPressTxtColor(int longPressTxtColor) {
|
||||
mLongPressTxtColor = longPressTxtColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getMacdBuyColor() {
|
||||
return mMacdBuyColor;
|
||||
}
|
||||
|
||||
public VolView setMacdBuyColor(int macdBuyColor) {
|
||||
mMacdBuyColor = macdBuyColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getMacdSellColor() {
|
||||
return mMacdSellColor;
|
||||
}
|
||||
|
||||
public VolView setMacdSellColor(int macdSellColor) {
|
||||
mMacdSellColor = macdSellColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getMacdDifColor() {
|
||||
return mMacdDifColor;
|
||||
}
|
||||
|
||||
public VolView setMacdDifColor(int macdDifColor) {
|
||||
mMacdDifColor = macdDifColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getMacdDeaColor() {
|
||||
return mMacdDeaColor;
|
||||
}
|
||||
|
||||
public VolView setMacdDeaColor(int macdDeaColor) {
|
||||
mMacdDeaColor = macdDeaColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getMacdMacdColor() {
|
||||
return mMacdMacdColor;
|
||||
}
|
||||
|
||||
public VolView setMacdMacdColor(int macdMacdColor) {
|
||||
mMacdMacdColor = macdMacdColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Paint getMacdPaint() {
|
||||
return mMacdPaint;
|
||||
}
|
||||
|
||||
public VolView setMacdPaint(Paint macdPaint) {
|
||||
mMacdPaint = macdPaint;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getMacdLineWidth() {
|
||||
return mMacdLineWidth;
|
||||
}
|
||||
|
||||
public VolView setMacdLineWidth(float macdLineWidth) {
|
||||
mMacdLineWidth = macdLineWidth;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getRsi6Color() {
|
||||
return mRsi6Color;
|
||||
}
|
||||
|
||||
public VolView setRsi6Color(int rsi6Color) {
|
||||
mRsi6Color = rsi6Color;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getRsi12Color() {
|
||||
return mRsi12Color;
|
||||
}
|
||||
|
||||
public VolView setRsi12Color(int rsi12Color) {
|
||||
mRsi12Color = rsi12Color;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getRsi24Color() {
|
||||
return mRsi24Color;
|
||||
}
|
||||
|
||||
public VolView setRsi24Color(int rsi24Color) {
|
||||
mRsi24Color = rsi24Color;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Paint getRsiPaint() {
|
||||
return mRsiPaint;
|
||||
}
|
||||
|
||||
public VolView setRsiPaint(Paint rsiPaint) {
|
||||
mRsiPaint = rsiPaint;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getRsiLineWidth() {
|
||||
return mRsiLineWidth;
|
||||
}
|
||||
|
||||
public VolView setRsiLineWidth(float rsiLineWidth) {
|
||||
mRsiLineWidth = rsiLineWidth;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getKColor() {
|
||||
return mKColor;
|
||||
}
|
||||
|
||||
public VolView setKColor(int KColor) {
|
||||
mKColor = KColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getDColor() {
|
||||
return mDColor;
|
||||
}
|
||||
|
||||
public VolView setDColor(int DColor) {
|
||||
mDColor = DColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getJColor() {
|
||||
return mJColor;
|
||||
}
|
||||
|
||||
public VolView setJColor(int JColor) {
|
||||
mJColor = JColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Paint getKdjPaint() {
|
||||
return mKdjPaint;
|
||||
}
|
||||
|
||||
public VolView setKdjPaint(Paint kdjPaint) {
|
||||
mKdjPaint = kdjPaint;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getKdjLineWidth() {
|
||||
return mKdjLineWidth;
|
||||
}
|
||||
|
||||
public VolView setKdjLineWidth(float kdjLineWidth) {
|
||||
mKdjLineWidth = kdjLineWidth;
|
||||
return this;
|
||||
}
|
||||
|
||||
public KViewType.MinorIndicatrixType getMinorType() {
|
||||
return mMinorType;
|
||||
}
|
||||
|
||||
public double getMinY() {
|
||||
return mMinY;
|
||||
}
|
||||
|
||||
public VolView setMinY(double minY) {
|
||||
mMinY = minY;
|
||||
return this;
|
||||
}
|
||||
|
||||
public double getMaxY() {
|
||||
return mMaxY;
|
||||
}
|
||||
|
||||
public VolView setMaxY(double maxY) {
|
||||
mMaxY = maxY;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getCandleDiverWidthRatio() {
|
||||
return mCandleDiverWidthRatio;
|
||||
}
|
||||
|
||||
public VolView setCandleDiverWidthRatio(float candleDiverWidthRatio) {
|
||||
mCandleDiverWidthRatio = candleDiverWidthRatio;
|
||||
return this;
|
||||
}
|
||||
|
||||
public VolView setVolListener(KViewListener.MinorListener volListener) {
|
||||
mVolListener = volListener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public KViewListener.MinorListener getVolListener() {
|
||||
return mVolListener;
|
||||
}
|
||||
|
||||
public boolean isShowVol() {
|
||||
return mShowVol;
|
||||
}
|
||||
|
||||
public VolView setShowVol(boolean showVol) {
|
||||
mShowVol = showVol;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package com.tophold.trade.view.pie;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.Transformation;
|
||||
|
||||
import com.tophold.trade.utils.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
* 作 者 : wgyscsf@163.com
|
||||
* 更新时间 :2018/07/17 16:50
|
||||
* 描 述 :
|
||||
* ============================================================
|
||||
*/
|
||||
public class PieChartAnimation extends Animation {
|
||||
public static final String TAG = PieChartAnimation.class.getSimpleName();
|
||||
List<PieEntrys> mPieEntrysList;
|
||||
float mSumValue;
|
||||
PieChartView mView;
|
||||
|
||||
public void setPieChartData(List<PieEntrys> pieEntrysList, float sumValue, PieChartView view) {
|
||||
this.mPieEntrysList = pieEntrysList;
|
||||
this.mSumValue = sumValue;
|
||||
this.mView = view;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyTransformation(float interpolatedTime, Transformation t) {
|
||||
super.applyTransformation(interpolatedTime, t);
|
||||
if (StringUtils.isEmpty(mPieEntrysList) || mSumValue <= 0 || mView == null) return;
|
||||
Log.d(TAG, "applyTransformation1: " + interpolatedTime);
|
||||
if (interpolatedTime == 1.0f) {
|
||||
mView.setDrawLine(true);
|
||||
}
|
||||
for (int i = 0; i < mPieEntrysList.size(); i++) {
|
||||
PieEntrys data = mPieEntrysList.get(i);
|
||||
//通过总和来计算百分比
|
||||
float percentage = data.value / mSumValue;
|
||||
//通过百分比来计算对应的角度
|
||||
float angle = percentage * 360;
|
||||
//根据插入时间来计算角度
|
||||
angle = angle * interpolatedTime;
|
||||
data.mSweepAngle = angle;
|
||||
}
|
||||
mView.invalidate();
|
||||
}
|
||||
}
|
||||
+552
@@ -0,0 +1,552 @@
|
||||
package com.tophold.trade.view.pie;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
|
||||
import com.tophold.trade.R;
|
||||
import com.tophold.trade.utils.GsonUtil;
|
||||
import com.tophold.trade.utils.ScreenUtils;
|
||||
import com.tophold.trade.utils.StringUtils;
|
||||
import com.tophold.trade.view.BaseView;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
* 作 者 : wgyscsf@163.com
|
||||
* 更新时间 :2018/07/10 20:40
|
||||
* 描 述 :对饼形图实现给出一个思路。之前看MP实现如果改成这样可能比较麻烦一点,这里就直接进行是实现。
|
||||
* 饼形图的实现难点不在于图的实现,而在于图周围的指示文字的控制,如果处理不好就会出现挤压问题。这里的处理思路是:给出最小比例,
|
||||
* 如果小于这个比例直接从最大份中"借"出一点满足最小比例。
|
||||
* ============================================================
|
||||
*/
|
||||
public class PieChartView extends BaseView {
|
||||
|
||||
public static final float DEF_PARTSTHRESHOLD = 0.1f;
|
||||
|
||||
List<PieEntrys> mPieEntryList;
|
||||
|
||||
/**
|
||||
* 内边距
|
||||
*/
|
||||
float basePaddingTop = 45;
|
||||
float basePaddingBottom = 45;
|
||||
float basePaddingLeft;
|
||||
float basePaddingRight;
|
||||
|
||||
//内部圆洞的半径
|
||||
float holdRadius = 40;
|
||||
//高亮环的宽度
|
||||
float highLightWidth = 4;
|
||||
//高亮环距离圆环的距离
|
||||
float highLightPadding = 5;
|
||||
|
||||
//指示线距离圆环的边距
|
||||
float lineLengthMargin = 10;
|
||||
//指示线延伸长度
|
||||
float lineLength = 85;
|
||||
//指示线的宽度
|
||||
float lineWidth = 1f;
|
||||
//是否延伸长度的阀值
|
||||
float lineThreshold = 10;
|
||||
//延长的宽度
|
||||
float lineThresholdLength = 50;
|
||||
|
||||
//指示文字下边距
|
||||
float textPaddingBottom = 2;
|
||||
//圆点半径
|
||||
float dotRadius = 2;
|
||||
|
||||
//上下两个txt之间的y轴的距离
|
||||
float txtThresholdmargin = 20;
|
||||
//上下两个txt之间的y轴的阀值
|
||||
float txtThreshold = 20;
|
||||
|
||||
//文字大小
|
||||
float textSize = 12;
|
||||
//饼图item最小的占比。0~1,定义最小的item,防止积压在一起。初始值设置小一点,为了动画好看。
|
||||
float minPartsThreshold = DEF_PARTSTHRESHOLD;
|
||||
|
||||
|
||||
//没有数据显示
|
||||
String mEmptyTxt = "还没有交易哦";
|
||||
|
||||
//开始角度
|
||||
float mBeginAngle = -90;
|
||||
|
||||
/**
|
||||
* 自定义动画
|
||||
*/
|
||||
private PieChartAnimation mAnimation;
|
||||
long mAnim = 1000;
|
||||
|
||||
//私有属性,不对外暴露
|
||||
private float centerX;
|
||||
private float centerY;
|
||||
private boolean firstCome = true;
|
||||
|
||||
boolean mDrawLine = false;
|
||||
|
||||
|
||||
public PieChartView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public PieChartView(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public PieChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initAttrs();
|
||||
}
|
||||
|
||||
private void initAttrs() {
|
||||
mAnimation = new PieChartAnimation();
|
||||
mAnimation.setDuration(mAnim);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有单位传递标准化参数即可,这里进行转换。
|
||||
*/
|
||||
private void allDp2Px() {
|
||||
if (!firstCome) return;
|
||||
firstCome = false;
|
||||
|
||||
basePaddingLeft = ScreenUtils.dip2px(basePaddingLeft);
|
||||
basePaddingRight = ScreenUtils.dip2px(basePaddingRight);
|
||||
basePaddingTop = ScreenUtils.dip2px(basePaddingTop);
|
||||
basePaddingBottom = ScreenUtils.dip2px(basePaddingBottom);
|
||||
highLightWidth = ScreenUtils.dip2px(highLightWidth);
|
||||
highLightPadding = ScreenUtils.dip2px(highLightPadding);
|
||||
holdRadius = ScreenUtils.dip2px(holdRadius);
|
||||
lineLength = ScreenUtils.dip2px(lineLength);
|
||||
lineWidth = ScreenUtils.dip2px(lineWidth);
|
||||
dotRadius = ScreenUtils.dip2px(dotRadius);
|
||||
textSize = ScreenUtils.sp2px(textSize);
|
||||
textPaddingBottom = ScreenUtils.dip2px(textPaddingBottom);
|
||||
lineThreshold = ScreenUtils.dip2px(lineThreshold);
|
||||
lineThresholdLength = ScreenUtils.dip2px(lineThresholdLength);
|
||||
txtThreshold = ScreenUtils.dip2px(txtThreshold);
|
||||
txtThresholdmargin = ScreenUtils.dip2px(txtThresholdmargin);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
allDp2Px();
|
||||
|
||||
centerX = getBaseWidth() / 2.0f;
|
||||
centerY = getBaseHeight() / 2.0f;
|
||||
|
||||
|
||||
drawCircle(canvas);
|
||||
}
|
||||
|
||||
|
||||
private void initPieData() {
|
||||
if (StringUtils.isEmptyList(mPieEntryList)) {
|
||||
return;
|
||||
}
|
||||
float sumValue = 0;
|
||||
int maxIndex = 0;
|
||||
float maxData = -1;
|
||||
//数据预处理:对占比比较小的值多分配一点防止太小。
|
||||
for (int i = 0; i < mPieEntryList.size(); i++) {
|
||||
PieEntrys pieEntry = mPieEntryList.get(i);
|
||||
sumValue += pieEntry.value;
|
||||
if (pieEntry.value > maxData) {
|
||||
maxData = pieEntry.value;
|
||||
maxIndex = i;
|
||||
}
|
||||
}
|
||||
mAnimation.setPieChartData(mPieEntryList, sumValue, this);
|
||||
/**
|
||||
* 边界问题处理:防止item太小,出现文字积压问题。
|
||||
*/
|
||||
PieEntrys maxPieEntry = mPieEntryList.get(maxIndex);
|
||||
for (int i = 0; i < mPieEntryList.size(); i++) {
|
||||
PieEntrys pieEntry = mPieEntryList.get(i);
|
||||
float tempValue = pieEntry.value;
|
||||
if (pieEntry.value / sumValue < minPartsThreshold) {
|
||||
//该块补全最小值
|
||||
pieEntry.value = sumValue * minPartsThreshold;
|
||||
float disLen = pieEntry.value - tempValue;
|
||||
//最大块减去被减去的
|
||||
maxPieEntry.value -= disLen;
|
||||
}
|
||||
}
|
||||
|
||||
//startAnimation的时候,如果这个View是不可见的,或者是gone的,就会导致传进去的Animation对象不执行
|
||||
postDelayed(() -> startAnimation(mAnimation), 300);
|
||||
}
|
||||
|
||||
private void drawCircle(Canvas canvas) {
|
||||
if (StringUtils.isEmptyList(mPieEntryList)) {
|
||||
drawEmptyView(canvas);
|
||||
return;
|
||||
}
|
||||
|
||||
//正式开始处理
|
||||
float valueW = getBaseWidth() - basePaddingLeft - basePaddingRight;
|
||||
float valueH = getBaseHeight() - basePaddingTop - basePaddingBottom;
|
||||
float radius = Math.min(valueW,
|
||||
valueH) / 2.0f;
|
||||
float lX = centerX - radius;
|
||||
float tY = centerY - radius;
|
||||
float rX = centerX + radius;
|
||||
float bY = centerY + radius;
|
||||
//圆环外边界,但是如果圆环成整个圆之后,就是内边界了。
|
||||
RectF rectF = new RectF(lX, tY, rX, bY);
|
||||
float beginAngle = mBeginAngle;
|
||||
float preYpos = 0;
|
||||
for (PieEntrys pieEntry : mPieEntryList) {
|
||||
|
||||
//无动画
|
||||
//float sweepAngle = 360 * pieEntry.value / mSumValue;
|
||||
//在这里实现动画
|
||||
float sweepAngle = pieEntry.mSweepAngle;
|
||||
|
||||
//防止item过小
|
||||
if (sweepAngle < 360 * minPartsThreshold) {
|
||||
sweepAngle = 360 * minPartsThreshold;
|
||||
}
|
||||
//防止绘制过度
|
||||
if (beginAngle + sweepAngle > 270) {
|
||||
sweepAngle = 270 - beginAngle;
|
||||
}
|
||||
|
||||
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
//paint.setStrokeWidth(roundWidth);
|
||||
paint.setColor(pieEntry.colorBg);
|
||||
canvas.drawArc(rectF, beginAngle, sweepAngle, true, paint);
|
||||
//高亮逻辑
|
||||
if (pieEntry.highLight) {
|
||||
float radius2 = Math.min(
|
||||
valueW,
|
||||
valueH) / 2.0f + highLightPadding + highLightWidth / 2.0f;
|
||||
|
||||
float lX2 = centerX - radius2;
|
||||
float tY2 = centerY - radius2;
|
||||
float rX2 = centerX + radius2;
|
||||
float bY2 = centerY + radius2;
|
||||
|
||||
RectF rectF2 = new RectF(lX2, tY2, rX2, bY2);
|
||||
Paint paint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
paint2.setStyle(Paint.Style.STROKE);
|
||||
paint2.setStrokeWidth(highLightWidth);
|
||||
paint2.setColor(pieEntry.colorBg);
|
||||
canvas.drawArc(rectF2, beginAngle, sweepAngle, false, paint2);
|
||||
}
|
||||
|
||||
if (mDrawLine) {
|
||||
/**
|
||||
* 指示文字,重要逻辑
|
||||
* 思路:取该模块的度数的中间值向外延伸一定距离。如果该度数在[-90,90]之间,在右边显示;否则在左边显示。
|
||||
*/
|
||||
//真实的角度
|
||||
float coreAngle = beginAngle + sweepAngle / 2.0f;//因为从-90开始的
|
||||
float r = radius;//圆的半径
|
||||
|
||||
Paint linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
linePaint.setColor(pieEntry.colorBg);
|
||||
linePaint.setStrokeWidth(lineWidth);
|
||||
|
||||
Paint ratePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
ratePaint.setColor(pieEntry.highLight ? getColor(R.color.color_pie_chart_red) : getColor(R.color.color_pie_chart_gray));
|
||||
ratePaint.setTextSize(textSize);
|
||||
|
||||
Paint symbolPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
symbolPaint.setColor(getColor(R.color.color_pie_chart_gray));
|
||||
symbolPaint.setTextSize(textSize);
|
||||
|
||||
|
||||
float yPos;
|
||||
//右边
|
||||
//Log.d(TAG, "drawCircle: " + coreAngle);
|
||||
float textPdding = getFontHeight(textSize, ratePaint);
|
||||
if (coreAngle >= -90 && coreAngle <= 90) {
|
||||
float circleX = centerX + radius + highLightPadding + highLightWidth;
|
||||
float xPos = circleX + lineLengthMargin;//x的坐标
|
||||
boolean isThreshold = false;
|
||||
if (coreAngle <= 0) {
|
||||
yPos = (float) (centerY - Math.cos(getAreaAngle(90 + coreAngle)) * r);//y的左边
|
||||
//在上边界或者下边界x方向延伸长一点
|
||||
if (r - (centerY - yPos) < lineThreshold) {
|
||||
xPos -= lineThresholdLength;
|
||||
isThreshold = true;
|
||||
}
|
||||
} else {
|
||||
yPos = (float) (centerY + Math.cos(getAreaAngle(90 - coreAngle)) * r);//y的左边
|
||||
//在上边界或者下边界x方向延伸长一点
|
||||
if (r - (yPos - centerY) < lineThreshold) {
|
||||
xPos -= lineThresholdLength;
|
||||
isThreshold = true;
|
||||
}
|
||||
}
|
||||
|
||||
//又一个边界问题
|
||||
if (preYpos != 0 && yPos - preYpos < txtThreshold) {
|
||||
yPos += txtThresholdmargin;
|
||||
}
|
||||
preYpos = yPos;
|
||||
|
||||
//Log.d(TAG, "drawCircle: " + xPos + "," + yPos);
|
||||
float lineLeft = !isThreshold ? xPos + lineLength : xPos + lineLength + lineThresholdLength;
|
||||
float txtLeft = !isThreshold ? xPos : xPos + lineThresholdLength;
|
||||
canvas.drawLine(xPos, yPos, lineLeft, yPos, linePaint);
|
||||
canvas.drawCircle(xPos, yPos, dotRadius, linePaint);
|
||||
float labelLen = lineLength - ratePaint.measureText(pieEntry.label);
|
||||
canvas.drawText(pieEntry.label, txtLeft + labelLen, yPos - textPaddingBottom, ratePaint);
|
||||
float symbolLen = lineLength - symbolPaint.measureText(pieEntry.symbol);
|
||||
canvas.drawText(pieEntry.symbol, txtLeft + symbolLen, yPos + textPaddingBottom + textPdding / 2.0f, symbolPaint);
|
||||
} else {
|
||||
float circleX = centerX - radius - highLightPadding - highLightWidth;
|
||||
float xPos = circleX + lineLengthMargin;//x的坐标
|
||||
boolean isThreshold = false;
|
||||
//左边
|
||||
if (coreAngle <= 180) {
|
||||
yPos = (float) (centerY + Math.cos(getAreaAngle(coreAngle - 90)) * r);//y的左边
|
||||
//在上边界或者下边界x方向延伸长一点
|
||||
if (r - (yPos - centerY) < lineThreshold) {
|
||||
xPos += lineThresholdLength;
|
||||
isThreshold = true;
|
||||
}
|
||||
} else {
|
||||
yPos = (float) (centerY - Math.cos(getAreaAngle(270 - coreAngle)) * r);//y的左边
|
||||
if (r - (centerY - yPos) < lineThreshold) {
|
||||
xPos += lineThresholdLength;
|
||||
isThreshold = true;
|
||||
}
|
||||
}
|
||||
|
||||
//又一个边界问题
|
||||
if (preYpos != 0 && preYpos - yPos < txtThreshold) {
|
||||
yPos -= txtThresholdmargin;
|
||||
}
|
||||
preYpos = yPos;
|
||||
|
||||
//Log.d(TAG, "drawCircle: " + xPos + "," + yPos);
|
||||
float lineLeft = !isThreshold ? xPos - lineLength : xPos - lineLength - lineThresholdLength;
|
||||
float txtLeft = !isThreshold ? xPos - lineLength : xPos - lineLength - lineThresholdLength;
|
||||
|
||||
canvas.drawLine(xPos, yPos, lineLeft, yPos, linePaint);
|
||||
canvas.drawCircle(xPos, yPos, dotRadius, linePaint);
|
||||
canvas.drawText(pieEntry.label, txtLeft, yPos - textPaddingBottom, ratePaint);
|
||||
canvas.drawText(pieEntry.symbol, txtLeft, yPos + textPaddingBottom + textPdding / 2.0f, symbolPaint);
|
||||
}
|
||||
}
|
||||
beginAngle += sweepAngle;
|
||||
}
|
||||
|
||||
//最后绘制中间的圆洞
|
||||
Paint holdPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
holdPaint.setColor(ContextCompat.getColor(getContext(), R.color.color_minorView_longPressTxtColor));
|
||||
canvas.drawCircle(centerX, centerY, holdRadius, holdPaint);
|
||||
|
||||
}
|
||||
|
||||
private float getAreaAngle(float angle) {
|
||||
return (float) (Math.PI * angle / 180);
|
||||
}
|
||||
|
||||
private void drawEmptyView(Canvas canvas) {
|
||||
String txt = mEmptyTxt;
|
||||
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
paint.setTextSize(ScreenUtils.sp2px(15));
|
||||
paint.setColor(getColor(R.color.color_pie_chart_gray));
|
||||
canvas.drawText(txt, getBaseWidth() / 2.0f - paint.measureText(txt) / 2.0f, getBaseHeight() / 2.0f, paint);
|
||||
}
|
||||
|
||||
public List<PieEntrys> getPieEntryList() {
|
||||
return mPieEntryList;
|
||||
}
|
||||
|
||||
public PieChartView setPieEntryList(List<PieEntrys> pieEntryList) {
|
||||
mPieEntryList = pieEntryList;
|
||||
initPieData();
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getBasePaddingTop() {
|
||||
return basePaddingTop;
|
||||
}
|
||||
|
||||
public PieChartView setBasePaddingTop(float basePaddingTop) {
|
||||
this.basePaddingTop = basePaddingTop;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getBasePaddingBottom() {
|
||||
return basePaddingBottom;
|
||||
}
|
||||
|
||||
public PieChartView setBasePaddingBottom(float basePaddingBottom) {
|
||||
this.basePaddingBottom = basePaddingBottom;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getBasePaddingLeft() {
|
||||
return basePaddingLeft;
|
||||
}
|
||||
|
||||
public PieChartView setBasePaddingLeft(float basePaddingLeft) {
|
||||
this.basePaddingLeft = basePaddingLeft;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getBasePaddingRight() {
|
||||
return basePaddingRight;
|
||||
}
|
||||
|
||||
public PieChartView setBasePaddingRight(float basePaddingRight) {
|
||||
this.basePaddingRight = basePaddingRight;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getHighLightWidth() {
|
||||
return highLightWidth;
|
||||
}
|
||||
|
||||
public PieChartView setHighLightWidth(float highLightWidth) {
|
||||
this.highLightWidth = highLightWidth;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getHighLightPadding() {
|
||||
return highLightPadding;
|
||||
}
|
||||
|
||||
public PieChartView setHighLightPadding(float highLightPadding) {
|
||||
this.highLightPadding = highLightPadding;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getLineLengthMargin() {
|
||||
return lineLengthMargin;
|
||||
}
|
||||
|
||||
public PieChartView setLineLengthMargin(float lineLengthMargin) {
|
||||
this.lineLengthMargin = lineLengthMargin;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getLineLength() {
|
||||
return lineLength;
|
||||
}
|
||||
|
||||
public PieChartView setLineLength(float lineLength) {
|
||||
this.lineLength = lineLength;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getLineWidth() {
|
||||
return lineWidth;
|
||||
}
|
||||
|
||||
public PieChartView setLineWidth(float lineWidth) {
|
||||
this.lineWidth = lineWidth;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getLineThreshold() {
|
||||
return lineThreshold;
|
||||
}
|
||||
|
||||
public PieChartView setLineThreshold(float lineThreshold) {
|
||||
this.lineThreshold = lineThreshold;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getLineThresholdLength() {
|
||||
return lineThresholdLength;
|
||||
}
|
||||
|
||||
public PieChartView setLineThresholdLength(float lineThresholdLength) {
|
||||
this.lineThresholdLength = lineThresholdLength;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getTextPaddingBottom() {
|
||||
return textPaddingBottom;
|
||||
}
|
||||
|
||||
public PieChartView setTextPaddingBottom(float textPaddingBottom) {
|
||||
this.textPaddingBottom = textPaddingBottom;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getDotRadius() {
|
||||
return dotRadius;
|
||||
}
|
||||
|
||||
public PieChartView setDotRadius(float dotRadius) {
|
||||
this.dotRadius = dotRadius;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getTxtThresholdmargin() {
|
||||
return txtThresholdmargin;
|
||||
}
|
||||
|
||||
public PieChartView setTxtThresholdmargin(float txtThresholdmargin) {
|
||||
this.txtThresholdmargin = txtThresholdmargin;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getTxtThreshold() {
|
||||
return txtThreshold;
|
||||
}
|
||||
|
||||
public PieChartView setTxtThreshold(float txtThreshold) {
|
||||
this.txtThreshold = txtThreshold;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getTextSize() {
|
||||
return textSize;
|
||||
}
|
||||
|
||||
public PieChartView setTextSize(float textSize) {
|
||||
this.textSize = textSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getMinPartsThreshold() {
|
||||
return minPartsThreshold;
|
||||
}
|
||||
|
||||
public PieChartView setMinPartsThreshold(float minPartsThreshold) {
|
||||
this.minPartsThreshold = minPartsThreshold;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getEmptyTxt() {
|
||||
return mEmptyTxt;
|
||||
}
|
||||
|
||||
public PieChartView setEmptyTxt(String emptyTxt) {
|
||||
mEmptyTxt = emptyTxt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PieChartView setDrawLine(boolean drawLine) {
|
||||
mDrawLine = drawLine;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.tophold.trade.view.pie;
|
||||
|
||||
import android.support.annotation.ColorInt;
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
* 作 者 : wgyscsf@163.com
|
||||
* 更新时间 :2018/07/10 20:36
|
||||
* 描 述 :
|
||||
* ============================================================
|
||||
*/
|
||||
public class PieEntrys {
|
||||
public float value;
|
||||
public String label;
|
||||
@ColorInt
|
||||
public int colorBg;
|
||||
public boolean highLight;
|
||||
public String symbol;
|
||||
|
||||
public PieEntrys(float value, String label, int colorBg, boolean highLight, String symbol) {
|
||||
this.value = value;
|
||||
this.label = label;
|
||||
this.colorBg = colorBg;
|
||||
this.highLight = highLight;
|
||||
this.symbol = symbol;
|
||||
}
|
||||
|
||||
//一些额外的字段辅助画图
|
||||
//单个item需要扫过的角度
|
||||
public float mSweepAngle;
|
||||
|
||||
}
|
||||
+502
@@ -0,0 +1,502 @@
|
||||
package com.tophold.trade.view.seekbar;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
|
||||
import com.tophold.trade.utils.RenderUtils;
|
||||
import com.tophold.trade.view.BaseView;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
* 作 者 : wgyscsf@163.com
|
||||
* 更新时间 :2018/11/30 14:00
|
||||
* 描 述 :
|
||||
* ============================================================
|
||||
*/
|
||||
public class DoubleThumbSeekBar extends BaseView {
|
||||
|
||||
private DoubleTumb mDoubleThumb;
|
||||
|
||||
//背景相关
|
||||
private Paint mBackgroudPaint;
|
||||
private RectF mBackgroudRectF;
|
||||
|
||||
//seekbar四个顶点
|
||||
private int[] mPostionArr;
|
||||
|
||||
//前景图1
|
||||
private Paint mForegroundPaintA;
|
||||
private RectF mForegroundRectFA;
|
||||
private Bitmap mThumbBitmapA;
|
||||
private Paint mTipPaintA;
|
||||
|
||||
//前景图2
|
||||
private Paint mForegroundPaintB;
|
||||
private RectF mForegroundRectFB;
|
||||
private Bitmap mThumbBitmapB;
|
||||
private Paint mTipPaintB;
|
||||
|
||||
//滑动监听
|
||||
private OnDoubleThumbChangeListener mOnDoubleThumbChangeListener;
|
||||
|
||||
//是否拦截事件
|
||||
private boolean mDisallowIntercept = false;
|
||||
|
||||
/**
|
||||
* 设置监听
|
||||
*
|
||||
* @param onDoubleThumbChangeListener
|
||||
*/
|
||||
public void setOnDoubleThumbChangeListener(OnDoubleThumbChangeListener onDoubleThumbChangeListener) {
|
||||
mOnDoubleThumbChangeListener = onDoubleThumbChangeListener;
|
||||
}
|
||||
|
||||
|
||||
public DoubleThumbSeekBar(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public DoubleThumbSeekBar(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public DoubleThumbSeekBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public void init(DoubleTumb doubleThumb) {
|
||||
this.mDoubleThumb = doubleThumb;
|
||||
initArrts();
|
||||
}
|
||||
|
||||
private void initArrts() {
|
||||
if (mDoubleThumb == null) {
|
||||
throw new IllegalArgumentException("请先初始化:init(DoubleThumbSeekBar doubleThumbSeekBar)");
|
||||
}
|
||||
initPaint();
|
||||
}
|
||||
|
||||
private void initPaint() {
|
||||
mBackgroudPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mBackgroudPaint.setColor(mDoubleThumb.mBackground);
|
||||
mBackgroudPaint.setStyle(Paint.Style.FILL);
|
||||
mBackgroudRectF = new RectF();
|
||||
|
||||
/**
|
||||
* thumbA相关设置
|
||||
*/
|
||||
mForegroundPaintA = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
Drawable foregroundDrawableA = mDoubleThumb.mThumbA.mForegroundDrawable;
|
||||
if (foregroundDrawableA != null) {
|
||||
// TODO: 03/12/2018
|
||||
} else {
|
||||
mForegroundPaintA.setColor(mDoubleThumb.mThumbA.mForeground);
|
||||
}
|
||||
mForegroundPaintA.setStyle(Paint.Style.FILL);
|
||||
|
||||
mForegroundRectFA = new RectF();
|
||||
//tipA
|
||||
mTipPaintA = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mTipPaintA.setStyle(Paint.Style.STROKE);
|
||||
mTipPaintA.setColor(mDoubleThumb.mThumbA.mForeground);
|
||||
mTipPaintA.setTextSize(sp2px(17));
|
||||
|
||||
|
||||
/**
|
||||
* thumbB相关设置
|
||||
*/
|
||||
DoubleTumb.Thumb thumbB = mDoubleThumb.mThumbB;
|
||||
if (thumbB != null) {
|
||||
mForegroundPaintB = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mForegroundPaintB.setColor(thumbB.mForeground);
|
||||
|
||||
Drawable foregroundDrawableB = thumbB.mForegroundDrawable;
|
||||
if (foregroundDrawableB != null) {
|
||||
// mForegroundPaintA.
|
||||
// TODO: 03/12/2018
|
||||
} else {
|
||||
mForegroundPaintB.setColor(thumbB.mForeground);
|
||||
}
|
||||
|
||||
mForegroundPaintB.setStyle(Paint.Style.FILL);
|
||||
mForegroundRectFB = new RectF();
|
||||
//tipB
|
||||
mTipPaintB = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mTipPaintB.setStyle(Paint.Style.STROKE);
|
||||
mTipPaintB.setColor(mDoubleThumb.mThumbB.mForeground);
|
||||
mTipPaintB.setTextSize(sp2px(17));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
if (mDoubleThumb == null) {
|
||||
throw new IllegalArgumentException("请初始化init()");
|
||||
}
|
||||
drawBg(canvas);
|
||||
//绘制前景图
|
||||
drawForegroundThumbA(canvas);
|
||||
drawForegroundThumbB(canvas);
|
||||
|
||||
//绘制thumb
|
||||
drawThumbA(canvas);
|
||||
drawThumbB(canvas);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
float x = event.getX();
|
||||
float y = event.getY();
|
||||
DoubleTumb.Thumb mThumbA = mDoubleThumb.mThumbA;
|
||||
DoubleTumb.Thumb mThumbB = mDoubleThumb.mThumbB;
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
if (mDisallowIntercept) getParent().requestDisallowInterceptTouchEvent(true);
|
||||
/**
|
||||
* 判断当前手指是否在触摸thumbA和thumb(存在时)。如果thumbA和thumbB同时存在,优先thumbA为主,且只能触摸一个。
|
||||
*/
|
||||
boolean touchThumbA = mThumbA.isTouchThumb(getContext(), x, y);
|
||||
mThumbA.mTouch = touchThumbA;
|
||||
if (!touchThumbA) {
|
||||
if (mThumbB != null) {
|
||||
mThumbB.mTouch = mThumbB.isTouchThumb(getContext(), x, y);
|
||||
Log.d(TAG, "onTouchEvent: " + Arrays.toString(mThumbA.getmTumbPosArr()) + "," + x + "," + y + "," + mThumbA.mTouch);
|
||||
}
|
||||
} else {
|
||||
if (mThumbB != null) {
|
||||
mThumbB.mTouch = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
if (mDisallowIntercept) getParent().requestDisallowInterceptTouchEvent(true);
|
||||
if (mThumbA.mTouch) {
|
||||
//注意左右方向
|
||||
float len = (mPostionArr[2] - mPostionArr[0]);
|
||||
float part = mThumbA.mFromLeft ? (x - mPostionArr[0]) : -(x - mPostionArr[2]);
|
||||
mThumbA.progress = part / len;
|
||||
if (mThumbA.progress <= 0) {
|
||||
mThumbA.progress = 0;
|
||||
}
|
||||
if (mThumbA.progress >= 1) {
|
||||
mThumbA.progress = 1;
|
||||
}
|
||||
if (mOnDoubleThumbChangeListener != null) {
|
||||
mOnDoubleThumbChangeListener.onProgressChanged(this, mThumbA.progress, true, true);
|
||||
}
|
||||
|
||||
//刷新
|
||||
invalidate();
|
||||
|
||||
} else if (mThumbB != null && mThumbB.mTouch) {
|
||||
//注意左右方向
|
||||
float len = (mPostionArr[2] - mPostionArr[0]);
|
||||
float part = mThumbB.mFromLeft ? (x - mPostionArr[0]) : -(x - mPostionArr[2]);
|
||||
mThumbB.progress = part / len;
|
||||
if (mThumbB.progress <= 0) {
|
||||
mThumbB.progress = 0;
|
||||
}
|
||||
if (mThumbB.progress >= 1) {
|
||||
mThumbB.progress = 1;
|
||||
}
|
||||
if (mOnDoubleThumbChangeListener != null) {
|
||||
mOnDoubleThumbChangeListener.onProgressChanged(this, mThumbB.progress, false, true);
|
||||
}
|
||||
|
||||
//刷新
|
||||
invalidate();
|
||||
} else {
|
||||
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
if (mDisallowIntercept)
|
||||
getParent().requestDisallowInterceptTouchEvent(false);
|
||||
mThumbA.mTouch = false;
|
||||
if (mThumbB != null) mThumbB.mTouch = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void drawForegroundThumbB(Canvas canvas) {
|
||||
if (mDoubleThumb.mThumbB == null) return;
|
||||
|
||||
DoubleTumb.Thumb mLeftThumb = mDoubleThumb.mThumbB;
|
||||
boolean mFromLeft = mLeftThumb.mFromLeft;
|
||||
double progress = mLeftThumb.progress;
|
||||
int[] ints = getmPostionArr();
|
||||
float l = mFromLeft ? ints[0] : ints[0] + (float) ((ints[2] - ints[0]) * (1 - progress));
|
||||
float t = ints[1];
|
||||
float r = mFromLeft ? ints[0] + (float) ((ints[2] - ints[0]) * progress) : ints[2];
|
||||
float b = ints[3];
|
||||
|
||||
Log.d(TAG, "drawForegroundThumbA: " + progress);
|
||||
//绘制背景
|
||||
mForegroundRectFB.set(l, t, r, b);
|
||||
canvas.drawRoundRect(mForegroundRectFB, mDoubleThumb.mCorners, mDoubleThumb.mCorners, mForegroundPaintB);
|
||||
}
|
||||
|
||||
private void drawThumbB(Canvas canvas) {
|
||||
if (mDoubleThumb.mThumbB == null) return;
|
||||
|
||||
DoubleTumb.Thumb mThumbB = mDoubleThumb.mThumbB;
|
||||
boolean mFromLeft = mThumbB.mFromLeft;
|
||||
double progress = mThumbB.progress;
|
||||
int[] ints = getmPostionArr();
|
||||
float l = mFromLeft ? ints[0] : ints[0] + (float) ((ints[2] - ints[0]) * (1 - progress));
|
||||
float t = ints[1];
|
||||
float r = mFromLeft ? ints[0] + (float) ((ints[2] - ints[0]) * progress) : ints[2];
|
||||
float b = ints[3];
|
||||
|
||||
//开始绘制thumb
|
||||
Bitmap thumbA = getThumbB();
|
||||
float[] tumbPosArr = mThumbB.getmTumbPosArr();
|
||||
tumbPosArr[0] = mFromLeft ? r - mThumbB.getmThumbWidth() / 2.0f : l - mThumbB.getmThumbWidth() / 2.0f;
|
||||
tumbPosArr[1] = t - dp2px(10);
|
||||
tumbPosArr[2] = tumbPosArr[0] + mThumbB.getmThumbWidth() / 2.0f;
|
||||
tumbPosArr[3] = ints[1] + mThumbB.getmThumbHight();
|
||||
canvas.drawBitmap(thumbA, tumbPosArr[0], tumbPosArr[1], mForegroundPaintB);
|
||||
|
||||
|
||||
//绘制tips
|
||||
if (mThumbB.mShowTips) {
|
||||
double len = mDoubleThumb.mMax - mDoubleThumb.mMin;
|
||||
|
||||
/**
|
||||
* 从左边开始计算最小值还是最右边和前景图无关,由开发者手动设置
|
||||
*/
|
||||
double realProcess = 0;
|
||||
if (mThumbB.mFromLeft) {
|
||||
if (mDoubleThumb.mMinLeft) {
|
||||
realProcess = progress;
|
||||
} else {
|
||||
realProcess = (1 - progress);
|
||||
}
|
||||
} else {
|
||||
if (mDoubleThumb.mMinLeft) {
|
||||
realProcess = (1 - progress);
|
||||
} else {
|
||||
realProcess = progress;
|
||||
}
|
||||
}
|
||||
|
||||
double result = mDoubleThumb.mMin + len * realProcess;
|
||||
String format = "%." + mDoubleThumb.mDigit + "f";
|
||||
String showTipStr = String.format(format, result);
|
||||
float txtWidth = mTipPaintB.measureText(showTipStr);
|
||||
float x = (tumbPosArr[0] + tumbPosArr[2]) / 2.0f - txtWidth / 2.0f;
|
||||
float y = tumbPosArr[1] - getTipsTopOffset();
|
||||
|
||||
//边界处理
|
||||
if (x < ints[0]) {
|
||||
x = ints[0];
|
||||
}
|
||||
if (x > ints[2] - txtWidth) {
|
||||
x = ints[2] - txtWidth;
|
||||
}
|
||||
|
||||
canvas.drawText(showTipStr, x, y, mTipPaintB);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tips向上的偏移量
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private int getTipsTopOffset() {
|
||||
return dp2px(6);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制thumbA前景图,左滑动和右滑动放在了一起,注意三元运算符,
|
||||
*
|
||||
* @param canvas
|
||||
*/
|
||||
private void drawForegroundThumbA(Canvas canvas) {
|
||||
DoubleTumb.Thumb mLeftThumb = mDoubleThumb.mThumbA;
|
||||
boolean mFromLeft = mLeftThumb.mFromLeft;
|
||||
double progress = mLeftThumb.progress;
|
||||
int[] ints = getmPostionArr();
|
||||
float l = mFromLeft ? ints[0] : ints[0] + (float) ((ints[2] - ints[0]) * (1 - progress));
|
||||
float t = ints[1];
|
||||
float r = mFromLeft ? ints[0] + (float) ((ints[2] - ints[0]) * progress) : ints[2];
|
||||
float b = ints[3];
|
||||
|
||||
Log.d(TAG, "drawForegroundThumbA: " + progress);
|
||||
//绘制背景
|
||||
mForegroundRectFA.set(l, t, r, b);
|
||||
canvas.drawRoundRect(mForegroundRectFA, mDoubleThumb.mCorners, mDoubleThumb.mCorners, mForegroundPaintA);
|
||||
}
|
||||
|
||||
private void drawThumbA(Canvas canvas) {
|
||||
DoubleTumb.Thumb mThumbA = mDoubleThumb.mThumbA;
|
||||
boolean mFromLeft = mThumbA.mFromLeft;
|
||||
double progress = mThumbA.progress;
|
||||
//seekbar四个顶点
|
||||
int[] ints = getmPostionArr();
|
||||
float l = mFromLeft ? ints[0] : ints[0] + (float) ((ints[2] - ints[0]) * (1 - progress));
|
||||
float t = ints[1];
|
||||
float r = mFromLeft ? ints[0] + (float) ((ints[2] - ints[0]) * progress) : ints[2];
|
||||
float b = ints[3];
|
||||
|
||||
//开始绘制thumb
|
||||
Bitmap thumbA = getThumbA();
|
||||
float[] tumbPosArr = mThumbA.getmTumbPosArr();
|
||||
tumbPosArr[0] = mFromLeft ? r - mThumbA.getmThumbWidth() / 2.0f : l - mThumbA.getmThumbWidth() / 2.0f;
|
||||
tumbPosArr[1] = t - dp2px(10);
|
||||
tumbPosArr[2] = tumbPosArr[0] + mThumbA.getmThumbWidth() / 2.0f;
|
||||
tumbPosArr[3] = ints[1] + mThumbA.getmThumbHight();
|
||||
canvas.drawBitmap(thumbA, tumbPosArr[0], tumbPosArr[1], mForegroundPaintA);
|
||||
|
||||
//绘制tips
|
||||
if (mThumbA.mShowTips) {
|
||||
double len = mDoubleThumb.mMax - mDoubleThumb.mMin;
|
||||
|
||||
/**
|
||||
* 从左边开始计算最小值还是最右边和前景图无关,由开发者手动设置
|
||||
*/
|
||||
double realProcess = 0;
|
||||
if (mThumbA.mFromLeft) {
|
||||
if (mDoubleThumb.mMinLeft) {
|
||||
realProcess = progress;
|
||||
} else {
|
||||
realProcess = (1 - progress);
|
||||
}
|
||||
} else {
|
||||
if (mDoubleThumb.mMinLeft) {
|
||||
realProcess = (1 - progress);
|
||||
} else {
|
||||
realProcess = progress;
|
||||
}
|
||||
}
|
||||
|
||||
double result = mDoubleThumb.mMin + len * realProcess;
|
||||
String format = "%." + mDoubleThumb.mDigit + "f";
|
||||
String showTipStr = String.format(format, result);
|
||||
float txtWidth = mTipPaintA.measureText(showTipStr);
|
||||
float x = (tumbPosArr[0] + tumbPosArr[2]) / 2.0f - txtWidth / 2.0f;
|
||||
float y = tumbPosArr[1] - getTipsTopOffset();
|
||||
|
||||
//边界处理
|
||||
if (x < ints[0]) {
|
||||
x = ints[0];
|
||||
}
|
||||
if (x > ints[2] - txtWidth) {
|
||||
x = ints[2] - txtWidth;
|
||||
}
|
||||
|
||||
canvas.drawText(showTipStr, x, y, mTipPaintA);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void drawBg(Canvas canvas) {
|
||||
int[] ints = getmPostionArr();
|
||||
mBackgroudRectF.set(ints[0], ints[1], ints[2], ints[3]);
|
||||
canvas.drawRoundRect(mBackgroudRectF, mDoubleThumb.mCorners, mDoubleThumb.mCorners, mBackgroudPaint);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取进度条四个角的位置坐标
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private int[] getmPostionArr() {
|
||||
if (mPostionArr == null) {
|
||||
mPostionArr = new int[4];
|
||||
mPostionArr[0] = 0 + mDoubleThumb.mLineLeft;
|
||||
mPostionArr[1] = 0 + mDoubleThumb.mLineTop;
|
||||
mPostionArr[2] = getWidth() - mDoubleThumb.mLineRight;
|
||||
mPostionArr[3] = getHeight() - mDoubleThumb.mLineBottom;
|
||||
}
|
||||
return mPostionArr;
|
||||
}
|
||||
|
||||
public boolean isDisallowIntercept() {
|
||||
return mDisallowIntercept;
|
||||
}
|
||||
|
||||
public DoubleThumbSeekBar setDisallowIntercept(boolean disallowIntercept) {
|
||||
mDisallowIntercept = disallowIntercept;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取thumbA
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@NonNull
|
||||
private Bitmap getThumbA() {
|
||||
if (mThumbBitmapA == null) {
|
||||
DoubleTumb.Thumb mThumbA = mDoubleThumb.mThumbA;
|
||||
Drawable drawable = mThumbA.getmThumb();
|
||||
if (drawable == null) {
|
||||
throw new IllegalArgumentException("请提供mThumbA的Drawable");
|
||||
}
|
||||
mThumbBitmapA = RenderUtils.drawableToBitmap(drawable, mThumbA.getmThumbWidth(), mThumbA.getmThumbHight());
|
||||
}
|
||||
return mThumbBitmapA;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取thumbB
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@NonNull
|
||||
private Bitmap getThumbB() {
|
||||
if (mDoubleThumb.mThumbB == null) {
|
||||
throw new IllegalArgumentException("请先初始化ThumbB");
|
||||
}
|
||||
|
||||
if (mThumbBitmapB == null) {
|
||||
Drawable drawable = null;
|
||||
DoubleTumb.Thumb mThumbB = mDoubleThumb.mThumbB;
|
||||
drawable = mThumbB.getmThumb();
|
||||
if (drawable == null) {
|
||||
throw new IllegalArgumentException("请提供mThumbB的Drawable");
|
||||
}
|
||||
mThumbBitmapB = RenderUtils.drawableToBitmap(drawable, mThumbB.getmThumbWidth(), mThumbB.getmThumbHight());
|
||||
}
|
||||
return mThumbBitmapB;
|
||||
}
|
||||
|
||||
public interface OnDoubleThumbChangeListener {
|
||||
/**
|
||||
* 监听滑动
|
||||
*
|
||||
* @param seekBar 当前seekbar对象
|
||||
* @param progress 进度百分比,[0,1]
|
||||
* @param thumbA 是否是thumbA,true:thumbA,false:thumbB。同时只允许滑动一个进度thumb.
|
||||
* @param fromUser 是否是用户自己手动触发的
|
||||
*/
|
||||
void onProgressChanged(DoubleThumbSeekBar seekBar, double progress, boolean thumbA, boolean fromUser);
|
||||
}
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
package com.tophold.trade.view.seekbar;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.annotation.ColorInt;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
* 作 者 : wgyscsf@163.com
|
||||
* 更新时间 :2018/11/30 14:13
|
||||
* 描 述 :
|
||||
* ============================================================
|
||||
*/
|
||||
public class DoubleTumb {
|
||||
public double mMin;
|
||||
public double mMax;
|
||||
public float mCorners;
|
||||
@ColorInt
|
||||
public int mBackground;
|
||||
@NonNull
|
||||
public Thumb mThumbA;
|
||||
@Nullable
|
||||
public Thumb mThumbB;//是否启用双thumb以这个参数为空不为空所解决。当这个不为空则显示双thumb.
|
||||
public int mDigit = 4;//精度
|
||||
//最小值从左边开始还是从右边开始,和前景图无关
|
||||
public boolean mMinLeft = true;
|
||||
|
||||
/**
|
||||
* 内部属性
|
||||
*/
|
||||
//背景的四边内间距
|
||||
public int mLineTop, mLineBottom, mLineLeft, mLineRight;
|
||||
|
||||
|
||||
public DoubleTumb(double mMin, double mMax, float mCorners,
|
||||
@ColorInt int mBackground,
|
||||
@NonNull Thumb mThumbA,
|
||||
@Nullable Thumb mThumbB) {
|
||||
this.mMin = mMin;
|
||||
this.mMax = mMax;
|
||||
this.mCorners = mCorners;
|
||||
this.mBackground = mBackground;
|
||||
this.mThumbA = mThumbA;
|
||||
this.mThumbB = mThumbB;
|
||||
}
|
||||
|
||||
|
||||
public static class Thumb {
|
||||
public boolean mFromLeft = true;
|
||||
@ColorInt
|
||||
public int mForeground;
|
||||
public Drawable mForegroundDrawable;
|
||||
|
||||
//进度,确认当前thumb所在的位置,值为百分比
|
||||
public double progress;
|
||||
//thumb
|
||||
private Drawable mThumb;
|
||||
private int mThumbWidth;
|
||||
private int mThumbHight;
|
||||
//是否展示当前htumb的值
|
||||
public boolean mShowTips;
|
||||
|
||||
/**
|
||||
* 内部使用属性
|
||||
*/
|
||||
//thumb的位置坐标,四个点的,用于判断手指是否在按压范围内。左、上、右、下
|
||||
private float[] mTumbPosArr = new float[4];
|
||||
//是否正在按压
|
||||
public boolean mTouch = false;
|
||||
|
||||
public int getmThumbWidth() {
|
||||
if (mThumbWidth <= 0) {
|
||||
if (mThumb != null)
|
||||
return mThumb.getIntrinsicWidth();
|
||||
}
|
||||
return mThumbWidth;
|
||||
}
|
||||
|
||||
public Thumb setmThumbWidth(int mThumbWidth) {
|
||||
this.mThumbWidth = mThumbWidth;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getmThumbHight() {
|
||||
if (mThumbHight <= 0) {
|
||||
if (mThumb != null)
|
||||
return mThumb.getIntrinsicHeight();
|
||||
}
|
||||
return mThumbHight;
|
||||
}
|
||||
|
||||
public Thumb setmThumbHight(int mThumbHight) {
|
||||
this.mThumbHight = mThumbHight;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Thumb(int mForeground) {
|
||||
this.mForeground = mForeground;
|
||||
}
|
||||
|
||||
public Thumb(boolean mFromLeft, Drawable mForegroundDrawable) {
|
||||
this.mFromLeft = mFromLeft;
|
||||
this.mForegroundDrawable = mForegroundDrawable;
|
||||
}
|
||||
|
||||
public Drawable getmThumb() {
|
||||
return mThumb;
|
||||
}
|
||||
|
||||
public Thumb setmThumb(Drawable mThumb) {
|
||||
this.mThumb = mThumb;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float[] getmTumbPosArr() {
|
||||
return mTumbPosArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 传递的x、y是否在按压范围内
|
||||
*
|
||||
* @param context
|
||||
* @param x
|
||||
* @param y
|
||||
* @return
|
||||
*/
|
||||
public boolean isTouchThumb(Context context, float x, float y) {
|
||||
float touchOffset = getTouchOffset(context);
|
||||
float[] ints = getmTumbPosArr();
|
||||
ints[0] -= touchOffset;
|
||||
ints[1] -= touchOffset;
|
||||
ints[2] += touchOffset;
|
||||
ints[3] += touchOffset;
|
||||
return x >= ints[0] && x <= ints[2] && y >= ints[1] && y <= ints[3];
|
||||
}
|
||||
|
||||
/**
|
||||
* 扩大按压区域
|
||||
*
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
public float getTouchOffset(Context context) {
|
||||
final float scale = context.getResources().getDisplayMetrics().density;
|
||||
return (int) (2 * scale + 0.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#3F51B5</color>
|
||||
<color name="colorPrimaryDark">#303F9F</color>
|
||||
<color name="colorAccent">#FF4081</color>
|
||||
|
||||
<!--++++++++++++++++++基金收益自定义View++++++++++++++++++++-->
|
||||
<!--基金自定义view相关属性-->
|
||||
<color name="color_fundView_xyTxtColor">#787274</color>
|
||||
<color name="color_fundView_xLineColor">#c1bdbe</color>
|
||||
<color name="color_fundView_brokenLineColor">#3785d9</color>
|
||||
<color name="color_fundView_longPressLineColor">#ec210f</color>
|
||||
<color name="color_fundView_pressIncomeTxt">#ec210f</color>
|
||||
<color name="color_fundView_pressIncomeTxtBg">#87c9e7</color>
|
||||
<color name="color_fundView_defIncomeTxt">#000000</color>
|
||||
|
||||
|
||||
<!--++++++++++++++++++++KView相关,包括:分时图、副图 开始+++++++++++++++++++++++++++++++++++++++++++++++-->
|
||||
|
||||
<!--公共属性-->
|
||||
<!--外边框颜色-->
|
||||
<color name="color_kview_outerStrokeColor">#787274</color>
|
||||
<color name="color_kview_loadingTxtColor">#787274</color>
|
||||
<!--内部xy虚线-->
|
||||
<color name="color_kview_innerXyDashColor">#787274</color>
|
||||
|
||||
|
||||
<!--++++++++++++++++++分时图自定义View++++++++++++++++++++-->
|
||||
<!--分时图相关属性-->
|
||||
<!--折线图-->
|
||||
<color name="color_timeSharing_brokenLineColor">#3785d9</color>
|
||||
<!--折线图最后的小圆点的颜色-->
|
||||
<color name="color_timeSharing_dotColor">#3785d9</color>
|
||||
<!--实时横线颜色-->
|
||||
<color name="color_timeSharing_timingLineColor">#ec210f</color>
|
||||
<!--折线下面的浅蓝色-->
|
||||
<color name="color_timeSharing_blowBlueColor">#74caf9</color>
|
||||
<!--x、y轴文字的颜色-->
|
||||
<color name="color_timeSharing_xYTxtColor">#787274</color>
|
||||
<!--实时横线右侧的红色的框和实时数据颜色-->
|
||||
<color name="color_timeSharing_timingTxtColor">#ffffff</color>
|
||||
<color name="color_timeSharing_timingTxtBgColor">#ec210f</color>
|
||||
<!--长按十字线的颜色-->
|
||||
<color name="color_timeSharing_longPressLineColor">#7b797b</color>
|
||||
<!--长按十字时间、数据的框和文字颜色-->
|
||||
<color name="color_timeSharing_longPressTxtColor">#ffffff</color>
|
||||
<color name="color_timeSharing_longPressTxtBgColor">#7b797b</color>
|
||||
<!--回调的颜色-->
|
||||
<color name="color_timeSharing_callBackRed">#ec210f</color>
|
||||
<color name="color_timeSharing_callBackGreen">#12ec3a</color>
|
||||
|
||||
<!--蜡烛图的颜色-->
|
||||
<color name="color_timeSharing_candleRed">#ec210f</color>
|
||||
<color name="color_timeSharing_candleGreen">#12ec3a</color>
|
||||
<color name="color_timeSharing_candleEqual">#5d5dec</color>
|
||||
|
||||
<!--主图指标-->
|
||||
<color name="color_masterView_legendColor">#8f8a8a</color>
|
||||
|
||||
|
||||
<!--MA-->
|
||||
<color name="color_masterView_ma5Color">#dcb1ad</color>
|
||||
<color name="color_masterView_ma10Color">#72a5e4</color>
|
||||
<color name="color_masterView_ma20Color">#3785d9</color>
|
||||
|
||||
<!--boll-->
|
||||
<color name="color_masterView_bollUpColor">#ed978f</color>
|
||||
<color name="color_masterView_bollMbColor">#136ad6</color>
|
||||
<color name="color_masterView_bollDnColor">#7897b9</color>
|
||||
|
||||
<color name="color_minmax">#f23f09</color>
|
||||
|
||||
<!--++++++++++++++++++副图自定义View++++++++++++++++++++-->
|
||||
|
||||
<!--共有属性-->
|
||||
<!--外边框颜色-->
|
||||
<color name="color_minorView_outerStrokeColor">#787274</color>
|
||||
<!--内部xy虚线-->
|
||||
<color name="color_minorView_innerXyDashColor">#787274</color>
|
||||
<!--x、y轴文字的颜色-->
|
||||
<color name="color_minorView_xYTxtColor">#787274</color>
|
||||
<!--图例文字的颜色-->
|
||||
<color name="color_minorView_legendTxtColor">#787274</color>
|
||||
<!--长按十字时间、数据的框和文字颜色-->
|
||||
<color name="color_minorView_longPressTxtColor">#ffffff</color>
|
||||
|
||||
<!--macd相关-->
|
||||
<color name="color_minorView_macdBuyColor">#ec210f</color>
|
||||
<color name="color_minorView_macdSellColor">#5aab52</color>
|
||||
<color name="color_minorView_macdDifColor">#dcb1ad</color>
|
||||
<color name="color_minorView_macdDeaColor">#9941e1</color>
|
||||
<color name="color_minorView_macdMacdColor">#3785d9</color>
|
||||
|
||||
<!--rsi相关-->
|
||||
<color name="color_minorView_rsi6Color">#dcb1ad</color>
|
||||
<color name="color_minorView_rsi12Color">#9941e1</color>
|
||||
<color name="color_minorView_rsi24Color">#3785d9</color>
|
||||
|
||||
<!--kdj相关-->
|
||||
<color name="color_minorView_kColor">#dcb1ad</color>
|
||||
<color name="color_minorView_dColor">#9941e1</color>
|
||||
<color name="color_minorView_jColor">#3785d9</color>
|
||||
|
||||
<!--++++++++++++++++++++KView相关,包括:分时图、副图 结束+++++++++++++++++++++++++++++++++++++++++++++++-->
|
||||
|
||||
|
||||
<!--饼图-->
|
||||
<color name="color_pie_chart_red">#ef1e13</color>
|
||||
<color name="color_pie_chart_gray">#8a8a8b</color>
|
||||
|
||||
|
||||
<!--具体业务-->
|
||||
<color name="color_tb_indicator">#ef1e13</color>
|
||||
<color name="color_tb_selectedTxt">#ef1e13</color>
|
||||
<color name="color_tb_Txt">#8a8a8b</color>
|
||||
|
||||
</resources>
|
||||
@@ -0,0 +1,9 @@
|
||||
<resources>
|
||||
<string name="app_name">FinancialCustomerView</string>
|
||||
<string name="string_fundView_defHintTxt">累计收益:</string>
|
||||
<string name="string_fundView_pressHintTxt">累计盈亏:</string>
|
||||
<string name="string_placeHold">--</string>
|
||||
|
||||
<!-- TODO: Remove or change this placeholder text -->
|
||||
<string name="hello_blank_fragment">Hello blank fragment</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user