gitweixin
  • 首页
  • 小程序代码
    • 资讯读书
    • 工具类
    • O2O
    • 地图定位
    • 社交
    • 行业软件
    • 电商类
    • 互联网类
    • 企业类
    • UI控件
  • 大数据开发
    • Hadoop
    • Spark
    • Hbase
    • Elasticsearch
    • Kafka
    • Flink
    • 数据仓库
    • 数据挖掘
    • flume
    • Kafka
    • Hive
    • shardingsphere
    • solr
  • 开发博客
    • Android
    • php
    • python
    • 运维
    • 技术架构
    • 数据库
  • 程序员网赚
  • bug清单
  • 量化投资
  • 在线查询工具
    • 去行号
    • 在线时间戳转换工具
    • 免费图片批量修改尺寸在线工具
    • SVG转JPG在线工具

月度归档12月 2020

精品微信小程序开发门户,代码全部亲测可用

  • 首页   /  2020   /  
  • 12月
  • ( 页面2 )
Android 12月 9,2020

Android系统工具类

public class SystemUtils {

public static final Context appContext = BaseApplication.getInstance();

public static final String TAG = SystemUtils.class.getSimpleName();

/**
* FlyMe版本号
*/
public static final String FLY_ME_VERSION = getFlyMeVersion();
/**
* 云OS版本号
*/
public static final String YUN_OS_VERSION = getYunOSVersion();

/**
* 小米特别处理
*/
public static final String MIUI_VERSION = SystemUtils.getMiuiVersion();

public static final long FIRST_INSTALL_TIME = SystemUtils.getFirstInstallTime();

public static final long LAST_UPDATE_TIME = SystemUtils.getLastUpdateTime();


public static boolean isYunOS() {
return StringUtil.isNotEmpty(YUN_OS_VERSION);
}

/**
* @return
*/
public static boolean isMiui() {
return StringUtil.isNotEmpty(SystemUtils.MIUI_VERSION);
}

public static boolean isFlyMe() {
return StringUtil.isNotEmpty(SystemUtils.FLY_ME_VERSION);
}

public static String getYunOSVersion() {
return getSystemProperty("ro.yunos.version");
}

/**
* 判断是否flyme,并返回版本号
*
* @return
*/
public static String getFlyMeVersion() {
String display = getSystemProperty("ro.build.version.incremental");
Pattern p = Pattern.compile("Flyme_OS_([\\d\\.]+)");
Matcher m = p.matcher(display);
String version = "";
if (m.find()) {
version = m.group(1);
}
return version;
}

public static String getApplicationName() {
try {
PackageManager packageManager = null;
ApplicationInfo applicationInfo = null;
try {
packageManager = appContext.getPackageManager();
applicationInfo = packageManager.getApplicationInfo(appContext.getPackageName(), 0);
} catch (PackageManager.NameNotFoundException e) {
applicationInfo = null;
}
String applicationName = (String) packageManager.getApplicationLabel(applicationInfo);
return applicationName;
}catch (Exception ex){
ex.printStackTrace();
return "";
}
}

public static String getMiuiVersion() {
return getSystemProperty("ro.miui.ui.version.name");
}


// 返回状态栏的高度
public static int getStatusBarHeight(Context ctx) {
int result = 0;
int resourceId = ctx.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = ctx.getResources().getDimensionPixelSize(resourceId);
}
return result;
}

public static void setViewTop(Context ctx, View view) {
int stHeight = SystemUtils.getStatusBarHeight(ctx);
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
params.setMargins(0, stHeight, 0, 0);
view.setLayoutParams(params);
}

/**
* 判断是否存在魅族的SmartBar
*
* @return
*/
public static boolean hasMeiZuSmartBar() {
try {
// 可用反射调用Build.hasSmartBar()
Method method = Class.forName("android.os.Build").getMethod("hasSmartBar");
return ((Boolean) method.invoke(null)).booleanValue();
} catch (Exception e) {
e.printStackTrace();
}

if (Build.DEVICE.equals("mx2")) {
return true;
} else if (Build.DEVICE.equals("mx") ||
Build.DEVICE.equals("m9")) {
return false;
}
return false;
}

public static String getDataDir() {
return appContext.getFilesDir().getParent() + "/";
}

public static boolean canReadExternalStorage() {
String state = Environment.getExternalStorageState();
return state.equals(Environment.MEDIA_MOUNTED) || state.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
}

public static boolean canWriteExternalStorage() {
String state = Environment.getExternalStorageState();
return state.equals(Environment.MEDIA_MOUNTED);
}

public static String getDeviceId() {
return BaseApplication.deviceId;
}

public static int getMyVersionCode(Context ctx) {
try {
return ctx.getPackageManager().getPackageInfo(
ctx.getPackageName(), 0).versionCode;
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "error", e);
return 1;
}
}

public static int getMyVersionCode() {
return getMyVersionCode(appContext);
}

/**
* 获取系统记录的首次安装时间
*
* @return
*/
public static long getFirstInstallTime() {
try {
return appContext.getPackageManager().getPackageInfo(appContext.getPackageName(), 0).firstInstallTime;
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "error", e);
return 0;
}
}

/**
* 获取上次更新时间
*
* @return
*/
public static long getLastUpdateTime() {
try {
return appContext.getPackageManager().getPackageInfo(appContext.getPackageName(), 0).lastUpdateTime;
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "error", e);
return 0;
}
}

public static String getMyPackageName() {
return appContext.getPackageName();
}

public static String getMyVersion() {
return getMyVersion(appContext);
}

public static String getMyVersion(Context ctx) {
try {
PackageManager packageManager = ctx.getPackageManager();
// getPackageName()是你当前类的包名,0代表是获取版本信息
PackageInfo packInfo = packageManager.getPackageInfo(ctx.getPackageName(), 0);
return packInfo.versionName;
} catch (Exception e) {
// MobclickAgent.reportError(ctx, e);
}
return "";
}

public static String getMyVersionWithoutBuild() {
String versionName = SystemUtils.getMyVersion();
if (versionName.contains("." + SystemUtils.getMyVersionCode())) {
versionName = versionName.replace("." + SystemUtils.getMyVersionCode(), "");
}
return versionName;
}


private static long serviceBootTime = -1;

/**
* 获取服务运行时间
*
* @return
*/
public static long getServiceBootTime() {
return serviceBootTime;
}

public static void setServiceBootTime(long sbt) {
serviceBootTime = sbt;
}

public static void restartSelf() {
Intent intent = appContext.getPackageManager().getLaunchIntentForPackage(appContext.getPackageName());
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
appContext.startActivity(intent);
ActivitiesManager.getInstance().exit();
}

/**
* 服务运行时间
*
* @return
*/
public static long serviceElapsedTime() {
if (serviceBootTime < 0) {
return -1;
}
return System.currentTimeMillis() - serviceBootTime;
}

public static String getSystemProperty(String propName) {
String line = "";
BufferedReader reader = null;
try {
Process p = Runtime.getRuntime().exec("getprop " + propName);
reader = new BufferedReader(new InputStreamReader(p.getInputStream()), 512);
line = reader.readLine();
} catch (IOException ex) {
Log.e(TAG, "Unable to read system property " + propName, ex);
} finally {
StreamUtils.closeQuiet(reader);
}
return line;
}

public static Map<String, String> getSystemProperties() {
Map<String, String> map = new HashMap<String, String>();
BufferedReader reader = null;
try {
Process p = Runtime.getRuntime().exec("getprop");
reader = new BufferedReader(new InputStreamReader(p.getInputStream()), 5120);
String line = reader.readLine();
while (line != null) {
Pattern pattern = Pattern.compile("\\[(.+)\\]\\s*:\\s*\\[(.+)\\]");
Matcher m = pattern.matcher(line);
if (m.find()) {
String key = m.group(1);
String value = m.group(2);
map.put(key, value);
}
line = reader.readLine();
}
} catch (IOException ex) {
Log.e(TAG, "Unable to read system properties.", ex);
} finally {
StreamUtils.closeQuiet(reader);
}
return map;
}

public static void appendValue(Context context, SpannableStringBuilder builder, int span, String str) {
appendValue(context, builder, span, str, (int) context.getResources().getDimension(R.dimen.gen_title_size));
}

public static void appendValue(Context context, SpannableStringBuilder builder, int span, String str, int fontSize) {
appendValue(context, builder, span, str, fontSize, -1);
}

public static void appendValue(Context context, SpannableStringBuilder builder, int span, String str, int fontSize, int color) {
int size = builder.length();
builder.append(str);
builder.setSpan(new AbsoluteSizeSpan(fontSize), size, size + span, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
if (color != -1) {
builder.setSpan(new ForegroundColorSpan(color), size, size + span, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
}
}

public static String getSystemProperty2(String s) {
String s1;
try {
s1 = Class.forName("android.os.SystemProperties").getMethod("get", new Class[]{String.class}).invoke(null, new Object[]{
s
}).toString();
} catch (Exception exception) {
Log.e("SystemUtils", exception);
return null;
}
return s1;
}

public static Map<String, String> dumpSystemInfo() {
Map<String, String> map = new HashMap<String, String>();
map.put("Version", getMyVersion(appContext));
map.put("VersionCode", String.valueOf(getMyVersionCode(appContext)));
map.put("Auid", BaseApplication.deviceId);
// map.put("DeviceId", XGPushConfig.getToken(appContext));
map.put("MIUI", getMiuiVersion());
map.put("FlyMe", getFlyMeVersion());
map.put("YunOS", getYunOSVersion());
map.put("CPU_ABI", Build.CPU_ABI);
map.put("TAGS", Build.TAGS);
map.put("MODEL", Build.MODEL);
map.put("SDK", String.valueOf(Build.VERSION.SDK_INT));
map.put("DEVICE", Build.DEVICE);
map.put("DISPLAY", Build.DISPLAY);
map.put("BRAND", Build.BRAND);
map.put("BOARD", Build.BOARD);
map.put("ID", Build.ID);
map.put("MANUFACTURER", Build.MANUFACTURER);
map.put("FINGERPRINT", Build.FINGERPRINT);
map.put("TYPE", Build.TYPE);
map.put("HARDWARE", Build.HARDWARE);
map.put("SERIAL", Build.SERIAL);
map.put("TIME", String.valueOf(Build.TIME));
map.put("USER", Build.USER);
map.put("HOST", Build.HOST);
map.put("VERSION.INCREMENTAL", Build.VERSION.INCREMENTAL);
map.put("VERSION.RELEASE", Build.VERSION.RELEASE);
map.put("VERSION.CODENAME", Build.VERSION.CODENAME);
map.put("VERSION_CODES.BASE", String.valueOf(Build.VERSION_CODES.BASE));
map.put("PKG_FIRST_INSTALL_TIME", DateUtil.simpleFormat(SystemUtils.getFirstInstallTime()));
map.put("PKG_LAST_UPDATE_TIME", DateUtil.simpleFormat(SystemUtils.getLastUpdateTime()));
return map;
}

/**
* 设备制造商,例如 XiaoMi
*
* @return
*/
public static String getManufacture() {
return Build.MANUFACTURER;
}

/**
* 手机设备型号,例如 MI2
*
* @return
*/
public static String getDeviceModel() {
return Build.MODEL;
}

public static String getDevice() {
return Build.DEVICE;
}

public static String getDeviceBrand() {
return Build.BRAND;
}

public static String getDisplay() {
return Build.DISPLAY;
}

public static String getUmengChannel() {
return getAppMetaData(appContext, "UMENG_CHANNEL");
}

/**
* 获取application中指定的meta-data
* @return 如果没有获取成功(没有对应值,或者异常),则返回值为空
*/
public static String getAppMetaData(Context ctx, String key) {
if (ctx == null || TextUtils.isEmpty(key)) {
return null;
}
String resultData = null;
try {
PackageManager packageManager = ctx.getPackageManager();
if (packageManager != null) {
ApplicationInfo applicationInfo = packageManager.getApplicationInfo(ctx.getPackageName(), PackageManager.GET_META_DATA);
if (applicationInfo != null) {
if (applicationInfo.metaData != null) {
resultData = applicationInfo.metaData.getString(key);
}
}

}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}

return resultData;
}

/**
* 操作系统版本,例如 4.4.4
*
* @return
*/
public static String getOSVersion() {
return Build.VERSION.RELEASE;
}

public static int getSDKInt() {
return Build.VERSION.SDK_INT;
}

public static String getTimeZone() {
return TimeZone.getDefault().getID();
}

public static String getCountry() {
return Locale.getDefault().getCountry();
}

public static String getLanguage() {
return Locale.getDefault().getLanguage();
}

public static Context getContext() {
Context ac = ActivitiesManager.getInstance().currentActivity();
return ac != null ? ac : appContext;
}


public static boolean isTopRunningApp(Context ctx) {
ActivityManager am = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE);
String pkgName = "";
try {
//处理android 5.0和云os版本不能获取的bug
if (Build.VERSION.SDK_INT >= 21 || isYunOS()) {
pkgName = getTopRunningAppForL(am, ctx);
} else {
ActivityManager.RunningTaskInfo taskInfo = am.getRunningTasks(1).get(0);
pkgName = taskInfo.topActivity.getPackageName();//获取当前在前台的应用;
}
} catch (Exception e) {
Exception ex = new Exception("failed to get top running app", e);
Log.e(TAG, ex.getMessage(), ex);
// MobclickAgent.reportError(ctx, ex);
}
return StringUtil.isEqual(pkgName, getMyPackageName());
}

public static String getTopRunningAppForL(ActivityManager am, Context ctx) {
List<ActivityManager.RunningAppProcessInfo> processInfos = am.getRunningAppProcesses();
List<String> runningApps = new ArrayList<String>(processInfos.size());
for (ActivityManager.RunningAppProcessInfo processInfo : processInfos) {
if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
runningApps.addAll(Arrays.asList(processInfo.pkgList));
}
}
Log.v(TAG, "Current running processes: " + TextUtils.join(", ", runningApps));
if (runningApps.size() > 0)
return runningApps.get(0);
return "";
}


/**
* 判断服务是否正在运行
*
* @param context
* @param className 判断的服务名字:包名+类名
* @return true在运行 false 不在运行
*/
public static boolean isServiceRunning(Context context, String className) {
boolean isRunning = false;
ActivityManager activityManager = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
//获取所有的服务
List<ActivityManager.RunningServiceInfo> services= activityManager.getRunningServices(Integer.MAX_VALUE);
if(services!=null&&services.size()>0){
for(ActivityManager.RunningServiceInfo service : services){
if(className.equals(service.service.getClassName())){
isRunning=true;
break;
}
}
}
return isRunning;
}

public static String getProvidersName(Context context) {
String IMSI;
String ProvidersName = null;
TelephonyManager telephonyManager = (TelephonyManager) context
.getSystemService(Context.TELEPHONY_SERVICE);
// 返回唯一的用户ID;就是这张卡的编号神马的
IMSI = telephonyManager.getSubscriberId();
// IMSI号前面3位460是国家,紧接着后面2位00 02是中国移动,01是中国联通,03是中国电信。
if(StringUtil.isEmpty(IMSI)){
return "";
}
if (IMSI.startsWith("46000") || IMSI.startsWith("46002")) {
ProvidersName = "中国移动";
} else if (IMSI.startsWith("46001")) {
ProvidersName = "中国联通";
} else if (IMSI.startsWith("46003")) {
ProvidersName = "中国电信";
}
return ProvidersName;
}
}
作者 east
Android 12月 9,2020

Android自定义Toast工具类


public class ToastUtil {

private static Handler handler = new Handler(Looper.getMainLooper());

private static Toast toast = null;

private final static Object synObj = new Object();

private static Toast toastCenter = null;

/**
* 弹出自定义toast
* @param msg 消息内容
* @param duration 消息显示时长
*/
public static void showMessageCenter(final String msg, final int duration){
if(null == toastCenter){
LayoutInflater inflater = LayoutInflater.from(BaseApplication.getInstance().getApplicationContext());
View layout = inflater.inflate(R.layout.toast_center_custom, null);
toastCenter = new Toast(BaseApplication.getInstance().getApplicationContext());
toastCenter.setView(layout);
}
toastCenter.setDuration(duration);
toastCenter.setGravity(Gravity.CENTER, 0, 0);
((TextView)toastCenter.getView().findViewById(R.id.toast_message)).setText(msg);
toastCenter.show();
}

public static void showMessage(final CharSequence msg) {
showMessage(msg, Toast.LENGTH_SHORT);
}

/**
* 根据设置的文本显示
* @param msg
*/
public static void showMessage(final int msg) {
showMessage(msg, Toast.LENGTH_SHORT);
}

/**
* 显示一个文本并且设置时长
* @param msg
* @param len
*/
public static void showMessage(final CharSequence msg, final int len) {
if (msg == null || msg.equals("")) {
return;
}
handler.post(new Runnable() {
@Override
public void run() {
synchronized (synObj) { //加上同步是为了每个toast只要有机会显示出来
if (toast != null) {
//toast.cancel();
toast.setText(msg);
toast.setDuration(len);
} else {
toast = Toast.makeText(BaseApplication.getInstance(), msg, len);
}
toast.show();
}
}
});
}

public static void showMessageForGravity(final CharSequence msg) {
if (msg == null || msg.equals("")) {
return;
}
handler.post(new Runnable() {
@Override
public void run() {
synchronized (synObj) { //加上同步是为了每个toast只要有机会显示出来
if (toast != null) {
//toast.cancel();
toast.setText(msg);
toast.setDuration(Toast.LENGTH_SHORT);
} else {
toast = Toast.makeText(BaseApplication.getInstance(), msg, Toast.LENGTH_SHORT);
}
toast.setGravity(Gravity.TOP, 0, 0);
toast.show();
}
}
});
}

/**
* 资源文件方式显示文本
* @param msg
* @param len
*/
public static void showMessage(final int msg, final int len) {
handler.post(new Runnable() {
@Override
public void run() {
synchronized (synObj) {
if (toast != null) {
//toast.cancel();
toast.setText(msg);
toast.setDuration(len);
} else {
toast = Toast.makeText(BaseApplication.getInstance(), msg, len);
}
toast.show();
}
}
});
}

public static void showMessage(final View v, final CharSequence msg) {
((TextView) v).setText(msg);
}
}
作者 east
Android 12月 9,2020

Android上传文件工具类




import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;



public class UploadUtil {
private static UploadUtil uploadUtil;
private static final String BOUNDARY = UUID.randomUUID().toString(); // 边界标识 随机生成
private static final String PREFIX = "--";
private static final String LINE_END = "\r\n";
private static final String CONTENT_TYPE = "multipart/form-data"; // 内容类型
private UploadUtil() {

}

/**
* 单例模式获取上传工具类
*
@return
*/
public static UploadUtil getInstance() {
if (null == uploadUtil) {
uploadUtil = new UploadUtil();
}
return uploadUtil;
}

private static final String TAG = "UploadUtil";
private int readTimeOut = 10 * 1000; // 读取超时
private int connectTimeout = 10 * 1000; // 超时时间
/***
* 请求使用多长时间
*/
private static int requestTime = 0;

private static final String CHARSET = "utf-8"; // 设置编码

/***
* 上传成功
*/
public static final int UPLOAD_SUCCESS_CODE = 1;
/**
* 文件不存在
*/
public static final int UPLOAD_FILE_NOT_EXISTS_CODE = 2;
/**
* 服务器出错
*/
public static final int UPLOAD_SERVER_ERROR_CODE = 3;
protected static final int WHAT_TO_UPLOAD = 1;
protected static final int WHAT_UPLOAD_DONE = 2;

/**
* android上传文件到服务器
*
*
@param filePath
* 需要上传的文件的路径
*
@param fileKey
* 在网页上<input type=file name=xxx/> xxx就是这里的fileKey
*
@param RequestURL
* 请求的URL
*/
public void uploadFile(String filePath, String fileKey, String RequestURL,
Map<String, String> param) {
if (filePath == null) {
sendMessage(UPLOAD_FILE_NOT_EXISTS_CODE,"文件不存在");
return;
}
try {
File file = new File(filePath);
uploadFile(file, fileKey, RequestURL, param);
} catch (Exception e) {
sendMessage(UPLOAD_FILE_NOT_EXISTS_CODE,"文件不存在");
e.printStackTrace();
return;
}
}

/**
* android上传文件到服务器
*
*
@param file
* 需要上传的文件
*
@param fileKey
* 在网页上<input type=file name=xxx/> xxx就是这里的fileKey
*
@param RequestURL
* 请求的URL
*/
public void uploadFile(final File file, final String fileKey,
final String RequestURL, final Map<String, String> param) {
if (file == null || (!file.exists())) {
sendMessage(UPLOAD_FILE_NOT_EXISTS_CODE,"文件不存在");
return;
}

Log.i(TAG, "请求的URL=" + RequestURL);
Log.i(TAG, "请求的fileName=" + file.getName());
Log.i(TAG, "请求的fileKey=" + fileKey);
new Thread(new Runnable() { //开启线程上传文件
@Override
public void run() {
toUploadFile(file, fileKey, RequestURL, param);
}
}).start();

}

private void toUploadFile(File file, String fileKey, String RequestURL,
Map<String, String> param) {
String result = null;
requestTime= 0;

long requestTime = System.currentTimeMillis();
long responseTime = 0;

try {
URL url = new URL(RequestURL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(readTimeOut);
conn.setConnectTimeout(connectTimeout);
conn.setDoInput(true); // 允许输入流
conn.setDoOutput(true); // 允许输出流
conn.setUseCaches(false); // 不允许使用缓存
conn.setRequestMethod("POST"); // 请求方式
conn.setRequestProperty("Charset", CHARSET); // 设置编码
conn.setRequestProperty("connection", "keep-alive");
conn.setRequestProperty("user-agent", ApiHelper.getUserAgent());
conn.setRequestProperty("Content-Type", CONTENT_TYPE + ";boundary=" + BOUNDARY);
// conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

/**
* 当文件不为空,把文件包装并且上传
*/
DataOutputStream dos = new DataOutputStream(conn.getOutputStream());
StringBuffer sb = null;
String params = "";

/***
* 以下是用于上传参数
*/
if (param != null && param.size() > 0) {
Iterator<String> it = param.keySet().iterator();
while (it.hasNext()) {
sb = null;
sb = new StringBuffer();
String key = it.next();
String value = param.get(key);
sb.append(PREFIX).append(BOUNDARY).append(LINE_END);
sb.append("Content-Disposition: form-data; name=\"").append(key).append("\"").append(LINE_END).append(LINE_END);
sb.append(value).append(LINE_END);
params = sb.toString();
Log.i(TAG, key+"="+params+"##");
dos.write(params.getBytes());
// dos.flush();
}
}

sb = null;
params = null;
sb = new StringBuffer();
/**
* 这里重点注意: name里面的值为服务器端需要key 只有这个key 才可以得到对应的文件
* filename是文件的名字,包含后缀名的 比如:abc.png
*/
sb.append(PREFIX).append(BOUNDARY).append(LINE_END);
sb.append("Content-Disposition:form-data; name=\"" + fileKey
+ "\"; filename=\"" + file.getName() + "\"" + LINE_END);
sb.append("Content-Type:"+ MimeTypesTools.getMimeType(BaseApplication.getInstance(), file.getName()) + LINE_END); // 这里配置的Content-type很重要的 ,用于服务器端辨别文件的类型的
sb.append(LINE_END);
params = sb.toString();
sb = null;

Log.i(TAG, file.getName()+"=" + params+"##");
dos.write(params.getBytes());
/**上传文件*/
InputStream is = new FileInputStream(file);
onUploadProcessListener.initUpload((int)file.length());
byte[] bytes = new byte[1024];
int len = 0;
int curLen = 0;
while ((len = is.read(bytes)) != -1) {
curLen += len;
dos.write(bytes, 0, len);
onUploadProcessListener.onUploadProcess(curLen);
}
is.close();

dos.write(LINE_END.getBytes());

byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINE_END).getBytes();
dos.write(end_data);
dos.flush();
//
// dos.write(tempOutputStream.toByteArray());
/**
* 获取响应码 200=成功 当响应成功,获取响应的流
*/
int res = conn.getResponseCode();
responseTime = System.currentTimeMillis();
UploadUtil.requestTime = (int) ((responseTime-requestTime)/1000);
Log.e(TAG, "response code:" + res);
if (res == 200) {
Log.e(TAG, "request success");
InputStream input = conn.getInputStream();
BufferedReader bufReader = null;
try {
bufReader = new BufferedReader(new InputStreamReader(
input, "utf-8"));
StringBuffer sb1 = new StringBuffer();
String line;
while ((line = bufReader.readLine()) != null) {
sb1.append(line);
}
result = sb1.toString();
Log.e(TAG, "result : " + result);
sendMessage(UPLOAD_SUCCESS_CODE, result);

} catch (IOException e) {

}finally {
try {
if (bufReader != null) {
bufReader.close();
}
} catch (IOException e) {

}
}

return;
} else {
Log.e(TAG, "request error");
sendMessage(UPLOAD_SERVER_ERROR_CODE,"上传失败:code=" + res);
return;
}
} catch (MalformedURLException e) {
sendMessage(UPLOAD_SERVER_ERROR_CODE,"上传失败:error=" + e.getMessage());
e.printStackTrace();
return;
} catch (IOException e) {
sendMessage(UPLOAD_SERVER_ERROR_CODE,"上传失败:error=" + e.getMessage());
e.printStackTrace();
return;
}
}

/**
* 发送上传结果
*
@param responseCode
* @param responseMessage
*/
private void sendMessage(int responseCode,String responseMessage)
{
onUploadProcessListener.onUploadDone(responseCode, responseMessage);
}

/**
* 下面是一个自定义的回调函数,用到回调上传文件是否完成
*
*
@author shimingzheng
*
*/
public interface OnUploadProcessListener {
/**
* 上传响应
*
@param responseCode
* @param message
*/
void onUploadDone(int responseCode, String message);
/**
* 上传中
*
@param uploadSize
*/
void onUploadProcess(int uploadSize);
/**
* 准备上传
*
@param fileSize
*/
void initUpload(int fileSize);
}
private OnUploadProcessListener onUploadProcessListener;



public void setOnUploadProcessListener(
OnUploadProcessListener onUploadProcessListener) {
this.onUploadProcessListener = onUploadProcessListener;
}

public int getReadTimeOut() {
return readTimeOut;
}

public void setReadTimeOut(int readTimeOut) {
this.readTimeOut = readTimeOut;
}

public int getConnectTimeout() {
return connectTimeout;
}

public void setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
}
/**
* 获取上传使用的时间
*
@return
*/
public static int getRequestTime() {
return requestTime;
}

public interface uploadProcessListener{

}




}

作者 east
Android 12月 9,2020

Android View封装工具类

public class ViewUtil {

public static <V extends View> V setGone(final V view, final boolean gone) {
if (view != null)
if (gone) {
if (View.GONE != view.getVisibility())
view.setVisibility(View.GONE);
} else {
if (View.VISIBLE != view.getVisibility())
view.setVisibility(View.VISIBLE);
}
return view;
}

public static <V extends View> V toggle(final V view) {
if (view != null) {
if (View.VISIBLE == view.getVisibility()) {
view.setVisibility(View.GONE);
}else {
view.setVisibility(View.VISIBLE);
}
}
return view;
}

/**
* 设置View是否可见
*
* @param view
* @param invisible
* @return
*/
public static <V extends View> V setInvisible(final V view,
final boolean invisible) {
if (view != null)
if (invisible) {
if (View.INVISIBLE != view.getVisibility())
view.setVisibility(View.INVISIBLE);
} else {
if (View.VISIBLE != view.getVisibility())
view.setVisibility(View.VISIBLE);
}
return view;
}

public static void increaseHitRectBy(final int amount, final View delegate) {
increaseHitRectBy(amount, amount, amount, amount, delegate);
}

public static void increaseHitRectBy(final int top, final int left, final int bottom, final int right, final View delegate) {
final View parent = (View) delegate.getParent();
if (parent != null && delegate.getContext() != null) {
parent.post(new Runnable() {
public void run() {
final float densityDpi = delegate.getContext().getResources().getDisplayMetrics().densityDpi;
final Rect r = new Rect();
delegate.getHitRect(r);
r.top -= transformToDensityPixel(top, densityDpi);
r.left -= transformToDensityPixel(left, densityDpi);
r.bottom += transformToDensityPixel(bottom, densityDpi);
r.right += transformToDensityPixel(right, densityDpi);
parent.setTouchDelegate(new TouchDelegate(r, delegate));
}
});
}
}

public static int transformToDensityPixel(int regularPixel, DisplayMetrics displayMetrics) {
return transformToDensityPixel(regularPixel, displayMetrics.densityDpi);
}

public static int transformToDensityPixel(int regularPixel, float densityDpi) {
return (int) (regularPixel * densityDpi);
}

private ViewUtil() {
}

}
作者 east
Android 12月 9,2020

Android自定义网络访问对话框

public class ECProgressDialog extends Dialog {

private TextView mTextView;
private View mImageView;
AsyncTask mAsyncTask;

private final OnCancelListener mCancelListener = new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
if(mAsyncTask != null) {
mAsyncTask.cancel(true);
}
}
};

/**
* @param context
*/
public ECProgressDialog(Context context) {
super(context , R.style.Theme_Light_CustomDialog_Blue);
mAsyncTask = null;
setCancelable(true);
setContentView(R.layout.common_loading_diloag);
mTextView = (TextView)findViewById(R.id.textview);
mImageView = findViewById(R.id.imageview);
setOnCancelListener(mCancelListener);
}

/**
* @param context
* @param resid
*/
public ECProgressDialog(Context context, int resid) {
this(context);
mTextView.setText(resid);
}

public ECProgressDialog(Context context, CharSequence text) {
this(context);
mTextView.setText(text);
}

public ECProgressDialog(Context context, AsyncTask asyncTask) {
this(context);
mAsyncTask = asyncTask;
}

public ECProgressDialog(Context context, CharSequence text, AsyncTask asyncTask) {
this(context , text);
mAsyncTask = asyncTask;
}

/**
* 设置对话框显示文本
* @param text
*/
public final void setPressText(CharSequence text) {
mTextView.setText(text);
}

public final void dismiss() {
try {
super.dismiss();
mImageView.clearAnimation();
}catch (Exception ex){
ex.printStackTrace();
}
}

public final void show() {
super.show();
Animation loadAnimation = AnimationUtils.loadAnimation(getContext() ,R.anim.loading);
mImageView.startAnimation(loadAnimation);
}

}
作者 east
Android 12月 9,2020

Android自定义对话框



import android.app.Dialog;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;



/**
* 对话框
*
*
@date 2015-1-3
*
@version 4.0
*/
public class ECAlertDialog extends Dialog implements View.OnClickListener {

public static final String TAG = "ECDemo.ECAlertDialog";
/**左边按钮*/
public static final int BUTTON_NEGATIVE = 0;
/**中间按钮*/
public static final int BUTTON_NEUTRAL = 1;
/**右边按钮*/
public static final int BUTTON_POSITIVE = 2;
private boolean mDismiss = true;
private boolean mCancelable = true;
private boolean mCanceledOnTouchOutside = false;
private List<Button> mButtons;
/**对话框标题*/
private View mLayoutTitle;
/**对话框内容*/
private ViewGroup mLayoutContent;
/**对话框按钮*/
private View mLayoutButton;

/**
*
@param context
*/
public ECAlertDialog(Context context) {
super(context, R.style.Theme_Light_FullScreenDialogAct);
super.setContentView(R.layout.common_dialog_generic);
initView();
}

/**
*
*/
private void initView() {
WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
layoutParams.height = WindowManager.LayoutParams.MATCH_PARENT;
getWindow().setAttributes(layoutParams);
mButtons = new ArrayList<Button>();
Button leftBtn = (Button) findViewById(R.id.dilaog_button1);
leftBtn.setOnClickListener(this);
mButtons.add(leftBtn);
Button middleBtn = (Button) findViewById(R.id.dilaog_button2);
middleBtn.setOnClickListener(this);
mButtons.add(middleBtn);
Button rightBtn = (Button) findViewById(R.id.dilaog_button3);
rightBtn.setOnClickListener(this);
mButtons.add(rightBtn);
mLayoutTitle = findViewById(R.id.dialog_layout_title);
mLayoutContent = ((ViewGroup) findViewById(R.id.dialog_layout_content));
mLayoutButton = findViewById(R.id.dialog_layout_button);
setCancelable(true);
setCanceledOnTouchOutside(true);
setTitle(R.string.app_tip);
}

public static ECAlertDialog buildAlert(Context ctx, int message,int button , OnClickListener listener) {
return buildAlert(ctx, ctx.getString(message) , ctx.getString(button), listener);
}

public static ECAlertDialog buildAlert(Context ctx, int resId, OnClickListener listener) {
return buildAlert(ctx, resId, android.R.string.cancel, R.string.string_confirm, null, listener);
}

public static ECAlertDialog buildAlert(Context ctx, int resId,
OnClickListener negativeClickListener,
OnClickListener positive) {
return buildAlert(ctx, ctx.getString(resId), ctx.getString(android.R.string.cancel), ctx.getString(R.string.string_confirm), negativeClickListener, positive);
}

public static ECAlertDialog buildAlert(Context ctx, CharSequence message,
OnClickListener listener) {
return buildAlert(ctx, message, ctx.getString(android.R.string.cancel), ctx.getString(R.string.string_confirm), null, listener);
}

public static ECAlertDialog buildAlert(Context ctx, CharSequence message,
OnClickListener negativeClickListener,
OnClickListener positive) {
return buildAlert(ctx, message, ctx.getString(android.R.string.cancel), ctx.getString(R.string.string_confirm), negativeClickListener, positive);
}

public static ECAlertDialog buildAlert(Context ctx, int message,
int leftBtnText, int rightText,
OnClickListener negativeClickListener,
OnClickListener positive) {
return buildAlert(ctx, ctx.getString(message), ctx.getString(leftBtnText), ctx.getString(rightText), negativeClickListener, positive);
}

/**
* 创建对话框
*
@param ctx 上下文
*
@param message 对话框内容
*
@param leftBtnText 取消按钮文本
*
@param rightText 确定按钮文本
*
@param negativeClickListener
* @param positive
* @return
*/
public static ECAlertDialog buildAlert(Context ctx, CharSequence message,
CharSequence leftBtnText, CharSequence rightText,
OnClickListener negativeClickListener,
OnClickListener positive) {
ECAlertDialog dialog = new ECAlertDialog(ctx);
dialog.setMessage(message);
dialog.setButton(BUTTON_NEGATIVE, leftBtnText, negativeClickListener);
dialog.setButton(BUTTON_POSITIVE, rightText, positive);
return dialog;
}

/**
* 创建只有一个按钮的对话框
*
@param ctx
* @param message
* @param text
* @param positive
* @return
*/
public static ECAlertDialog buildAlert(Context ctx, CharSequence message,CharSequence text,
OnClickListener positive) {
ECAlertDialog dialog = new ECAlertDialog(ctx);
dialog.setMessage(message);
dialog.setButton(BUTTON_NEGATIVE, text, positive);
return dialog;
}

public static ECAlertDialog buildPositiveAlert(Context ctx , int resId , OnClickListener listener) {
return buildPositiveAlert(ctx, ctx.getString(resId), listener);
}

/**
*
*
@param ctx
* @param message
* @param listener
* @return
*/
public static ECAlertDialog buildPositiveAlert(Context ctx , CharSequence message , OnClickListener listener) {
ECAlertDialog dialog = new ECAlertDialog(ctx);
dialog.setMessage(message);
dialog.setButton(BUTTON_POSITIVE, ctx.getString(R.string.string_confirm), listener);
return dialog;
}

/**
* 设置对话框按钮
*
@param id
* @param resId
* @param listener
* @return
*/
public final Button setButton(int id , int resId , OnClickListener listener) {
return setButton(resId, getContext().getString(resId), listener);
}

/**
* 设置按钮
*
@param id 按钮号
*
@param text 按钮显示文本
*
@param listener
* @return
*/
public final Button setButton(int id , CharSequence text , OnClickListener listener) {
Button button = mButtons.get(id);
button.setText(text);
button.setVisibility(View.VISIBLE);
setButtonTag(id, listener);
mLayoutButton.setVisibility(View.VISIBLE);
return button;
}

public final ECAlertDialog setButtonTag(int id , OnClickListener listener) {
Button button = mButtons.get(id);
button.setTag(listener);
return this;
}

public final void setMessage(int resId) {
setMessage(getContext().getString(resId));
}

/**
* 设置对话框显示文本
*
@param text
*/
public final void setMessage(CharSequence text) {
((TextView)findViewById(R.id.dialog_tv_message)).setText(text);
}

public final void setTitleNormalColor() {
((TextView)findViewById(R.id.dialog_tv_title)).setTextColor(getContext().getResources().getColor(R.color.text_content));
}

/**
* 设置标题是否可见
*
@param visibility
*/
public final void setTitleVisibility(int visibility) {
mLayoutTitle.setVisibility(visibility);
}

/**
* 设置内容显示区域
*
@param left
* @param top
* @param right
* @param bottom
*/
public final void setContentPadding(int left, int top, int right, int bottom) {
if (left < 0) {
left = mLayoutContent.getPaddingLeft();
}
if (top < 0) {
top = mLayoutContent.getPaddingRight();
}
if (right < 0) {
right = mLayoutContent.getPaddingTop();
}
if (bottom < 0) {
bottom = mLayoutContent.getPaddingBottom();
}
Drawable localDrawable = mLayoutContent.getBackground();
mLayoutContent.setPadding(left, top, right, bottom);
mLayoutContent.setBackgroundDrawable(localDrawable);
}

public final View getContent() {
return mLayoutContent;
}

/**
* 点击按钮不销毁对话框
*/
public void setDismissFalse() {
mDismiss = false;
}

/**
*
*
@param view
* @return
*/
private int getViewLocation(View view) {
for(int i = 0 ; i < mButtons.size() ; i ++) {
if(mButtons.get(i) == view) {
return i;
}
}
return -1;
}

@Override
public void onClick(View v) {
OnClickListener clickListener = (OnClickListener) v.getTag();
if(clickListener != null) {
clickListener.onClick(this, getViewLocation(v));
}
if(mDismiss) {
dismiss();
return ;
}
// mDismiss = true;
}

public void setCancelable(boolean flag) {
super.setCancelable(flag);
mCancelable = flag;
}

public void setCanceledOnTouchOutside(boolean cancel) {
super.setCanceledOnTouchOutside(cancel);
mCanceledOnTouchOutside = cancel;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
if(mCancelable && mCanceledOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN) {
cancel();
return true;
}
return super.onTouchEvent(event);
}

/**
* 设置自定义View
*/
public final void setContentView(int resource) {
setContentView(getLayoutInflater().inflate(resource, null));
}

public void setContentView(View contentView) {
if (mLayoutContent.getChildCount() > 0) {
mLayoutContent.removeAllViews();
}
mLayoutContent.addView(contentView);
}

public void setContentView(View child, ViewGroup.LayoutParams params) {
if (mLayoutContent.getChildCount() > 0) {
mLayoutContent.removeAllViews();
}
mLayoutContent.addView(child, params);
}

/**
* 设置对话框标题
*/
public void setTitle(int title) {
setTitle(getContext().getString(title));
}

/**
* 设置对话框标题
*/
public void setTitle(CharSequence text) {
if ((text == null) || (TextUtils.isEmpty(text.toString()))) {
mLayoutContent.setVisibility(View.VISIBLE);
setTitleVisibility(View.GONE);
return;
}
((TextView) findViewById(R.id.dialog_tv_title)).setText(text);
mLayoutContent.setVisibility(View.VISIBLE);
setTitleVisibility(View.VISIBLE);
}

public void show() {
super.show();

int i = 0;
Button btn = null;
Iterator<Button> iterator = mButtons.iterator();
while (iterator.hasNext()) {
Button button = iterator.next();
if(button.getVisibility() != View.VISIBLE) {
continue;
}
++i;
btn = button;
}
if (i == 1) {
btn.setBackgroundResource(R.drawable.btn_dialog_single);
}
if (i == 2) {
btn.setSelected(true);
((ViewGroup.MarginLayoutParams)(this.mButtons.get(0)).getLayoutParams()).rightMargin = 1;
}
if (i == 3) {
btn.setSelected(true);
((ViewGroup.MarginLayoutParams)(this.mButtons.get(2)).getLayoutParams()).leftMargin = 1;
((ViewGroup.MarginLayoutParams)(this.mButtons.get(0)).getLayoutParams()).rightMargin = 1;
}

}


}
作者 east
Android 12月 9,2020

Android绘制股票k线图系列4-指标计算


import java.util.List;
import java.util.Vector;

public class IndexCalculation {
    /**
     * 计算成交量包含ma5、ma10、ma20
     *
     * @param data
     * @return
     */
    public static Vector<KLineData> calculationVol(Vector<KLineData> data) {
        data = indexDayMovingAverage(data);
        double ma5s = 0;
        double ma10s = 0;
        double ma20s = 0;
        for (int i = 0; i < data.size(); i++) {
            double num = data.get(i).getCjl();
            double ma5 = 0;
            double ma10 = 0;
            double ma20 = 0;
            ma5s += data.get(i).getCjl();
            ma10s += data.get(i).getCjl();
            ma20s += data.get(i).getCjl();
            if (i >= 4) {
                ma5 = ma5s / 5;
                ma5s -= data.get(i - 4).getCjl();
                if (i >= 9) {
                    ma10 = ma10s / 10;
                    ma10s -= data.get(i - 9).getCjl();
                    if (i >= 19) {
                        ma20 = ma20s / 20;
                        ma20s -= data.get(i - 19).getCjl();
                    }
                }
            }
            Volume vol = new Volume(num, ma5, ma10, ma20);
            data.get(i).setVolume(vol);
        }
        return data;
    }

    /**
     * 计算MACD指标
     *
     * @param data
     * @return
     */
    public static Vector<KLineData> calculationMACD(Vector<KLineData> data) {
        // MACD:参数快线移动平均、慢线移动平均、移动平均,
        // 参数值12、26、9。
        // 公式:⒈首先分别计算出收盘价12日指数平滑移动平均线与26日指数平滑移动平均线,分别记为EMA(12)与EMA(26)。
        // ⒉求这两条指数平滑移动平均线的差,即:DIFF=EMA(SHORT)-EMA(LONG)。
        // ⒊再计算DIFF的M日的平均的指数平滑移动平均线,记为DEA。
        // ⒋最后用DIFF减DEA,得MACD。MACD通常绘制成围绕零轴线波动的柱形图。MACD柱状大于0红色,小于0绿色。
        data = indexDayMovingAverage(data);
        double ema12 = 0;
        double ema26 = 0;
        double oldEma12 = 0;
        double oldEma26 = 0;
        double diff = 0;
        double dea = 0;
        double oldDea = 0;
        double macd = 0;
        double sum = 0;
        double sumDif = 0;
        for (int i = 0; i < data.size(); i++) {
            sum += data.get(i).getClose();
            if (i == 11) {
                ema12 = sum / 12;
                oldEma12 = ema12;
            } else if (i > 11) {
                ema12 = (2 * data.get(i).getClose() + 11 * oldEma12) / 13;
                oldEma12 = ema12;
            }
            if (i == 25) {
                ema26 = sum / 26;
                oldEma26 = ema26;
            } else if (i > 25) {
                ema26 = (2 * data.get(i).getClose() + 25 * oldEma26) / 27;
                oldEma26 = ema26;
            }
            if (i >= 25) {
                diff = ema12 - ema26;
                sumDif += diff;
                if (i == 33) {
                    dea = sumDif / 9;
                    macd = (diff - dea) * 2;
                    oldDea = dea;
                } else if (i > 33) {
                    dea = (2 * diff + 8 * oldDea) / 10;
                    oldDea = dea;
                    macd = (diff - dea) * 2;
                }
            }

            MACD mMacd = new MACD(diff, dea, macd);
            data.get(i).setMacd(mMacd);
        }
        return data;
    }

    /**
     * 计算BOLL指标
     * 日BOLL指标的计算公式
     中轨线=N日的移动平均线
     上轨线=中轨线+两倍的标准差
     下轨线=中轨线-两倍的标准差
     日BOLL指标的计算过程

     1)计算MA,N取为26
     MA=N日内的收盘价之和÷N
     2)计算标准差MD
     两倍的标准差 = 平方根N日的(收盘价- MA)两次方之和除以N
     3)计算MB、上轨线、下轨线
     中轨线=(N-1)日的MA
     上轨线 = 中轨线+2×两倍的标准差
     下轨线 = 中轨线-2×两倍的标准差
     *
     * @param data
     * @return
     */
    public static Vector<KLineData> calculationBOLL(Vector<KLineData> data) {
        double closes26 = 0;// MA
        double MA = 0;// 中轨线
        double MD = 0;// 标准差
        double UP = 0;// 上轨线
        double DN = 0;// 下轨线
        for (int i = 0; i < data.size(); i++) {
            closes26 += data.get(i).getClose();
            if (i >= 25) {
                MA = closes26 / 26;
                List<KLineData> subList =  data.subList(i - 25, i + 1);
                MD = getBollMD(data.subList(i - 25, i + 1), MA);
                UP = MA + 2 * MD;
                DN = MA - 2 * MD;
                closes26 -= data.get(i - 25).getClose();
            }
            BOLL boll = new BOLL(UP, MA, DN);
            data.get(i).setBoll(boll);
        }
        return data;
    }

    /**
     * 计算KDJ
     *
     * @param data
     * @return
     */
    public static Vector<KLineData> calculationKDJ(Vector<KLineData> data) {
        data = indexDayMovingAverage(data);
        double K = 0;
        double D = 0;
        double J = 0;
        for (int i = 0; i < data.size(); i++) {
            if (i >= 8) {
                double Cn = data.get(i).getClose();
                double Ln = getLow(data.subList(i - 8, i + 1));
                double Hn = getHigh(data.subList(i - 8, i + 1));
                double RSV = (Cn - Ln) / (Hn - Ln == 0 ? 1 : Hn - Ln) * 100;
                Log.w("KDJ", "i= "+ i + " Ln=" + Ln + " Hn=" + Hn + " RSV=" + RSV);
                // 当日K值=2/3×前一日K值+1/3×当日RSV
                // 当日D值=2/3×前一日D值+1/3×当日K值
                // 若无前一日K 值与D值,则可分别用50来代替。
                // J值=3*当日K值-2*当日D值
                K = 2.0 / 3.0
                    * (i == 8 ? 50.0 : data.get(i - 1).getKdj().getK())
                    + 1.0 / 3.0 * RSV;
                D = 2.0 / 3.0
                    * (i == 8 ? 50.0 : data.get(i - 1).getKdj().getD())
                    + 1.0 / 3.0 * K;
                J = 3.0 * K - 2.0 * D;

               Log.w("KDJ", "K=" + K + " D=" + D + " J=" + J);

            }
            KDJ kdj = new KDJ(K, D, J);
            data.get(i).setKdj(kdj);

        }

        return data;
    }

    /**
     * 计算RSI
     *
     * @param data
     * @return
     */
    public static Vector<KLineData> calculationRSI(Vector<KLineData> data) {
        // N日RSI =
        // N日内收盘涨幅的平均值/(N日内收盘涨幅均值+N日内收盘跌幅均值) ×100%

        data = indexDayMovingAverage(data);
        double RSI1 = 0;// 参数6
        double RSI2 = 0;// 参数12
        double RSI3 = 0;// 参数24

        double sumCloseA = 0;
        double sumCloseB = 0;

        double a1 = 0;
        double b1 = 0;
        double oldA1 = 0;
        double oldB1 = 0;

        double a2 = 0;
        double b2 = 0;
        double oldA2 = 0;
        double oldB2 = 0;

        double a3 = 0;
        double b3 = 0;
        double oldA3 = 0;
        double oldB3 = 0;
        if(data != null && data.size() > 0){
        data.get(0).setRsi(new RSI(0, 0, 0));
        if (data.size() < 2)
            return data;
        for (int i = 1; i < data.size(); i++) {
            double tmp = data.get(i).getClose() - data.get(i - 1).getClose();
            if (tmp > 0) {
                sumCloseA += tmp;
            } else {
                sumCloseB += tmp;
            }
            double AA = tmp > 0 ? tmp : 0;
            double BB = Math.abs(tmp);

            if (i >= 6) {
                if (i == 6) {
                    a1 = sumCloseA / 6;
                    b1 = (Math.abs(sumCloseB) + sumCloseA) / 6;
                    oldA1 = a1;
                    oldB1 = b1;
                } else {
                    a1 = (AA + 5 * oldA1) / 6;
                    b1 = (BB + 5 * oldB1) / 6;
                    oldA1 = a1;
                    oldB1 = b1;
                }
                RSI1 = a1 / b1 * 100;
                if (i >= 12) {
                    if (i == 12) {
                        a2 = sumCloseA / 12;
                        b2 = (Math.abs(sumCloseB) + sumCloseA) / 12;
                        oldA2 = a2;
                        oldB2 = b2;
                    } else {
                        a2 = (AA + 11 * oldA2) / 12;
                        b2 = (BB + 11 * oldB2) / 12;
                        oldA2 = a2;
                        oldB2 = b2;
                    }
                    RSI2 = a2 / b2 * 100;
                    if (i >= 24) {
                        if (i == 24) {
                            a3 = sumCloseA / 24;
                            b3 = (Math.abs(sumCloseB) + sumCloseA) / 24;
                            oldA3 = a3;
                            oldB3 = b3;
                        } else {
                            a3 = (AA + 23 * oldA3) / 24;
                            b3 = (BB + 23 * oldB3) / 24;
                            oldA3 = a3;
                            oldB3 = b3;
                        }
                        RSI3 = a3 / b3 * 100;
                    }
                }
            }
            RSI rsi = new RSI(RSI1, RSI2, RSI3);
            data.get(i).setRsi(rsi);
        }
        }
        return data;
    }

    /**
     * 计算BIAS指标
     *
     * @param data
     * @return
     */
    public static Vector<KLineData> calculationBIAS(Vector<KLineData> data) {
        // 乖离率=[(当日收盘价-N日平均价)/N日平均价]*100%
        // 参数:6,12、24
        data = indexDayMovingAverage(data);
        double bias1 = 0;
        double bias2 = 0;
        double bias3 = 0;
        double closes1 = 0;
        double closes2 = 0;
        double closes3 = 0;
        for (int i = 0; i < data.size(); i++) {
            closes1 += data.get(i).getClose();
            closes2 += data.get(i).getClose();
            closes3 += data.get(i).getClose();
            if (i >= 5) {
                double mean6 = closes1 / 6;
                closes1 -= data.get(i - 5).getClose();
                bias1 = ((data.get(i).getClose() - mean6) / mean6) * 100;
                if (i >= 11) {
                    double mean12 = closes2 / 12;
                    closes2 -= data.get(i - 11).getClose();
                    bias2 = ((data.get(i).getClose() - mean12) / mean12) * 100;
                    if (i >= 23) {
                        double mean24 = closes3 / 24;
                        closes3 -= data.get(i - 23).getClose();
                        bias3 = ((data.get(i).getClose() - mean24) / mean24) * 100;
                    }
                }
            }
            BIAS bias = new BIAS(bias1, bias2, bias3);
            data.get(i).setBias(bias);
        }
        return data;
    }

    /**
     * 计算BRAR指标
     *
     * @param data
     * @return
     */
    public static Vector<KLineData> calculationBRAR(Vector<KLineData> data) {
        // 参数是26。
        // 公式N日BR=N日内(H-CY)之和除以N日内(CY-L)之和*100,
        // 其中,H为当日最高价,L为当日最低价,CY为前一交易日的收盘价,N为设定的时间参数。
        // N日AR=(N日内(H-O)之和除以N日内(O-L)之和)*100,
        // 其中,H为当日最高价,L为当日最低价,O为当日开盘价,N为设定的时间参数
        data = indexDayMovingAverage(data);
        double BR = 0;
        double AR = 0;
        double HCY = 0;
        double CYL = 0;
        double HO = 0;
        double OL = 0;
        for (int i = 0; i < data.size(); i++) {
            HO += (data.get(i).getHighPrice() - data.get(i).getOpen());
            OL += (data.get(i).getOpen() - data.get(i).getLowPrice());
            if (i > 0) {
                double CY = data.get(i - 1).getClose();
                HCY += (data.get(i).getHighPrice() - CY) > 0 ? (data.get(i)
                                                                        .getHighPrice() - CY) : 0;
                CYL += (CY - data.get(i).getLowPrice()) > 0 ? (CY - data.get(i)
                        .getLowPrice()) : 0;
                if (i >= 25) {
                    AR = HO / OL * 100;
                    HO -= data.get(i - 25).getHighPrice()
                          - data.get(i - 25).getOpen();
                    OL -= data.get(i - 25).getOpen()
                          - data.get(i - 25).getLowPrice();
                    if (i >= 26) {
                        BR = HCY / CYL * 100;
                        double CY1 = data.get(i - 26).getClose();
                        HCY -= (data.get(i - 25).getHighPrice() - CY1) > 0 ? (data
                                                                                      .get(i -
                                                                                           25).getHighPrice() -
                                                                              CY1) : 0;
                        CYL -= (CY1 - data.get(i - 25).getLowPrice()) > 0 ? (CY1 - data
                                .get(i - 25).getLowPrice()) : 0;
                    }
                }
            }
            BRAR brar = new BRAR(BR, AR);
            data.get(i).setBrar(brar);
        }
        return data;
    }

    /**
     * 计算CCI指标
     *
     * @param data
     * @return
     */
    public static Vector<KLineData> calculationCCI(Vector<KLineData> data) {
        // 中价与 中价的N日内移动平均 的差 除以 N日内中价的平均绝对偏差
        // 其中,中价等于最高价、最低价和收盘价之和除以3
        // ={【79-(79+62+45+90+25)/5)】
        // +【62-(79+62+45+90+25)/5)】
        // +【45-(79+62+45+90+25)/5)】
        // +【90-(79+62+45+90+25)/5)】
        // +【25-(79+62+45+90+25)/5)】}/5
        data = indexDayMovingAverage(data);
        double TYPEs = 0;
        double cci = 0;
        for (int i = 0; i < data.size(); i++) {
            double TYP = (data.get(i).getHighPrice()
                          + data.get(i).getLowPrice() + data.get(i).getClose()) / 3;
            TYPEs += TYP;
            if (i >= 13) {
                double TYPEsMean = TYPEs / 14;
                TYPEs -= (data.get(i - 13).getHighPrice()
                          + data.get(i - 13).getLowPrice() + data.get(i - 13)
                                  .getClose()) / 3;

                double types = 0;
                for (int j = i - 13; j < i + 1; j++) {
                    double typ = (data.get(j).getHighPrice()
                                  + data.get(j).getLowPrice() + data.get(j)
                                          .getClose()) / 3;
                    types += Math.abs(typ - TYPEsMean);
                }
                double MD = types / 14;
                if (MD == 0) {
                    cci = 0;
                } else {
                    cci = 200 * (TYP - TYPEsMean) / 3 / MD;
                }
            }
            CCI CCi = new CCI(cci);
            data.get(i).setCci(CCi);
        }
        return data;
    }

    /**
     * 计算DMI
     *
     * @param data
     * @return
     */
    public static Vector<KLineData> calculationDMI(Vector<KLineData> data) {
        // 参数 14,6
        // MTR:=EXPMEMA(MAX(MAX(HIGH-LOW,ABS(HIGH-REF(CLOSE,1))),ABS(REF(CLOSE,1)-LOW)),N)
        // HD :=HIGH-REF(HIGH,1);
        // LD :=REF(LOW,1)-LOW;
        // DMP:=EXPMEMA(IF(HD>0&&HD>LD,HD,0),N);
        // DMM:=EXPMEMA(IF(LD>0&&LD>HD,LD,0),N);
        //
        // PDI: DMP*100/MTR;
        // MDI: DMM*100/MTR;
        // ADX: EXPMEMA(ABS(MDI-PDI)/(MDI+PDI)*100,MM);
        // ADXR:EXPMEMA(ADX,MM);
        // 公式含义:
        // MTR赋值:最高价-最低价和最高价-昨收的绝对值的较大值和昨收-最低价的绝对值的较大值的N日指数平滑移动平均 
        // HD赋值:最高价-昨日最高价 
        // LD赋值:昨日最低价-最低价 
        // DMP赋值:如果HD>0并且HD>LD,返回HD,否则返回0的N日指数平滑移动平均 
        // DMM赋值:如果LD>0并且LD>HD,返回LD,否则返回0的N日指数平滑移动平均 
        // 输出PDI: DMP*100/MTR 
        // 输出MDI: DMM*100/MTR 
        // 输出ADX: MDI-PDI的绝对值/(MDI+PDI)*100的MM日指数平滑移动平均 
        // 输出ADXR:ADX的MM日指数平滑移动平均
        data = indexDayMovingAverage(data);
        double pdi = 0;
        double mdi = 0;
        double adx = 0;
        double adxr = 0;

        double HD = 0;
        double LD = 0;
        double refClose = 0;
        List<Double> sumMax = new Vector<Double>();
        List<Double> sumMaxDmp = new Vector<Double>();
        List<Double> sumMaxDmm = new Vector<Double>();
        List<Double> sumAdx = new Vector<Double>();
        List<Double> sumAdxr = new Vector<Double>();
        for (int i = 0; i < data.size(); i++) {
            if (i > 0) {
                refClose = data.get(i - 1).getClose();
                HD = data.get(i).getHighPrice()
                     - data.get(i - 1).getHighPrice();
                LD = data.get(i - 1).getLowPrice() - data.get(i).getLowPrice();

                double max1 = data.get(i).getHighPrice()
                              - data.get(i).getLowPrice() > Math.abs(data.get(i)
                                                                             .getHighPrice() -
                                                                     refClose) ? data.get(i)
                                                                                         .getHighPrice() -
                                                                                 data.get(i).getLowPrice()
                                                                               : Math
                                      .abs(data.get(i).getHighPrice() - refClose);
                double max2 = max1 > Math.abs(refClose)
                                     - data.get(i).getLowPrice() ? max1 : Math.abs(refClose)
                                                                          -
                                                                          data.get(i).getLowPrice();
                sumMax.add(max2);

                double H;
                if (HD > 0 && HD > LD)
                    H = HD;
                else
                    H = 0;
                sumMaxDmp.add(H);

                double L;
                if (LD > 0 && LD > HD)
                    L = LD;
                else
                    L = 0;
                sumMaxDmm.add(L);

                if (i >= 14) {
                    double sumMax1 = 0;
                    double sumMaxDmp1 = 0;
                    double sumMaxDmm1 = 0;
                    for (int j = 0; j < sumMax.size(); j++) {
                        sumMax1 += sumMax.get(j);
                        sumMaxDmp1 += sumMaxDmp.get(j);
                        sumMaxDmm1 += sumMaxDmm.get(j);
                    }
                    sumMax.remove(0);
                    sumMaxDmp.remove(0);
                    sumMaxDmm.remove(0);
                    double mtr = sumMax1;
                    double dmp = sumMaxDmp1;
                    double dmm = sumMaxDmm1;

                    pdi = dmp * 100 / mtr;
                    mdi = dmm * 100 / mtr;
                    double adxN1 = Math.abs((mdi - pdi)) / (mdi + pdi) * 100;
                    sumAdx.add(adxN1);
                    if (i >= 19) {
                        double sum = 0;
                        for (int j = 0; j < sumAdx.size(); j++) {
                            sum += sumAdx.get(j);
                        }
                        adx = sum / 6;
                        sumAdx.remove(0);
                        sumAdxr.add(adx);
                        if (i >= 25) {
                            double sum1 = 0;
                            sum1 += sumAdxr.get(0);
                            sum1 += sumAdxr.get(sumAdxr.size() - 1);
                            adxr = sum1 / 2;
                            sumAdxr.remove(0);
                        }
                    }
                }
            }
            DMI dmi = new DMI(pdi, mdi, adx, adxr);
            data.get(i).setDmi(dmi);
        }
        return data;
    }

    /**
     * 计算CR
     *
     * @param data
     * @return
     */
    public static Vector<KLineData> calculationCR(Vector<KLineData> data) {
        // 参数26、5、10、20
        // MID:=REF(HIGH+LOW,1)/2;
        // CR:SUM(MAX(0,HIGH-MID),N)/SUM(MAX(0,MID-LOW),N)*100;
        // MA1:REF(MA(CR,M1),M1/2.5+1);
        // MA2:REF(MA(CR,M2),M2/2.5+1);
        // MA3:REF(MA(CR,M3),M3/2.5+1);
        // MID赋值:(昨日最高价+昨日最低价)/2
        // 输出带状能量线:0和最高价-MID的较大值的N日累和/0和MID-最低价的较大值的N日累和*100
        // 输出MA1:M1(5)/2.5+1日前的CR的M1(5)日简单移动平均
        // 输出MA2:M2(10)/2.5+1日前的CR的M2(10)日简单移动平均
        // 输出MA3:M3(20)/2.5+1日前的CR的M3(20)日简单移动平均
        // 输出MA4:M4/2.5+1日前的CR的M4日简单移动平均

        data = indexDayMovingAverage(data);
        double cr = 0;
        double ma1 = 0;
        double ma2 = 0;
        double ma3 = 0;
        double P1 = 0;
        double P2 = 0;
        double ma1Index = 0;
        double ma2Index = 0;
        double ma3Index = 0;
        for (int i = 0; i < data.size(); i++) {
            if (i > 0) {
                double YM = (data.get(i - 1).getHighPrice()
                             + data.get(i - 1).getLowPrice() + data.get(i - 1)
                                     .getClose()) / 3;
                P1 += (0 >= data.get(i).getHighPrice() - YM ? 0 : data.get(i)
                                                                          .getHighPrice() - YM);
                P2 += (0 >= YM - data.get(i).getLowPrice() ? 0 : YM
                                                                 - data.get(i).getLowPrice());
                if (i >= 26) {
                    cr = P1 / P2 * 100;
                    ma1Index += cr;
                    ma2Index += cr;
                    ma3Index += cr;
                    double YM1 = (data.get(i - 26).getHighPrice()
                                  + data.get(i - 26).getLowPrice() + data.get(i - 26)
                                          .getClose()) / 3;
                    P1 -= 0 >= data.get(i - 25).getHighPrice() - YM1 ? 0 : data
                                                                                   .get(i -
                                                                                        25).getHighPrice() -
                                                                           YM1;
                    P2 -= 0 >= YM1 - data.get(i - 25).getLowPrice() ? 0 : YM1
                                                                          - data.get(
                            i - 25).getLowPrice();
                    if (i >= 33) {
                        double ma1Index1 = ma1Index
                                           - (data.get(i - 2).getCr().getCr()
                                              + data.get(i - 1).getCr().getCr() + cr);
                        ma1 = ma1Index1 / 5;
                        ma1Index -= data.get(i - 7).getCr().getCr();
                        if (i >= 40) {
                            double ma2Index2 = ma2Index
                                               - (data.get(i - 4).getCr().getCr()
                                                  + data.get(i - 3).getCr().getCr()
                                                  + data.get(i - 2).getCr().getCr()
                                                  + data.get(i - 1).getCr().getCr() + cr);
                            ma2 = ma2Index2 / 10;
                            ma2Index -= data.get(i - 14).getCr().getCr();
                            if (i >= 54) {
                                double ma3Index3 = ma3Index
                                                   - (data.get(i - 8).getCr().getCr()
                                                      + data.get(i - 7).getCr()
                                                              .getCr()
                                                      + data.get(i - 6).getCr()
                                                              .getCr()
                                                      + data.get(i - 5).getCr()
                                                              .getCr()
                                                      + data.get(i - 4).getCr()
                                                              .getCr()
                                                      + data.get(i - 3).getCr()
                                                              .getCr()
                                                      + data.get(i - 2).getCr()
                                                              .getCr()
                                                      + data.get(i - 1).getCr()
                                                              .getCr() + cr);
                                ma3 = ma3Index3 / 20;
                                ma3Index -= data.get(i - 28).getCr().getCr();
                            }
                        }
                    }
                }
            }
            CR Cr = new CR(cr, ma1, ma2, ma3);
            data.get(i).setCr(Cr);
        }
        return data;
    }

    /**
     * 计算PSY
     *
     * @param data
     * @return
     */
    public static Vector<KLineData> calculationPSY(Vector<KLineData> data) {
        // PSY:参数是12。公式:PSY=N日内的上涨天数/N×100%。
        data = indexDayMovingAverage(data);
        double psy = 0;
        double upDay = 0;
        for (int i = 0; i < data.size(); i++) {
            if (i > 0) {
                upDay += (data.get(i).getClose() - data.get(i - 1).getClose() > 0 ? 1
                                                                                  : 0);
                if (i >= 12) {
                    psy = upDay / 12 * 100;
                    upDay -= (data.get(i - 11).getClose()
                              - data.get(i - 12).getClose() > 0 ? 1 : 0);

                }
            }
            PSY Psy = new PSY(psy);
            data.get(i).setPsy(Psy);
        }
        return data;
    }

    /**
     * 计算DMA
     *
     * @param data
     * @return
     */
    public static Vector<KLineData> calculationDMA(Vector<KLineData> data) {
        // 参数是10、50、10。公式:DIF:MA(CLOSE,N1)-MA(CLOSE,N2);DIFMA:MA(DIF,M)
        data = indexDayMovingAverage(data);
        double Dma = 0;
        double Ama = 0;
        double ma10Index = 0;
        double ma50Index = 0;
        double Dma10Index = 0;
        for (int i = 0; i < data.size(); i++) {
            ma10Index += data.get(i).getClose();
            ma50Index += data.get(i).getClose();

            if (i >= 49) {
                Dma = ma10Index / 10 - ma50Index / 50;
                Dma10Index += Dma;
                if (i >= 58) {
                    Ama = Dma10Index / 10;
                    Dma10Index -= data.get(i - 9).getDma().getDif();
                }
                ma50Index -= data.get(i - 49).getClose();
            }
            if (i >= 9) {
                ma10Index -= data.get(i - 9).getClose();
            }
            DMA DMa = new DMA(Dma, Ama);
            data.get(i).setDma(DMa);
        }
        return data;
    }

    /**
     * 计算TRIX
     *
     * @param data
     * @return
     */
    public static Vector<KLineData> calculationTRIX(Vector<KLineData> data) {
        // TR=收盘价的N日指数移动平均的N日指数移动平均的N日指数移动平均;
        // TRIX=(TR-昨日TR)/昨日TR*100;
        // MATRIX=TRIX的M日简单移动平均;
        // 参数N设为12,参数M设为20;
        // 参数12、20
        // 公式:MTR:=EMA(EMA(EMA(CLOSE,N),N),N)
        // TRIX:(MTR-REF(MTR,1))/REF(MTR,1)*100;
        // TRMA:MA(TRIX,M)
        data = indexDayMovingAverage(data);
        double trix = 0;
        double maTrix = 0;
        double sumTrix = 0;

        double sumClose = 0;
        double emaClose = 0;
        double oldEmaClose = 0;
        double sumEmaClose = 0;
        double ema2 = 0;
        double oldEma2 = 0;
        double sumEma2 = 0;
        double ema3 = 0;
        double oldEma3 = 0;
        for (int i = 0; i < data.size(); i++) {
            sumClose += data.get(i).getClose();
            if (i == 11) {
                emaClose = sumClose / 12;
                oldEmaClose = emaClose;
            } else if (i > 11) {
                emaClose = (2 * data.get(i).getClose() + 11 * oldEmaClose) / 13;
                oldEmaClose = emaClose;
            }
            sumEmaClose += emaClose;
            if (i == 22) {
                ema2 = sumEmaClose / 12;
                oldEma2 = ema2;
            } else if (i > 22) {
                ema2 = (2 * emaClose + 11 * oldEma2) / 13;
                oldEma2 = ema2;
            }
            sumEma2 += ema2;
            if (i == 33) {
                ema3 = sumEma2 / 12;
                oldEma3 = ema3;
            } else if (i > 33) {
                ema3 = (2 * ema2 + 11 * oldEma3) / 13;
                // 公式:MTR:=EMA(EMA(EMA(CLOSE,N),N),N)
                // TRIX:(MTR-REF(MTR,1))/REF(MTR,1)*100;
                // TRMA:MA(TRIX,M)
                trix = (ema3 - oldEma3) / oldEma3 * 100;
                sumTrix += trix;
                if (i >= 52) {
                    maTrix = sumTrix / 20;
                    sumTrix -= data.get(i - 19).getTrix().getTrix();
                }
                oldEma3 = ema3;
            }

            TRIX Trix = new TRIX(trix, maTrix);
            data.get(i).setTrix(Trix);
        }
        return data;
    }

    /**
     * 计算均线数据
     *
     * @param data
     * @return
     */
    private static Vector<KLineData> indexDayMovingAverage(Vector<KLineData> data) {
        double ma5Num = 0.0;
        double ma10Num = 0.0;
        double ma20Num = 0.0;

        double ma5 = 0;
        double ma10 = 0;
        double ma20 = 0;
        for (int i = 0; i < data.size(); i++) {
            double close = data.get(i).getClose();
            ma5Num += close;
            ma10Num += close;
            ma20Num += close;
            if (i >= 4) {
                ma5 = ma5Num / 5;
                ma5Num -= Parse.getInstance().parseDouble(
                        data.get(i - 4).getClose());
                if (i >= 9) {
                    ma10 = ma10Num / 10;
                    ma10Num -= Parse.getInstance().parseDouble(
                            data.get(i - 9).getClose());
                    if (i >= 19) {
                        ma20 = ma20Num / 20;
                        ma20Num -= Parse.getInstance().parseDouble(
                                data.get(i - 19).getClose());
                    }
                }
            }
         //   Log.w("IndexCalculation", "i=" + i + ",dMA1:" + ma5 + ",dMA2:" + ma10 + ",dMA3:" + ma20 );
            KLineData.DayMovingAverage dma = new KLineData.DayMovingAverage(ma5, ma10, ma20);
            data.get(i).setDayMovingAverage(dma);
        }
        return data;
    }

    /**
     * 计算布林指标中的标准差
     *
     * @param list
     * @param MA
     * @return
     */
    private static double getBollMD(List<KLineData> list, double MA) {
        double sum = 0;
        for (int i = 0; i < list.size(); i++) {
            sum += (list.get(i).getClose() - MA)
                   * (list.get(i).getClose() - MA);
        }
        boolean b = sum > 0;
        sum = Math.abs(sum);
        double MD = Math.sqrt(sum / 26);
        return b ? MD : -1 * MD;
    }

    /**
     * 获取list中的最大的最高价
     *
     * @param list
     * @return
     */
    private static double getHigh(List<KLineData> list) {
        double high = 0;
        if (list != null && list.size() > 0) {
            int size = list.size();
            high = list.get(0).getHighPrice();
            for (int i = 1; i < size; i++) {
                high = high < list.get(i).getHighPrice() ? list.get(i)
                        .getHighPrice() : high;
            }
        }
        return high;
    }

    /**
     * 获取list中的最小的最低价
     *
     * @param list
     * @return
     */
    private static double getLow(List<KLineData> list) {
        double low = 0;
        if (list != null && list.size() > 0) {
            int size = list.size();
            low = list.get(0).getLowPrice();
            for (int i = 1; i < size; i++) {
                low = low > list.get(i).getLowPrice() ? list.get(i)
                        .getLowPrice() : low;
            }
        }
        return low;
    }

    /**
     * 获取N日内的涨和
     *
     * @param list
     * @return
     */
    private static double getUpHe(List<KLineData> list) {
        double up = 0;
        if (list != null && list.size() > 0) {
            int size = list.size();
            for (int i = 1; i < size; i++) {
                double z = list.get(i).getClose() - list.get(i - 1).getClose();
                up += z > 0 ? z : 0;
            }
        }
        return up;
    }

    /**
     * 获取N日内跌和
     *
     * @param list
     * @return
     */
    private static double getDownHe(List<KLineData> list) {
        double down = 0;
        if (list != null && list.size() > 0) {
            int size = list.size();
            for (int i = 1; i < size; i++) {
                double d = list.get(i).getClose() - list.get(i - 1).getClose();
                down += d < 0 ? Math.abs(d) : 0;
            }
        }
        return down;
    }

    /**
     * 比较较大值并返回
     *
     * @param d1
     * @param d2
     * @return
     */
    private static double getMax(double d1, double d2) {
        if (d1 > d2)
            return d1;
        else
            return d2;
    }

}
作者 east
Android 12月 8,2020

Android绘制股票k线图系列3-绘制k线


import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.animation.AnimationUtils;

import com.dlj.library.util.Log;
import com.dlj.library.util.Utils;

import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static com.kedll.kedelllibrary.utils.StockDateUtil.judgeTDNextDay;

public class KLineChartView extends GridChartView {
public static final int T_D_TYPE = 18259; //上海T+D等品种

/**
* 接口
*/
private KLineListener l;
private OnKLineChartClickListener clickL;// 单击接口
private OnLoadMoreListener onLoadMoreListener;// 加载更多接口
private boolean isLoadMore;// 是否加载更多

/**
* 数据
*/
private Vector<KLineData> data;
/**
* 左边要显示当前十字光标中心点的数据
*/
private float leftData;
/**
* 对应的时间
*/
private long date;

/**
* 默认上表纬线条数
*/
private final int DEFAULT_LATITUDE_UP_NUM = 4;
/**
* 默认下表纬线条数
*/
private final int DEFAULT_LATITUDE_DOWN_NUM = 2;
/**
* 默认经线条数
*/
private final int DEFAULT_LONGITUDE_NUM = 3;
/**
* 默认上表高度与总高度比例
*/
private final float DEFAULT_UP_RATIO = 0.75f;
/**
* 默认纬线颜色
*/
private final int DEFAULT_LATITUDE_COLOR = 0xffb7b7b7;
/**
* 默认经纬线宽
*/
private final float DEFAULT_LATLONGWIDTH = 0.8f;
/**
* 默认字体大小
*/
private final float DEFAULT_TEXT_SIZE = 9;
/**
* 默认最多显示数
*/
private final int DEFAULT_CANDLE_NUM = 20 * 3;

/**
* 控件高度
*/
private float viewHeight;
/**
* 控件宽度
*/
private float viewWidth;
/**
* 上表纬线条数
*/
protected int latitudesUpNum = DEFAULT_LATITUDE_UP_NUM;
/**
* 下表纬线条数
*/
protected int latitudesDownNum = DEFAULT_LATITUDE_DOWN_NUM;
/**
* 经线条数
*/
protected int longitudesNum = DEFAULT_LONGITUDE_NUM;
/**
* 上表纬线间距
*/
private float upLatitudesSpacing;
/**
* 下表纬线间距
*/
private float downLatitudesSpacing;
/**
* 经线间距
*/
private float longitudesSpacing;
/**
* 上表与下表间距
*/
private float spacing;
/**
* 经、纬线颜色
*/
private int latLngColor = DEFAULT_LATITUDE_COLOR;
/**
* 经纬线宽
*/
private float latLongWidth = DEFAULT_LATLONGWIDTH;
/**
* 字体大小
*/
private float textSize;
/**
* 上表底部
*/
protected float upChartBottom;
/**
* 下表底部
*/
protected float downChartBottom;
/**
* 上表高度
*/
protected float upChartHeight;
/**
* 下表高度
*/
protected float downChartHeight;
/**
* 上表高度与控件高度比
*/
private float upRatio = DEFAULT_UP_RATIO;

/**
* 上表最大数据
*/
private float upMaxData;
/**
* 上表最小数据
*/
private float upMinData;
/**
* 下表最大数据
*/
private float downMaxData;
/**
* 下表最小数据
*/
private float downMinData;
/**
* 上表的尺寸与数据比例
*/
private float upDataRatio;
/**
* 下表的尺寸与数据比例
*/
private float downDataRatio;

/**
* 最多显示数
*/
private int candleNum = DEFAULT_CANDLE_NUM;
/**
* 最多显示数上限
*/
private int maxCandleNum = DEFAULT_CANDLE_NUM * 4;
/**
* 最多显示数下限
*/
private int minCandleNum = DEFAULT_CANDLE_NUM / 4;
/**
* 数据间距
*/
private float dataSpacing;
/**
* 记录当前滚动到的位置
*/
private int position;
/**
* 加载更多的个数
*/
private int oldSize = 0;
/**
* 是否显示十字光标
*/
private boolean isShowCrossLine;
/**
* 主图指标
*/
private MainIndexType mMainIndexType;

/**
* 指标
*/
private IndexType mIndexType;
/**
*
*/

/**
* 手势相关
*/
private float downX;// 按下X坐标
private float downY;// 按下Y坐标
private float moveX;// 触摸中的X坐标
private float moveY;// 触摸中的Y坐标
private int touchMode;// 触摸模式
/**
* 拖拽模式
*/
private final int TOUCH_DRAG = 1;// 左右滑动模式
/**
* 缩放模式
*/
private final int TOUCH_ZOOM = 2;// 捏合模式
/**
* 吸附模式,即长按内容不滚动,仅光标滚动
*/
private final int TOUCH_ADSORPTION = 3;// 十字光标模式
// private float oldDistance;// 旧的移动距离
// private float newDistance;// 新的移动距离
private Runnable mRunnable;// 长按事件的线程
private boolean isMoved;// 是否触摸中
private boolean isReleased;// 手指是否离开屏幕
private boolean isStartMoved;// 是否可以执行OnTouch中的Move事件了
// private boolean isLeftRightMoved;// 是否在左右滑动
// private float distance;// 捏合事件中的捏合距离
private float crossX;// 十字光标X坐标
private float crossY;// 十字光标Y坐标

private ExecutorService executorService = Executors.newCachedThreadPool();
private NotifyRunnable notifyRunnable = new NotifyRunnable();// 更新数据的线程

/**
* 记录多点触摸时中点坐标
*/
private PointF mTouchPointCenter = new PointF();
/**
* 记录触摸时按下的起始坐标
*/
private PointF mTouchStartPoint = new PointF();
/**
* 分别记录触摸时中间坐标索引、触摸时candleNum、触摸时起始索引
*/
private int mTouchPosition = -1, mTouchCandleNum = 15,
mTouchStartPosition = 0;
/**
* 记录触摸起始x方向两点距离、两点原始距离
*/
private float mSavedXDist = 1f, mSavedDist = 1f;

/**
* 记录快速手势处理过程中,上一次手势的位移
*/
private float mLastFlingDist = 0;
/**
* 快速手势事件检测者
*/
private VelocityTracker mVelocityTracker;
/**
* 减速度运行上一次时间戳
*/
private long mDecelerationLastTime = 0;
/**
* 减速度运行过程中的当前坐标
*/
private PointF mDecelerationCurrentPoint = new PointF();
/**
* 记录减速度运行过程中的位移值
*/
private PointF mDecelerationVelocity = new PointF();
/**
* 设置是否将两边坐标值绘制在边框线外(注:如果设置成true,必须设置strokeLeft和strokeRight)
*/
private boolean isDrawOutside;

/**
* 记录上表各种颜色
*/
private int[] upColors;
/**
* 记录下表各种颜色
*/
private int[] downColors;

/**
* 是否显示小时分钟(true:显示yyyy/MM/dd HH:mm;false:显示yyyy/MM/dd)
*/
private boolean isShowTime;

/**
* 小数保留位数
*/
private int num = 2;// 默认保留2位

private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 0x1:
data = (Vector<KLineData>) msg.getData().getSerializable(
"data");
int mainIndex = msg.getData().getInt("mainindex");
if(mainIndex == 0){
mMainIndexType = MainIndexType.MA;
}else if(mainIndex == 1){
mMainIndexType = MainIndexType.BOLL;
}
mIndexType = (IndexType) msg.obj;

if (touchMode == 0) {
Log.i("KLineChartView", "postInvalidate============" + data.size());
postInvalidate();
}
break;

default:
break;
}
}
};

/**
* 主图指标类型
*
*
@author dingrui
*/
public enum MainIndexType {

/**
* 均线
*/
MA("MA"),

/**
* 布林指标
*/
BOLL("BOLL");

private String value;
private boolean isSelected = false;


MainIndexType(String value) {
this.value = value;
}

public String getValue() {
return value;
}

public boolean isSelected() {
return isSelected;
}

public void setSelected(boolean selected) {
isSelected = selected;
}
}

/**
* 指标类型
*
*
@author dingrui
*/
public enum IndexType {

/**
* 成交量
*/
VOL("VOL"),
/**
* 指数平滑异同平均线(MACD指标)
*/
MACD("MACD"),

/**
* 随机指标(KDJ)
*/
KDJ("KDJ"),
/**
* 随机指标(KD),同KDJ,只输出KD
*/
KD("KD"),
/**
* 强弱指标
*/
RSI("RSI"),
/**
* 乖离率(BIAS)是测量股价偏离均线大小程度的指标
*/
BIAS("BIAS"),
/**
* 情绪指标(BRAR)也称为人气意愿指标
*/
BRAR("BRAR"),
/**
* 顺势指标
*/
CCI("CCI"),
/**
* 动向指标
*/
DMI("DMI"),
/**
* 能量指标
*/
CR("CR"),
/**
* 心理线(PSY)指标是研究投资者对股市涨跌产生心理波动的情绪指标
*/
PSY("PSY"),
/**
* 平行线差指标
*/
DMA("DMA"),
/**
* 三重指数平滑平均线(TRIX)属于长线指标
*/
TRIX("TRIX");

private String value;
private boolean isSelected = false;


IndexType(String value) {
this.value = value;
}

public String getValue() {
return value;
}

public boolean isSelected() {
return isSelected;
}

public void setSelected(boolean selected) {
isSelected = selected;
}
}

public KLineChartView(Context context) {
super(context);
// TODO Auto-generated constructor stub
init();
}

public KLineChartView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
init();
}

public KLineChartView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// TODO Auto-generated constructor stub
init();
}

/**
* 初始化
*/
private void init() {
this.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
touchMode = 0;
textSize = MyUtils.getInstance().dp2px(getContext(), DEFAULT_TEXT_SIZE);
spacing = textSize * 2;
position = 0;
isShowCrossLine = false;
mIndexType = IndexType.VOL;
mMainIndexType = MainIndexType.MA;
isDrawOutside = false;
isLoadMore = true;
mRunnable = new Runnable() {

@Override
public void run() {
// TODO Auto-generated method stub
if (isMoved || isReleased)
return;
isStartMoved = true;
touchMode = TOUCH_ADSORPTION;
}
};

}

@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub

super.onDraw(canvas);

try {
viewHeight = getHeight();
viewWidth = getWidth();
Log.w("KLineChartView", "viewHeight===" + viewHeight + " viewWidth=" + viewWidth);
// 计算上表高度
upChartHeight = (viewHeight - getStrokeWidth() * 2 - getStrokeTop()
- getStrokeBottom() - spacing)
* upRatio;
// 计算上表底部位置
upChartBottom = upChartHeight + getStrokeWidth() + getStrokeTop();
// 计算经线间距
longitudesSpacing = (viewWidth - (getStrokeWidth() * 2
+ getStrokeLeft() + getStrokeRight()))
/ (longitudesNum + 1);
// 计算上表纬线间距
upLatitudesSpacing = upChartHeight / latitudesUpNum;
// 计算下表高度
downChartHeight = (viewHeight - getStrokeWidth() * 2 - getStrokeTop()
- getStrokeBottom() - spacing)
* (1 - upRatio);
// 计算下表底部位置
downChartBottom = viewHeight - getStrokeWidth() - getStrokeBottom();
// 计算下表纬线间距
downLatitudesSpacing = downChartHeight / latitudesDownNum;
// 计算数据(蜡烛)间距
dataSpacing = (viewWidth - getStrokeWidth() * 2 - getStrokeLeft() - getStrokeRight())
/ candleNum;

// 计算上表最大、最小数值
upMaxData = 0;
upMinData = 0;
downMaxData = 0;
Log.i("KLineChart", "onDraw....viewHeight=" + viewHeight + " viewWidth=" + viewWidth);
if (data != null && data.size() > 0 && position < data.size() && position >= 0) {
upMaxData = (float) data.get(position).getHighPrice();// 最高价
upMinData = (float) data.get(position).getLowPrice();// 最低价
for (int i = position; i < data.size() && i < candleNum + position; i++) {
upMaxData = upMaxData < (float) data.get(i).getHighPrice() ? (float) data
.get(i).getHighPrice() : upMaxData;

upMinData = (float) (upMinData < data.get(i).getLowPrice() ? upMinData
: data.get(i).getLowPrice());

if (mMainIndexType == MainIndexType.BOLL) {
if (i >= 25) {
upMaxData = upMaxData < (float) data.get(i).getBoll()
.getUpper() ? (float) data.get(i).getBoll()
.getUpper() : upMaxData;

upMinData = (float) (upMinData < data.get(i).getBoll()
.getLower() ? upMinData : data.get(i).getBoll()
.getLower());
}
} else {
if (i >= 4) {
upMaxData = (float) (upMaxData < data.get(i)
.getDayMovingAverage().getMa5() ? data.get(i)
.getDayMovingAverage().getMa5() : upMaxData);
upMinData = (float) (upMinData < data.get(i)
.getDayMovingAverage().getMa5() ? upMinData
: data.get(i).getDayMovingAverage().getMa5());
if (i >= 9) {
upMaxData = (float) (upMaxData < data.get(i)
.getDayMovingAverage().getMa10() ? data
.get(i).getDayMovingAverage().getMa10()
: upMaxData);
upMinData = (float) (upMinData < data.get(i)
.getDayMovingAverage().getMa10() ? upMinData
: data.get(i).getDayMovingAverage()
.getMa10());
if (i >= 19) {
upMaxData = (float) (upMaxData < data.get(i)
.getDayMovingAverage().getMa20() ? data
.get(i).getDayMovingAverage().getMa20()
: upMaxData);
upMinData = (float) (upMinData < data.get(i)
.getDayMovingAverage().getMa20() ? upMinData
: data.get(i).getDayMovingAverage()
.getMa20());
}
}
}
}
}
if (mIndexType == IndexType.VOL ) {
// if (mIndexType == IndexType.VOL || mMainIndexType == MainIndexType.BOLL) {
// 计算成交量最大、最小
downMaxData = (float) data.get(position).getVolume().getNum();
downMinData = 0.0f;
for (int i = position; i < data.size()
&& i < candleNum + position; i++) {
downMaxData = (float) (downMaxData < data.get(i)
.getVolume().getNum() ? data.get(i).getVolume()
.getNum() : downMaxData);
downMaxData = (float) (downMaxData < data.get(i)
.getVolume().getMa5() ? data.get(i).getVolume()
.getMa5() : downMaxData);
downMaxData = (float) (downMaxData < data.get(i)
.getVolume().getMa10() ? data.get(i).getVolume()
.getMa10() : downMaxData);
downMaxData = (float) (downMaxData < data.get(i)
.getVolume().getMa20() ? data.get(i).getVolume()
.getMa20() : downMaxData);
}
} else if (mIndexType == IndexType.MACD) {
// 计算MACD最大、最小
downMaxData = (float) data.get(position).getMacd().getDiff();
downMinData = downMaxData;
for (int i = position; i < data.size()
&& i < candleNum + position; i++) {
downMaxData = (float) (downMaxData < data.get(i).getMacd()
.getDiff() ? data.get(i).getMacd().getDiff()
: downMaxData);
downMaxData = (float) (downMaxData < data.get(i).getMacd()
.getDea() ? data.get(i).getMacd().getDea()
: downMaxData);
downMaxData = (float) (downMaxData < data.get(i).getMacd()
.getMacd() ? data.get(i).getMacd().getMacd()
: downMaxData);

downMinData = (float) (downMinData > data.get(i).getMacd()
.getDiff() ? data.get(i).getMacd().getDiff()
: downMinData);
downMinData = (float) (downMinData > data.get(i).getMacd()
.getDea() ? data.get(i).getMacd().getDea()
: downMinData);
downMinData = (float) (downMinData > data.get(i).getMacd()
.getMacd() ? data.get(i).getMacd().getMacd()
: downMinData);
if (downMaxData >= 0.0 && downMinData <= 0.0) {
if (downMaxData - 0.0f >= 0.0f - downMinData) {
downMinData = 0.0f - (downMaxData - 0.0f);
} else {
downMaxData = 0.0f + (0.0f - downMinData);
}
} else if (downMaxData >= 0.0f && downMinData >= 0.0f) {
downMinData = 0.0f - (downMaxData - 0.0f);
} else {
downMaxData = 0.0f + (0.0f - downMinData);
}
}
float upSpacing = Math.abs(downMaxData - 0.0f);
float downSpacing = Math.abs(downMinData - 0.0f);
if (upSpacing > downSpacing) {
downMinData = 0.0f - upSpacing;
} else {
downMaxData = downSpacing + 0.0f;
}
}
// else if (mIndexType == IndexType.BOLL) {
// // 计算布林最大、最小
// downMaxData = (float) data.get(position).getBoll().getUpper();
// downMinData = (float) data.get(position).getBoll().getLower();
// for (int i = position; i < data.size()
// && i < candleNum + position; i++) {
// downMaxData = (float) (downMaxData < data.get(i).getBoll()
// .getUpper() ? data.get(i).getBoll().getUpper()
// : downMaxData);
//
// downMinData = (float) (downMinData > data.get(i).getBoll()
// .getLower() ? data.get(i).getBoll().getLower()
// : downMinData);
// }
// }
else if (mIndexType == IndexType.KDJ || mIndexType == IndexType.KD) {
// 计算KDJ、KD最大、最小
downMaxData = (float) data.get(position).getKdj().getK();
downMinData = downMaxData;
for (int i = position; i < data.size()
&& i < candleNum + position; i++) {
downMaxData = (float) (downMaxData < data.get(i).getKdj()
.getK() ? data.get(i).getKdj().getK() : downMaxData);
downMaxData = (float) (downMaxData < data.get(i).getKdj()
.getD() ? data.get(i).getKdj().getD() : downMaxData);

downMinData = (float) (downMinData > data.get(i).getKdj()
.getK() ? data.get(i).getKdj().getK() : downMinData);
downMinData = (float) (downMinData > data.get(i).getKdj()
.getD() ? data.get(i).getKdj().getD() : downMinData);

if (mIndexType != IndexType.KD) {
downMaxData = (float) (downMaxData < data.get(i)
.getKdj().getJ() ? data.get(i).getKdj().getJ()
: downMaxData);
downMinData = (float) (downMinData > data.get(i)
.getKdj().getJ() ? data.get(i).getKdj().getJ()
: downMinData);
}
}
} else if (mIndexType == IndexType.RSI) {
// 计算RSI最大、最小
downMaxData = (float) data.get(position).getRsi().getRsi6();
downMinData = downMaxData;
for (int i = position; i < data.size()
&& i < candleNum + position; i++) {
downMaxData = (float) (downMaxData < data.get(i).getRsi()
.getRsi6() ? data.get(i).getRsi().getRsi6()
: downMaxData);
downMaxData = (float) (downMaxData < data.get(i).getRsi()
.getRsi12() ? data.get(i).getRsi().getRsi12()
: downMaxData);
downMaxData = (float) (downMaxData < data.get(i).getRsi()
.getRsi24() ? data.get(i).getRsi().getRsi24()
: downMaxData);

downMinData = (float) (downMinData > data.get(i).getRsi()
.getRsi6() ? data.get(i).getRsi().getRsi6()
: downMinData);
downMinData = (float) (downMinData > data.get(i).getRsi()
.getRsi12() ? data.get(i).getRsi().getRsi12()
: downMinData);
downMinData = (float) (downMinData > data.get(i).getRsi()
.getRsi24() ? data.get(i).getRsi().getRsi24()
: downMinData);
}
} else if (mIndexType == IndexType.BIAS) {
// 计算BIAS最大、最小
downMaxData = (float) data.get(position).getBias().getBias1();
downMinData = downMaxData;
for (int i = position; i < data.size()
&& i < candleNum + position; i++) {
downMaxData = (float) (downMaxData < data.get(i).getBias()
.getBias1() ? data.get(i).getBias().getBias1()
: downMaxData);
downMaxData = (float) (downMaxData < data.get(i).getBias()
.getBias2() ? data.get(i).getBias().getBias2()
: downMaxData);
downMaxData = (float) (downMaxData < data.get(i).getBias()
.getBias3() ? data.get(i).getBias().getBias3()
: downMaxData);

downMinData = (float) (downMinData > data.get(i).getBias()
.getBias1() ? data.get(i).getBias().getBias1()
: downMinData);
downMinData = (float) (downMinData > data.get(i).getBias()
.getBias2() ? data.get(i).getBias().getBias2()
: downMinData);
downMinData = (float) (downMinData > data.get(i).getBias()
.getBias3() ? data.get(i).getBias().getBias3()
: downMinData);
}
} else if (mIndexType == IndexType.BRAR) {
// 计算BRAR最大、最小
downMaxData = (float) data.get(position).getBrar().getBr();
downMinData = downMaxData;
for (int i = position; i < data.size()
&& i < candleNum + position; i++) {
downMaxData = (float) (downMaxData < data.get(i).getBrar()
.getBr() ? data.get(i).getBrar().getBr()
: downMaxData);
downMaxData = (float) (downMaxData < data.get(i).getBrar()
.getAr() ? data.get(i).getBrar().getAr()
: downMaxData);

downMinData = (float) (downMinData > data.get(i).getBrar()
.getBr() ? data.get(i).getBrar().getBr()
: downMinData);
downMinData = (float) (downMinData > data.get(i).getBrar()
.getAr() ? data.get(i).getBrar().getAr()
: downMinData);
}
} else if (mIndexType == IndexType.CCI) {
// 计算CCI最大、最小
downMaxData = (float) data.get(position).getCci().getCci();
downMinData = downMaxData;
for (int i = position; i < data.size()
&& i < candleNum + position; i++) {
downMaxData = (float) (downMaxData < data.get(i).getCci()
.getCci() ? data.get(i).getCci().getCci()
: downMaxData);

downMinData = (float) (downMinData > data.get(i).getCci()
.getCci() ? data.get(i).getCci().getCci()
: downMinData);
}
} else if (mIndexType == IndexType.DMI) {
// 计算DMI最大、最小
downMaxData = (float) data.get(position).getDmi().getPdi();
downMinData = downMaxData;
for (int i = position; i < data.size()
&& i < candleNum + position; i++) {
downMaxData = (float) (downMaxData < data.get(i).getDmi()
.getPdi() ? data.get(i).getDmi().getPdi()
: downMaxData);
downMaxData = (float) (downMaxData < data.get(i).getDmi()
.getMdi() ? data.get(i).getDmi().getMdi()
: downMaxData);
downMaxData = (float) (downMaxData < data.get(i).getDmi()
.getAdx() ? data.get(i).getDmi().getAdx()
: downMaxData);
downMaxData = (float) (downMaxData < data.get(i).getDmi()
.getAdxr() ? data.get(i).getDmi().getAdxr()
: downMaxData);

downMinData = (float) (downMinData > data.get(i).getDmi()
.getPdi() ? data.get(i).getDmi().getPdi()
: downMinData);
downMinData = (float) (downMinData > data.get(i).getDmi()
.getMdi() ? data.get(i).getDmi().getMdi()
: downMinData);
downMinData = (float) (downMinData > data.get(i).getDmi()
.getAdx() ? data.get(i).getDmi().getAdx()
: downMinData);
downMinData = (float) (downMinData > data.get(i).getDmi()
.getAdxr() ? data.get(i).getDmi().getAdxr()
: downMinData);
}
} else if (mIndexType == IndexType.CR) {
// 计算CR最大、最小
downMaxData = (float) data.get(position).getCr().getCr();
downMinData = downMaxData;
for (int i = position; i < data.size()
&& i < candleNum + position; i++) {
downMaxData = (float) (downMaxData < data.get(i).getCr()
.getCr() ? data.get(i).getCr().getCr()
: downMaxData);
downMaxData = (float) (downMaxData < data.get(i).getCr()
.getMa1() ? data.get(i).getCr().getMa1()
: downMaxData);
downMaxData = (float) (downMaxData < data.get(i).getCr()
.getMa2() ? data.get(i).getCr().getMa2()
: downMaxData);
downMaxData = (float) (downMaxData < data.get(i).getCr()
.getMa3() ? data.get(i).getCr().getMa3()
: downMaxData);

downMinData = (float) (downMinData > data.get(i).getCr()
.getCr() ? data.get(i).getCr().getCr()
: downMinData);
downMinData = (float) (downMinData > data.get(i).getCr()
.getMa1() ? data.get(i).getCr().getMa1()
: downMinData);
downMinData = (float) (downMinData > data.get(i).getCr()
.getMa2() ? data.get(i).getCr().getMa2()
: downMinData);
downMinData = (float) (downMinData > data.get(i).getCr()
.getMa2() ? data.get(i).getCr().getMa2()
: downMinData);
}
} else if (mIndexType == IndexType.PSY) {
// 计算PSY最大、最小
downMaxData = (float) data.get(position).getPsy().getPsy();
downMinData = downMaxData;
for (int i = position; i < data.size()
&& i < candleNum + position; i++) {
downMaxData = (float) (downMaxData < data.get(i).getPsy()
.getPsy() ? data.get(i).getPsy().getPsy()
: downMaxData);

downMinData = (float) (downMinData > data.get(i).getPsy()
.getPsy() ? data.get(i).getPsy().getPsy()
: downMinData);
}
} else if (mIndexType == IndexType.DMA) {
// 计算DMA最大、最小
downMaxData = (float) data.get(position).getDma().getDif();
downMinData = downMaxData;
for (int i = position; i < data.size()
&& i < candleNum + position; i++) {
downMaxData = (float) (downMaxData < data.get(i).getDma()
.getDif() ? data.get(i).getDma().getDif()
: downMaxData);
downMaxData = (float) (downMaxData < data.get(i).getDma()
.getAma() ? data.get(i).getDma().getAma()
: downMaxData);

downMinData = (float) (downMinData > data.get(i).getDma()
.getDif() ? data.get(i).getDma().getDif()
: downMinData);
downMinData = (float) (downMinData > data.get(i).getDma()
.getAma() ? data.get(i).getDma().getAma()
: downMinData);
}
} else if (mIndexType == IndexType.TRIX) {
// 计算TRIX最大、最小
downMaxData = (float) data.get(position).getTrix().getTrix();
downMinData = downMaxData;
for (int i = position; i < data.size()
&& i < candleNum + position; i++) {
downMaxData = (float) (downMaxData < data.get(i).getTrix()
.getTrix() ? data.get(i).getTrix().getTrix()
: downMaxData);
downMaxData = (float) (downMaxData < data.get(i).getTrix()
.getMaTrix() ? data.get(i).getTrix().getMaTrix()
: downMaxData);

downMinData = (float) (downMinData > data.get(i).getTrix()
.getTrix() ? data.get(i).getTrix().getTrix()
: downMinData);
downMinData = (float) (downMinData > data.get(i).getTrix()
.getMaTrix() ? data.get(i).getTrix().getMaTrix()
: downMinData);
}
}
}
TextPaint tp = new TextPaint();
tp.setTextSize(textSize);
// tp.setTypeface(Typeface.DEFAULT_BOLD);
tp.setAntiAlias(true);
// float upDataWidth = tp.measureText(Parse.getInstance().parse2String(
// upMaxData));
// float downDataWidth = tp.measureText(Parse.getInstance()
// .parse2CNString(downMaxData));
float dataWidth = tp.measureText("19990909");
// if (spacing == 0.0f)
// setStrokeBottom(textSize);

// 计算上表与数据的比例
upDataRatio = upChartHeight / (upMaxData - upMinData);
// 计算下表与数据比例
downDataRatio = downChartHeight / (downMaxData - downMinData);

Paint paint = new Paint();
paint.setAntiAlias(true);

// 绘制纬线
drawLatitudes(canvas, paint);
// 绘制经线
drawLongitudes(canvas, paint);
// 绘制时间
drawTime(canvas, tp);
// 绘制蜡烛线
drawCandleLine(canvas, paint, tp);
// 绘制是上表折线
drawUpLine(canvas, paint);
// 绘制指标
drawIndex(canvas, paint);
// 绘制上表左边Y轴刻度
drawUpAxisXTitle(canvas, tp, dataWidth);
// 绘制下表左边Y轴刻度
drawDownAxisXTitle(canvas, tp, dataWidth);
// 绘制十字光标
drawCrossLine(canvas, paint, tp);
} catch (NullPointerException ex) {
ex.printStackTrace();
}
}

/**
* 绘制纬线
*
*
@param canvas
* @param paint
*/
private void drawLatitudes(Canvas canvas, Paint paint) {
Log.i("KLineChart", "drawLatitudes.....................");
Paint paint1 = new Paint();
paint1.setColor(latLngColor);
paint1.setStyle(Paint.Style.STROKE);
paint1.setStrokeWidth(latLongWidth);
paint1.setAntiAlias(true);
paint1.setPathEffect(new DashPathEffect(new float[]{4, 4}, 1));
Path path = new Path();
for (int i = 1; i <= latitudesUpNum; i++) {
/* canvas.drawLine(getStrokeLeft(), upLatitudesSpacing * i
+ getStrokeWidth() + getStrokeTop(), getWidth()
-
getStrokeRight(),
upLatitudesSpacing * i
+ getStrokeWidth() + getStrokeTop(), paint); */
path.moveTo(getStrokeLeft(), upLatitudesSpacing * i
+ getStrokeWidth() + getStrokeTop());
path.lineTo(getWidth()
-
getStrokeRight(), upLatitudesSpacing * i
+ getStrokeWidth() + getStrokeTop());

}
canvas.drawPath(path, paint1);
for (int i = 1; i <= latitudesDownNum; i++) {
canvas.drawLine(getStrokeLeft(),
(getHeight() - getStrokeWidth() - getStrokeBottom())
- downLatitudesSpacing * i, getWidth()
- getStrokeRight(),
(getHeight() - getStrokeWidth() - getStrokeBottom())
- downLatitudesSpacing * i, paint1);

}

}

/**
* 绘制经线
*
*
@param canvas
* @param paint
*/
private void drawLongitudes(Canvas canvas, Paint paint) {
Paint paint1 = new Paint();
paint1.setColor(latLngColor);
paint1.setStyle(Paint.Style.STROKE);
paint1.setStrokeWidth(latLongWidth);
paint1.setAntiAlias(true);
paint1.setPathEffect(new DashPathEffect(new float[]{4, 4}, 1));
Path path = new Path();
for (int i = 1; i <= longitudesNum; i++) {
float X = getStrokeLeft() + i * longitudesSpacing;
path.moveTo(X, getStrokeTop() + getStrokeWidth() / 2);
path.lineTo(X, upChartBottom);
path.moveTo(X, upChartBottom + spacing);
path.lineTo(X, downChartBottom);
/*
canvas.drawLine(X, getStrokeTop() + getStrokeWidth() / 2, X,
upChartBottom, paint);
canvas.drawLine(X, upChartBottom + spacing, X, downChartBottom,
paint);
*/
}
canvas.drawPath(path, paint1);
}

/**
* 绘制时间
*
*
@param canvas
* @param paint
*/
private void drawTime(Canvas canvas, TextPaint paint) {
try {
paint.setColor(getResources().getColor(R.color.text_color_5c5f66));
String startTime = "00:00";
String endTime = "00:00";
if (data != null && data.size() > position) {
startTime = MyUtils.getInstance().date2String(
"yyyy/MM/dd HH:mm:ss", data.get(position).getTime() * 1000);
endTime = position + candleNum >= data.size() ? MyUtils
.getInstance().date2String("yyyy/MM/dd HH:mm:ss",
data.get(data.size() - 1).getTime() * 1000)
: MyUtils.getInstance()
.date2String(
"yyyy/MM/dd HH:mm:ss",
data.get(position + candleNum - 1)
.getTime() * 1000);

if (isShowTime) {
startTime = startTime.substring(0, startTime.length() - 3);
endTime = endTime.substring(0, endTime.length() - 3);
} else {
if(data.get(0).getType() == T_D_TYPE){
startTime = judgeTDNextDay(startTime);
endTime = judgeTDNextDay(endTime);
}else {
startTime = startTime.substring(0, startTime.length() - 9);
endTime = endTime.substring(0, endTime.length() - 9);
}
}
}
canvas.drawText(startTime, getStrokeLeft() + getStrokeWidth() + 2,
upChartBottom + textSize, paint);
canvas.drawText(endTime, getWidth() - getStrokeRight()
- getStrokeWidth() - paint.measureText(endTime) - 2,
upChartBottom + textSize, paint);
}catch(ArrayIndexOutOfBoundsException ex){
ex.printStackTrace();
}catch(Exception ex){
ex.printStackTrace();
}
}



/**
* 绘制蜡烛线
*
*
@param canvas
* @param paint
*/
private void drawCandleLine(Canvas canvas, Paint paint, TextPaint tp) {
if (data == null)
return;
paint.setStrokeWidth(2);
float startX = getStrokeWidth() + getStrokeLeft();
int index = 0;
float maxY = Float.MAX_VALUE;
float minY = 0;
float maxX = 0;
float minX = 0;
KLineData maxYEntity = null;
KLineData minYEntity = null;
// float oldDate;
// float oldX = 0.0f;
for (int i = position; i < data.size() && i < candleNum + position; i++) {
KLineData entity = data.get(i);
// String date = Parse.getInstance().isNull(entity.getDate());
// date = date.substring(0, 6);
double close = entity.getClose();
double open = entity.getOpen();

// 柱形图
float openY = upChartBottom
- (float) (open - upMinData) * upDataRatio;
float closeY = upChartBottom
- (float) (close - upMinData) * upDataRatio;
// 中线
float highY = upChartBottom
- (float) (entity.getHighPrice() - upMinData) * upDataRatio;
float lowY = upChartBottom
- (float) (entity.getLowPrice() - upMinData) * upDataRatio;

float endX = getStrokeWidth() + getStrokeLeft()
+ (dataSpacing * (index + 1) - dataSpacing * 0.25f);

if (highY < maxY) {
maxY = highY;
maxYEntity = entity;
maxX = endX;
}
if (lowY > minY) {
minY = lowY;
minYEntity = entity;
minX = endX;
}

// 绘制经线
// if (entity.isDrawLongitude()) {
// paint.setColor(latitudesColor);
//
// tp.setColor(0xff868F9B);
// String date = Parse.getInstance().isNull(entity.getTime());
// date = date.substring(0, 6);
// float textWidth = tp.measureText(date);
// float textY = upChartBottom + textSize * 2 / 2;
// float newX = 0.0f;
// if (((endX - startX) / 2 + startX) - (textWidth / 2) <=
// getStrokeLeft()
// + getStrokeWidth()) {
// newX = getStrokeWidth() + getStrokeLeft() + 2;
// } else if (viewWidth - getStrokeWidth() - getStrokeRight()
// - ((endX - startX) / 2 + startX) <= textWidth / 2) {
// newX = viewWidth - textWidth - 2;
// } else {
// newX = ((endX - startX) / 2 + startX) - (textWidth / 2);
// }
// if (i != position) {
// if (newX - oldX > textWidth + 2) {
// canvas.drawLine((endX - startX) / 2 + startX,
// getStrokeWidth() + getStrokeTop(),
// (endX - startX) / 2 + startX, upChartBottom,
// paint);
// canvas.drawLine((endX - startX) / 2 + startX,
// upChartBottom + spacing, (endX - startX) / 2
// + startX, downChartBottom, paint);
// canvas.drawText(date, newX, textY, tp);
// oldX = newX;
// }
// } else {
// canvas.drawLine((endX - startX) / 2 + startX,
// getStrokeWidth() + getStrokeTop(), (endX - startX)
// / 2 + startX, upChartBottom, paint);
// canvas.drawLine((endX - startX) / 2 + startX, upChartBottom
// + spacing, (endX - startX) / 2 + startX,
// downChartBottom, paint);
//
// canvas.drawText(date, newX, textY, tp);
// oldX = newX;
// }
// }
// oldDate = date;

if (openY < closeY) {
paint.setStyle(Style.FILL);
paint.setColor(getResources().getColor(R.color.green));
canvas.drawRect(startX, openY, endX, closeY, paint);
canvas.drawLine((endX - startX) / 2 + startX, highY,
(endX - startX) / 2 + startX, lowY, paint);
} else if (openY > closeY) {
paint.setStyle(Style.FILL);
paint.setColor(getResources().getColor(R.color.red));
canvas.drawRect(startX, closeY, endX, openY, paint);
paint.setStyle(Style.FILL);
canvas.drawLine((endX - startX) / 2 + startX, highY,
(endX - startX) / 2 + startX, closeY, paint);
canvas.drawLine((endX - startX) / 2 + startX, lowY,
(endX - startX) / 2 + startX, openY, paint);
} else {
if (i != 0) {
KLineData oldItem = data.get(i - 1);
double oldClose = oldItem.getClose();
if (close > oldClose) {
paint.setColor(getResources().getColor(R.color.red));
} else if (close < oldClose) {
paint.setColor(getResources().getColor(R.color.green));
} else {
double oldOpen = oldItem.getOpen();
if (open > oldOpen) {
paint.setColor(getResources().getColor(R.color.red));
} else if (open < oldOpen) {
paint.setColor(getResources().getColor(R.color.green));
} else {
paint.setColor(getResources().getColor(R.color.red));
}
}
} else {
paint.setColor(getResources().getColor(R.color.red));
}
paint.setStyle(Style.FILL);
canvas.drawLine(startX, openY, endX, openY, paint);
canvas.drawLine((endX - startX) / 2 + startX, highY,
(endX - startX) / 2 + startX, lowY, paint);
}

startX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* (index + 1);
index++;
// if (i == position) {
// tp.setColor(0xff868F9B);
// canvas.drawText(String.valueOf(entity.getDate()),
// getStrokeLeft() + getStrokeWidth() + 2, viewHeight, tp);
// }
}
/************在K线图上标记最高、最低价***********/
tp.setColor(getResources().getColor(R.color.color_333333));
if (maxYEntity != null) {
String h = Parse.getInstance().parse2String(maxYEntity.getHighPrice(), 2);
Rect bounds = new Rect();
tp.getTextBounds(h, 0, h.length(), bounds);
maxY += Utils.Dp2Px(getContext(), 10);
if (viewWidth / 2 < maxX) {
maxX = maxX - bounds.width() - Utils.Dp2Px(getContext(), 15);
drawAL(canvas, tp, (int) maxX + bounds.width() + Utils.Dp2Px(getContext(), 1), (int) maxY + bounds.height() / 2, (int) maxX + bounds.width() + Utils.Dp2Px(getContext(), 13), (int) maxY + bounds.height() / 2);
// Log.i(getClass().getSimpleName(), "getWidth()=" + getWidth() + )

} else {
maxX += Utils.Dp2Px(getContext(), 15);
drawAL(canvas, tp, (int) maxX - Utils.Dp2Px(getContext(), 1), (int) maxY + bounds.height() / 2, (int) maxX - Utils.Dp2Px(getContext(), 13), (int) maxY + bounds.height() / 2);
}
canvas.drawText(h, maxX, maxY + bounds.height(), tp);
}
if (minYEntity != null) {
String l = Parse.getInstance().parse2String(minYEntity.getLowPrice(), 2);
Rect bounds = new Rect();
tp.getTextBounds(l, 0, l.length(), bounds);
minY -= Utils.Dp2Px(getContext(), 10);
if (viewWidth / 2 < minX) {
minX = minX - bounds.width() - Utils.Dp2Px(getContext(), 15);
drawAL(canvas, tp, (int) minX + bounds.width() + Utils.Dp2Px(getContext(), 1), (int) minY - bounds.height() / 2, (int) minX + bounds.width() + Utils.Dp2Px(getContext(), 13), (int) minY - bounds.height() / 2);
} else {
minX += Utils.Dp2Px(getContext(), 15);
drawAL(canvas, tp, (int) minX - Utils.Dp2Px(getContext(), 1), (int) minY - bounds.height() / 2, (int) minX - Utils.Dp2Px(getContext(), 13), (int) minY - bounds.height() / 2);
}
canvas.drawText(l, minX, minY, tp);
}
/**************************/
}

/**
* 绘制上表折线
*
*
@param canvas
* @param paint
*/
private void drawUpLine(Canvas canvas, Paint paint) {
if (data == null || data.size() < position)
return;
paint.setStyle(Style.FILL);
paint.setStrokeWidth(1);
int index = 0;
if (mMainIndexType == MainIndexType.BOLL) {
int[] colors = {0xff187cef, 0xfff5a623, 0xfff80101};// 白,黄,红
upColors = colors;
Path path = new Path();
Path path1 = new Path();
Path path2 = new Path();
for (int i = position; i < data.size() && i < candleNum + position; i++) {
KLineData entity = data.get(i);
if (i < data.size() - 1 && i < candleNum + position - 1) {
if (i >= 25) {
float startUPY = upChartBottom
- ((float) entity.getBoll().getUpper() - upMinData)
* upDataRatio;
float endUPY = upChartBottom
- ((float) data.get(i + 1).getBoll().getUpper() - upMinData)
* upDataRatio;
float lineStartX = getStrokeWidth() + getStrokeLeft()
+ dataSpacing * index
+ (dataSpacing * 0.75f / 2);
float lineEndX = getStrokeWidth() + getStrokeLeft()
+ dataSpacing * (index + 1)
+ (dataSpacing * 0.75f / 2);
// paint.setColor(colors[0]);
// canvas.drawLine(lineStartX, startUPY, lineEndX, endUPY,
// paint);

float startMIDY = upChartBottom
- ((float) entity.getBoll().getmID() - upMinData)
* upDataRatio;
float endMIDY = upChartBottom
- ((float) data.get(i + 1).getBoll().getmID() - upMinData)
* upDataRatio;
// paint.setColor(colors[1]);
// canvas.drawLine(lineStartX, startMIDY, lineEndX,
// endMIDY, paint);

float startLOWY = upChartBottom
- ((float) entity.getBoll().getLower() - upMinData)
* upDataRatio;
float endLOWY = upChartBottom
- ((float) data.get(i + 1).getBoll().getLower() - upMinData)
* upDataRatio;
// paint.setColor(colors[2]);
// canvas.drawLine(lineStartX, startLOWY, lineEndX,
// endLOWY, paint);

if (i == position || (position <= 25 && i == 25)) {
path.moveTo(lineStartX, startUPY);
path1.moveTo(lineStartX, startMIDY);
path2.moveTo(lineStartX, startLOWY);
} else {
path.lineTo(lineEndX, endUPY);
path1.lineTo(lineEndX, endMIDY);
path2.lineTo(lineEndX, endLOWY);
}
}
}
index++;
}
paint.setStyle(Style.STROKE);
paint.setColor(colors[0]);
canvas.drawPath(path, paint);
paint.setColor(colors[1]);
canvas.drawPath(path1, paint);
paint.setColor(colors[2]);
canvas.drawPath(path2, paint);
} else {
int[] colors = {0xfff5a623, 0xff187cef, 0xffbd10e0};
upColors = colors;
Path path = new Path();
Path path1 = new Path();
Path path2 = new Path();
for (int i = position; i < data.size() && i < candleNum + position; i++) {
KLineData entity = data.get(i);
if (i < data.size() - 1 && i < candleNum + position - 1) {
if (i >= 4) {
float startMA5Y = upChartBottom
- ((float) entity.getDayMovingAverage()
.getMa5() - upMinData) * upDataRatio;
float endMA5Y = upChartBottom
- ((float) data.get(i + 1)
.getDayMovingAverage().getMa5() - upMinData)
* upDataRatio;
float lineStartX = getStrokeWidth() + getStrokeLeft()
+ dataSpacing * index
+ (dataSpacing * 0.75f / 2);
float lineEndX = getStrokeWidth() + getStrokeLeft()
+ dataSpacing * (index + 1)
+ (dataSpacing * 0.75f / 2);
// paint.setColor(colors[0]);
// canvas.drawLine(lineStartX, startMA5Y, lineEndX,
// endMA5Y, paint);
if (i == position || (position <= 4 && i == 4)) {
path.moveTo(lineStartX, startMA5Y);
} else {
path.lineTo(lineEndX, endMA5Y);
}
if (i >= 9) {
float startMA10Y = upChartBottom
- ((float) entity.getDayMovingAverage()
.getMa10() - upMinData)
* upDataRatio;
float endMA10Y = upChartBottom
- ((float) data.get(i + 1)
.getDayMovingAverage().getMa10() - upMinData)
* upDataRatio;
// paint.setColor(colors[1]);
// canvas.drawLine(lineStartX, startMA10Y, lineEndX,
// endMA10Y, paint);
if (i == position || (position <= 9 && i == 9)) {
path1.moveTo(lineStartX, startMA10Y);
} else {
path1.lineTo(lineEndX, endMA10Y);
}
if (i >= 19) {
float startMA20Y = upChartBottom
- ((float) entity.getDayMovingAverage()
.getMa20() - upMinData)
* upDataRatio;
float endMA20Y = upChartBottom
- ((float) data.get(i + 1)
.getDayMovingAverage()
.getMa20() - upMinData)
* upDataRatio;
// paint.setColor(colors[2]);
// canvas.drawLine(lineStartX, startMA20Y,
// lineEndX, endMA20Y, paint);
if (i == position || (position <= 19 && i == 19)) {
path2.moveTo(lineStartX, startMA20Y);
} else {
path2.lineTo(lineEndX, endMA20Y);
}
}
}
}
}
index++;
}
paint.setStyle(Style.STROKE);
paint.setColor(colors[0]);
canvas.drawPath(path, paint);
paint.setColor(colors[1]);
canvas.drawPath(path1, paint);
paint.setColor(colors[2]);
canvas.drawPath(path2, paint);
}
}

/**
* 绘制指标
*
*
@param canvas
* @param paint
*/
private void drawIndex(Canvas canvas, Paint paint) {
if (data == null) {
return;
}
if (mIndexType == IndexType.VOL)
drawVOL(canvas, paint);
else if (mIndexType == IndexType.MACD)
drawMACD(canvas, paint);
// else if (mIndexType == IndexType.BOLL)
// drawVOL(canvas, paint);
// drawBOLL(canvas, paint);
else if (mIndexType == IndexType.KDJ)
drawKDJ(canvas, paint);
else if (mIndexType == IndexType.KD)
drawKDJ(canvas, paint);
else if (mIndexType == IndexType.RSI)
drawRSI(canvas, paint);
else if (mIndexType == IndexType.BIAS)
drawBIAS(canvas, paint);
else if (mIndexType == IndexType.BRAR)
drawBRAR(canvas, paint);
else if (mIndexType == IndexType.CCI)
drawCCI(canvas, paint);
else if (mIndexType == IndexType.DMI)
drawDMI(canvas, paint);
else if (mIndexType == IndexType.CR)
drawCR(canvas, paint);
else if (mIndexType == IndexType.PSY)
drawPSY(canvas, paint);
else if (mIndexType == IndexType.DMA)
drawDMA(canvas, paint);
else if (mIndexType == IndexType.TRIX)
drawTRIX(canvas, paint);
}

/**
* 绘制成交量指标(vol指标)
*
*
@param canvas
* @param paint
*/
private void drawVOL(Canvas canvas, Paint paint) {
int redColor = getResources().getColor(R.color.red);
int greenColor = getResources().getColor(R.color.green);
int[] colors = {redColor,
greenColor};
if (data == null || data.size() < position)
return;
int index = 0;
for (int i = position; i < data.size() && i < candleNum + position; i++) {
KLineData entity = data.get(i);
if (entity.getClose() >= entity.getOpen()) {
paint.setStyle(Style.FILL);
paint.setColor(colors[0]);
} else {
paint.setStyle(Style.FILL);
paint.setColor(colors[1]);
}
float startX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* index;
float endX = getStrokeWidth() + getStrokeLeft()
+ (dataSpacing * (index + 1) - dataSpacing * 0.25f);
float highY = downChartBottom
- ((float) entity.getVolume().getNum() - downMinData)
* downDataRatio;
float lowY = downChartBottom - (0 - downMinData) * downDataRatio;
canvas.drawRect(startX, highY, endX, lowY, paint);
index++;
}
drawVOLMA(canvas, paint);
}

/**
* 绘制成交量MA折线
*
*
@param canvas
* @param paint
*/
private void drawVOLMA(Canvas canvas, Paint paint) {
int[] colors = {0xff187cef, 0xfff5a623, 0xffbd10e0};
downColors = colors;
paint.setStyle(Style.FILL);
paint.setStrokeWidth(1);
int index = 0;

Path path = new Path();
Path path1 = new Path();
Path path2 = new Path();
for (int i = position; i < data.size() - 1
&& i < candleNum + position - 1; i++) {
KLineData entity = data.get(i);
float lineStartX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* index +
(dataSpacing * 0.75f / 2);
float lineEndX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* (index + 1) +
(dataSpacing * 0.75f / 2);

if (i >= 4) {
float startVMA5Y = (float) (downChartBottom - (entity
.getVolume().getMa5() -
downMinData) * downDataRatio);
float endVMA5Y = (float) (downChartBottom - (data.get(i + 1)
.getVolume().getMa5() -
downMinData)
* downDataRatio);
// paint.setColor(colors[0]);
// canvas.drawLine(lineStartX, startVMA5Y, lineEndX, endVMA5Y,
// paint);
if (i == position || (position <= 4 && i == 4)) {
path.moveTo(lineStartX, startVMA5Y);
} else {
path.lineTo(lineEndX, endVMA5Y);
}
}
if (i >= 9) {
float startVMA10Y = (float) (downChartBottom - (entity
.getVolume().getMa10() -
downMinData) * downDataRatio);
float endVMA10Y = (float) (downChartBottom - (data.get(i + 1)
.getVolume().getMa10() -
downMinData)
* downDataRatio);
// paint.setColor(colors[1]);
// canvas.drawLine(lineStartX, startVMA10Y, lineEndX, endVMA10Y,
// paint);
if (i == position || (position <= 9 && i == 9)) {
path1.moveTo(lineStartX, startVMA10Y);
} else {
path1.lineTo(lineEndX, endVMA10Y);
}
}
if (i >= 19) {
float startVMA20Y = (float) (downChartBottom - (entity
.getVolume().getMa20() -
downMinData) * downDataRatio);
float endVMA20Y = (float) (downChartBottom - (data.get(i + 1)
.getVolume().getMa20() -
downMinData)
* downDataRatio);
// paint.setColor(colors[2]);
// canvas.drawLine(lineStartX, startVMA20Y, lineEndX, endVMA20Y,
// paint);
if (i == position || (position <= 19 && i == 19)) {
path2.moveTo(lineStartX, startVMA20Y);
} else {
path2.lineTo(lineEndX, startVMA20Y);
}
}
index++;
}
paint.setStyle(Style.STROKE);
paint.setColor(colors[0]);
canvas.drawPath(path, paint);
paint.setColor(colors[1]);
canvas.drawPath(path1, paint);
paint.setColor(colors[2]);
canvas.drawPath(path2, paint);
}

/**
* 绘制MACD指标
*
*
@param canvas
* @param paint
*/
private void drawMACD(Canvas canvas, Paint paint) {
int[] colors = {0xff187cef, 0xfff5a623, 0xfff80101};
int redColor = getResources().getColor(R.color.red);
int greenColor = getResources().getColor(R.color.green);
int[] colorss = {redColor, greenColor};
downColors = colors;
paint.setStyle(Style.FILL);
paint.setStrokeWidth(1);
int index = 0;
for (int i = position; i < data.size() && i < candleNum + position; i++) {
KLineData entity = data.get(i);
float lineX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* index + (dataSpacing * 0.75f / 2);
if (i >= 33) {
float MACD = (float) (downChartBottom - (entity.getMacd()
.getMacd() - downMinData) *
downDataRatio);
float zero = (float) (downChartBottom - (0 - downMinData)
* downDataRatio);
if (MACD < zero) {
paint.setColor(colorss[0]);
} else {
paint.setColor(colorss[1]);
}
canvas.drawLine(lineX, zero, lineX, MACD, paint);
}
index++;
}
index = 0;
Path path = new Path();
Path path1 = new Path();
for (int i = position; i < data.size() - 1
&& i < candleNum + position - 1; i++) {
KLineData entity = data.get(i);
float lineStartX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* index +
(dataSpacing * 0.75f / 2);
float lineEndX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* (index + 1) +
(dataSpacing * 0.75f / 2);

if (i >= 25) {
float startEMA26Y = (float) (downChartBottom - (entity
.getMacd().getDiff() -
downMinData) * downDataRatio);
float endEMA26Y = (float) (downChartBottom - (data.get(i + 1)
.getMacd().getDiff() -
downMinData)
* downDataRatio);
// paint.setColor(colors[0]);
// canvas.drawLine(lineStartX, startEMA26Y, lineEndX, endEMA26Y,
// paint);
if (i == position || (position <= 25 && i == 25)) {
path.moveTo(lineStartX, startEMA26Y);
} else {
path.lineTo(lineEndX, endEMA26Y);
}
}
if (i >= 33) {
float startEMA9Y = (float) (downChartBottom - (entity.getMacd()
.getDea() - downMinData) *
downDataRatio);
float endEMA9Y = (float) (downChartBottom - (data.get(i + 1)
.getMacd().getDea() -
downMinData)
* downDataRatio);
// paint.setColor(colors[1]);
// canvas.drawLine(lineStartX, startEMA9Y, lineEndX, endEMA9Y,
// paint);
if (i == position || (position <= 33 && i == 33)) {
path1.moveTo(lineStartX, startEMA9Y);
} else {
path1.lineTo(lineEndX, endEMA9Y);
}
}
index++;
}
paint.setStyle(Style.STROKE);
paint.setColor(colors[0]);
canvas.drawPath(path, paint);
paint.setColor(colors[1]);
canvas.drawPath(path1, paint);
}

/**
* 绘制BOLL指标
*
*
@param canvas
* @param paint
*/
private void drawBOLL(Canvas canvas, Paint paint) {
int[] colors = {0xff187cef, 0xfff5a623, 0xfff80101};// 白,黄,红
downColors = colors;
int index = 0;
paint.setStrokeWidth(1);
for (int i = position; i < data.size() - 1
&& i < candleNum + position - 1; i++) {
KLineData entity = data.get(i);
float lineStartX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* index +
(dataSpacing * 0.75f / 2);
float lineEndX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* (index + 1) +
(dataSpacing * 0.75f / 2);

if (i >= 25) {
// 中轨线
float startMIDY = (float) (downChartBottom - (entity.getBoll()
.getmID() - downMinData) *
downDataRatio);
float endMIDY = (float) (downChartBottom - (data.get(i + 1)
.getBoll().getmID() -
downMinData)
* downDataRatio);
paint.setColor(colors[0]);
canvas.drawLine(lineStartX, startMIDY, lineEndX, endMIDY, paint);
// 上轨线
float startUpY = (float) (downChartBottom - (entity.getBoll()
.getUpper() - downMinData) *
downDataRatio);
float endUpY = (float) (downChartBottom - (data.get(i + 1)
.getBoll().getUpper() -
downMinData)
* downDataRatio);
paint.setColor(colors[1]);
canvas.drawLine(lineStartX, startUpY, lineEndX, endUpY, paint);
// 下轨线
float startLowY = (float) (downChartBottom - (entity.getBoll()
.getLower() - downMinData) *
downDataRatio);
float endLowY = (float) (downChartBottom - (data.get(i + 1)
.getBoll().getLower() -
downMinData)
* downDataRatio);
paint.setColor(colors[2]);
canvas.drawLine(lineStartX, startLowY, lineEndX, endLowY, paint);
}
index++;
}
}

/**
* 绘制KDJ
*
*
@param canvas
* @param paint
*/
private void drawKDJ(Canvas canvas, Paint paint) {
int[] colors = {0xff187cef, 0xfff5a623, 0xfff80101};// 白,黄,红
downColors = colors;
int index = 0;
paint.setStrokeWidth(1);
Path path = new Path();
Path path1 = new Path();
Path path2 = new Path();
for (int i = position; i < data.size() - 1
&& i < candleNum + position - 1; i++) {
KLineData entity = data.get(i);
float lineStartX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* index +
(dataSpacing * 0.75f / 2);
float lineEndX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* (index + 1) +
(dataSpacing * 0.75f / 2);

if (i >= 8) {
float startKY = (float) (downChartBottom - (entity.getKdj()
.getK() - downMinData) *
downDataRatio);
float endKY = (float) (downChartBottom - (data.get(i + 1)
.getKdj().getK() - downMinData)
* downDataRatio);
// paint.setColor(colors[0]);
// canvas.drawLine(lineStartX, startKY, lineEndX, endKY, paint);

float startDY = (float) (downChartBottom - (entity.getKdj()
.getD() - downMinData) *
downDataRatio);
float endDY = (float) (downChartBottom - (data.get(i + 1)
.getKdj().getD() - downMinData)
* downDataRatio);
// paint.setColor(colors[1]);
// canvas.drawLine(lineStartX, startDY, lineEndX, endDY, paint);

if (i == position || (position <= 8 && i == 8)) {
path.moveTo(lineStartX, startKY);
path1.moveTo(lineStartX, startDY);
} else {
path.lineTo(lineEndX, endKY);
path1.lineTo(lineEndX, endDY);
}

if (mIndexType == IndexType.KDJ) {
float startJY = (float) (downChartBottom - (entity.getKdj()
.getJ() - downMinData) *
downDataRatio);
float endJY = (float) (downChartBottom - (data.get(i + 1)
.getKdj().getJ() -
downMinData)
* downDataRatio);
// paint.setColor(colors[2]);
// canvas.drawLine(lineStartX, startJY, lineEndX, endJY, paint);
if (i == position || (position <= 8 && i == 8)) {
path2.moveTo(lineStartX, startJY);
} else {
path2.lineTo(lineEndX, endJY);
}
}
}
index++;
}
paint.setStyle(Style.STROKE);
paint.setColor(colors[0]);
canvas.drawPath(path, paint);
paint.setColor(colors[1]);
canvas.drawPath(path1, paint);
if (mIndexType == IndexType.KDJ) {
paint.setColor(colors[2]);
canvas.drawPath(path2, paint);
}
}

/**
* 绘制RSI
*
*
@param canvas
* @param paint
*/
private void drawRSI(Canvas canvas, Paint paint) {
int[] colors = {0xff187cef, 0xfff5a623, 0xfff80101};// 白,黄,红
downColors = colors;
int index = 0;
paint.setStrokeWidth(1);
Path path = new Path();
Path path1 = new Path();
Path path2 = new Path();
for (int i = position; i < data.size() - 1
&& i < candleNum + position - 1; i++) {
KLineData entity = data.get(i);
float lineStartX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* index +
(dataSpacing * 0.75f / 2);
float lineEndX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* (index + 1) +
(dataSpacing * 0.75f / 2);

if (i >= 6) {
float startRSI1Y = (float) (downChartBottom - (entity.getRsi()
.getRsi6() - downMinData) *
downDataRatio);
float endRSI1Y = (float) (downChartBottom - (data.get(i + 1)
.getRsi().getRsi6() -
downMinData)
* downDataRatio);
// paint.setColor(colors[0]);
// canvas.drawLine(lineStartX, startRSI1Y, lineEndX, endRSI1Y,
// paint);
if (i == position || (position <= 6 && i == 6)) {
path.moveTo(lineStartX, startRSI1Y);
} else {
path.lineTo(lineEndX, endRSI1Y);
}

if (i >= 12) {
float startRSI2Y = (float) (downChartBottom - (entity
.getRsi().getRsi12() -
downMinData) * downDataRatio);
float endRSI2Y = (float) (downChartBottom - (data
.get(i +
1).getRsi().getRsi12() -
downMinData)
* downDataRatio);
// paint.setColor(colors[1]);
// canvas.drawLine(lineStartX, startRSI2Y, lineEndX, endRSI2Y,
// paint);
if (i == position || (position <= 12 && i == 12)) {
path1.moveTo(lineStartX, startRSI2Y);
} else {
path1.lineTo(lineEndX, endRSI2Y);
}
if (i >= 24) {
float startRSI3Y = (float) (downChartBottom - (entity
.getRsi().getRsi24() -
downMinData)
* downDataRatio);
float endRSI3Y = (float) (downChartBottom - (data
.get(i +
1).getRsi().getRsi24() -
downMinData)
* downDataRatio);
// paint.setColor(colors[2]);
// canvas.drawLine(lineStartX, startRSI3Y, lineEndX,
// endRSI3Y, paint);
if (i == position || (position <= 24 && i == 24)) {
path2.moveTo(lineStartX, startRSI3Y);
} else {
path2.lineTo(lineEndX, endRSI3Y);
}
}
}
}
index++;
}
paint.setStyle(Style.STROKE);
paint.setColor(colors[0]);
canvas.drawPath(path, paint);
paint.setColor(colors[1]);
canvas.drawPath(path1, paint);
paint.setColor(colors[2]);
canvas.drawPath(path2, paint);
}

/**
* 绘制BIAS
*
*
@param canvas
* @param paint
*/
private void drawBIAS(Canvas canvas, Paint paint) {
int[] colors = {0xff187cef, 0xfff5a623, 0xfff80101};// 白,黄,红
downColors = colors;
int index = 0;
paint.setStrokeWidth(1);
Path path = new Path();
Path path1 = new Path();
Path path2 = new Path();
for (int i = position; i < data.size() - 1
&& i < candleNum + position - 1; i++) {
KLineData entity = data.get(i);
float lineStartX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* index +
(dataSpacing * 0.75f / 2);
float lineEndX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* (index + 1) +
(dataSpacing * 0.75f / 2);

if (i >= 5) {
float startBIAS1Y = (float) (downChartBottom - (entity
.getBias().getBias1() -
downMinData) * downDataRatio);
float endBIAS1Y = (float) (downChartBottom - (data.get(i + 1)
.getBias().getBias1() -
downMinData)
* downDataRatio);
// paint.setColor(colors[0]);
// canvas.drawLine(lineStartX, startBIAS1Y, lineEndX, endBIAS1Y,
// paint);
if (i == position || (position <= 5 && i == 5)) {
path.moveTo(lineStartX, startBIAS1Y);
} else {
path.lineTo(lineEndX, endBIAS1Y);
}

if (i >= 11) {
float startBIAS2Y = (float) (downChartBottom - (entity
.getBias().getBias2() -
downMinData)
* downDataRatio);
float endBIAS2Y = (float) (downChartBottom - (data
.get(i +
1).getBias().getBias2() -
downMinData)
* downDataRatio);
// paint.setColor(colors[1]);
// canvas.drawLine(lineStartX, startBIAS2Y, lineEndX,
// endBIAS2Y, paint);
if (i == position || (position <= 11 && i == 11)) {
path1.moveTo(lineStartX, startBIAS2Y);
} else {
path1.lineTo(lineEndX, endBIAS2Y);
}
if (i >= 23) {
float startBIAS3Y = (float) (downChartBottom - (entity
.getBias().getBias3() -
downMinData)
* downDataRatio);
float endBIAS3Y = (float) (downChartBottom - (data
.get(i +
1).getBias().getBias3() -
downMinData)
* downDataRatio);
// paint.setColor(colors[2]);
// canvas.drawLine(lineStartX, startBIAS3Y, lineEndX,
// endBIAS3Y, paint);
if (i == position || (position <= 23 && i == 23)) {
path2.moveTo(lineStartX, startBIAS3Y);
} else {
path2.lineTo(lineEndX, endBIAS3Y);
}
}
}
}
index++;
}
paint.setStyle(Style.STROKE);
paint.setColor(colors[0]);
canvas.drawPath(path, paint);
paint.setColor(colors[1]);
canvas.drawPath(path1, paint);
paint.setColor(colors[2]);
canvas.drawPath(path2, paint);
}

/**
* 绘制BRAR
*
*
@param canvas
* @param paint
*/
private void drawBRAR(Canvas canvas, Paint paint) {
int[] colors = {0xff187cef, 0xfff5a623};// 白,黄
downColors = colors;
int index = 0;
paint.setStrokeWidth(1);
Path path = new Path();
Path path1 = new Path();
for (int i = position; i < data.size() - 1
&& i < candleNum + position - 1; i++) {
KLineData entity = data.get(i);
float lineStartX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* index +
(dataSpacing * 0.75f / 2);
float lineEndX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* (index + 1) +
(dataSpacing * 0.75f / 2);

if (i >= 25) {
float startARY = (float) (downChartBottom - (entity.getBrar()
.getAr() - downMinData) *
downDataRatio);
float endARY = (float) (downChartBottom - (data.get(i + 1)
.getBrar().getAr() - downMinData)
* downDataRatio);
// paint.setColor(colors[0]);
// canvas.drawLine(lineStartX, startARY, lineEndX, endARY, paint);

if (i == position || (position <= 25 && i == 25)) {
path.moveTo(lineStartX, startARY);
} else {
path.lineTo(lineEndX, endARY);
}

if (i >= 26) {
float startBRY = (float) (downChartBottom - (entity
.getBrar().getBr() -
downMinData) * downDataRatio);
float endBRY = (float) (downChartBottom - (data.get(i + 1)
.getBrar().getBr() -
downMinData)
* downDataRatio);
// paint.setColor(colors[1]);
// canvas.drawLine(lineStartX, startBRY, lineEndX, endBRY,
// paint);
if (i == position || (position <= 26 && i == 26)) {
path1.moveTo(lineStartX, startBRY);
} else {
path1.lineTo(lineEndX, endBRY);
}
}
}
index++;
}
paint.setStyle(Style.STROKE);
paint.setColor(colors[0]);
canvas.drawPath(path, paint);
paint.setColor(colors[1]);
canvas.drawPath(path1, paint);
}

/**
* 绘制CCI
*
*
@param canvas
* @param paint
*/
private void drawCCI(Canvas canvas, Paint paint) {
int[] colors = {0xff187cef};// 白
downColors = colors;
int index = 0;
paint.setStrokeWidth(1);
Path path = new Path();
for (int i = position; i < data.size() - 1
&& i < candleNum + position - 1; i++) {
KLineData entity = data.get(i);
float lineStartX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* index +
(dataSpacing * 0.75f / 2);
float lineEndX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* (index + 1) +
(dataSpacing * 0.75f / 2);

if (i >= 13) {
float startCCIY = (float) (downChartBottom - (entity.getCci()
.getCci() - downMinData) *
downDataRatio);
float endCCIY = (float) (downChartBottom - (data.get(i + 1)
.getCci().getCci() -
downMinData)
* downDataRatio);
// paint.setColor(colors[0]);
// canvas.drawLine(lineStartX, startCCIY, lineEndX, endCCIY, paint);
if (i == position || (position <= 13 && i == 13)) {
path.moveTo(lineStartX, startCCIY);
} else {
path.lineTo(lineEndX, endCCIY);
}

}
index++;
}
paint.setStyle(Style.STROKE);
paint.setColor(colors[0]);
canvas.drawPath(path, paint);
}

/**
* 绘制CCI
*
*
@param canvas
* @param paint
*/
private void drawDMI(Canvas canvas, Paint paint) {
int[] colors = {0xff187cef, 0xfff5a623, 0xfff80101, 0xff74bd4f};// 白,黄,红,绿
downColors = colors;
int index = 0;
paint.setStrokeWidth(1);
Path path = new Path();
Path path1 = new Path();
Path path2 = new Path();
Path path3 = new Path();
for (int i = position; i < data.size() - 1
&& i < candleNum + position - 1; i++) {
KLineData entity = data.get(i);
float lineStartX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* index +
(dataSpacing * 0.75f / 2);
float lineEndX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* (index + 1) +
(dataSpacing * 0.75f / 2);

if (i >= 14) {
float startPDIY = (float) (downChartBottom - (entity.getDmi()
.getPdi() - downMinData) *
downDataRatio);
float endPDIY = (float) (downChartBottom - (data.get(i + 1)
.getDmi().getPdi() -
downMinData)
* downDataRatio);
// paint.setColor(colors[0]);
// canvas.drawLine(lineStartX, startPDIY, lineEndX, endPDIY, paint);
float startMDIY = (float) (downChartBottom - (entity.getDmi()
.getMdi() - downMinData) *
downDataRatio);
float endMDIY = (float) (downChartBottom - (data.get(i + 1)
.getDmi().getMdi() -
downMinData)
* downDataRatio);
// paint.setColor(colors[1]);
// canvas.drawLine(lineStartX, startMDIY, lineEndX, endMDIY, paint);
if (i == position || (position <= 14 && i == 14)) {
path.moveTo(lineStartX, startPDIY);
path1.moveTo(lineStartX, startMDIY);
} else {
path.lineTo(lineEndX, endPDIY);
path1.lineTo(lineEndX, endMDIY);
}
if (i >= 19) {
float startADXY = (float) (downChartBottom - (entity
.getDmi().getAdx() -
downMinData) * downDataRatio);
float endADXY = (float) (downChartBottom - (data.get(i + 1)
.getDmi().getAdx() -
downMinData)
* downDataRatio);
// paint.setColor(colors[2]);
// canvas.drawLine(lineStartX, startADXY, lineEndX, endADXY,
// paint);
if (i == position || (position <= 19 && i == 19)) {
path2.moveTo(lineStartX, startADXY);
} else {
path2.lineTo(lineEndX, endADXY);
}
if (i >= 25) {
float startADXRY = (float) (downChartBottom - (entity
.getDmi().getAdxr() -
downMinData)
* downDataRatio);
float endADXRY = (float) (downChartBottom - (data
.get(i +
1).getDmi().getAdxr() -
downMinData)
* downDataRatio);
// paint.setColor(colors[3]);
// canvas.drawLine(lineStartX, startADXRY, lineEndX,
// endADXRY, paint);
if (i == position || (position <= 25 && i == 25)) {
path3.moveTo(lineStartX, startADXRY);
} else {
path3.lineTo(lineEndX, endADXRY);
}
}
}
}
index++;
}
paint.setStyle(Style.STROKE);
paint.setColor(colors[0]);
canvas.drawPath(path, paint);
paint.setColor(colors[1]);
canvas.drawPath(path1, paint);
paint.setColor(colors[2]);
canvas.drawPath(path2, paint);
paint.setColor(colors[3]);
canvas.drawPath(path3, paint);
}

/**
* 绘制CR
*
*
@param canvas
* @param paint
*/
private void drawCR(Canvas canvas, Paint paint) {
int[] colors = {0xff187cef, 0xfff5a623, 0xfff80101, 0xff74bd4f};// 白,黄,红,绿
downColors = colors;
int index = 0;
paint.setStrokeWidth(1);
Path path = new Path();
Path path1 = new Path();
Path path2 = new Path();
Path path3 = new Path();
for (int i = position; i < data.size() - 1
&& i < candleNum + position - 1; i++) {
KLineData entity = data.get(i);
float lineStartX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* index +
(dataSpacing * 0.75f / 2);
float lineEndX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* (index + 1) +
(dataSpacing * 0.75f / 2);

if (i >= 26) {
float startCRY = (float) (downChartBottom - (entity.getCr()
.getCr() - downMinData) *
downDataRatio);
float endCRY = (float) (downChartBottom - (data.get(i + 1)
.getCr().getCr() - downMinData)
* downDataRatio);
// paint.setColor(colors[0]);
// canvas.drawLine(lineStartX, startCRY, lineEndX, endCRY, paint);

if (i == position || (position <= 26 && i == 26)) {
path.moveTo(lineStartX, startCRY);
} else {
path.lineTo(lineEndX, endCRY);
}

if (i >= 33) {
float startMA1Y = (float) (downChartBottom - (entity
.getCr().getMa1() -
downMinData) * downDataRatio);
float endMA1Y = (float) (downChartBottom - (data.get(i + 1)
.getCr().getMa1() -
downMinData)
* downDataRatio);
// paint.setColor(colors[1]);
// canvas.drawLine(lineStartX, startMA1Y, lineEndX, endMA1Y,
// paint);
if (i == position || (position <= 33 && i == 33)) {
path1.moveTo(lineStartX, startMA1Y);
} else {
path1.lineTo(lineEndX, endMA1Y);
}
if (i >= 40) {
float startMA2Y = (float) (downChartBottom - (entity
.getCr().getMa2() -
downMinData)
* downDataRatio);
float endMA2Y = (float) (downChartBottom - (data
.get(i +
1).getCr().getMa2() -
downMinData)
* downDataRatio);
// paint.setColor(colors[2]);
// canvas.drawLine(lineStartX, startMA2Y, lineEndX,
// endMA2Y, paint);
if (i == position || (position <= 40 && i == 40)) {
path2.moveTo(lineStartX, startMA2Y);
} else {
path2.lineTo(lineEndX, endMA2Y);
}
if (i >= 54) {
float startMA3Y = (float) (downChartBottom - (entity
.getCr().getMa3() -
downMinData)
* downDataRatio);
float endMA3Y = (float) (downChartBottom - (data
.get(i +
1).getCr().getMa3() -
downMinData)
* downDataRatio);
// paint.setColor(colors[3]);
// canvas.drawLine(lineStartX, startMA3Y, lineEndX,
// endMA3Y, paint);
if (i == position || (position <= 54 && i == 54)) {
path3.moveTo(lineStartX, startMA3Y);
} else {
path3.lineTo(lineEndX, endMA3Y);
}
}
}
}
}
index++;
}
paint.setStyle(Style.STROKE);
paint.setColor(colors[0]);
canvas.drawPath(path, paint);
paint.setColor(colors[1]);
canvas.drawPath(path1, paint);
paint.setColor(colors[2]);
canvas.drawPath(path2, paint);
paint.setColor(colors[3]);
canvas.drawPath(path3, paint);
}

/**
* 绘制PSY
*
*
@param canvas
* @param paint
*/
private void drawPSY(Canvas canvas, Paint paint) {
int[] colors = {0xff187cef};
downColors = colors;
int index = 0;
paint.setStrokeWidth(1);
Path path = new Path();
for (int i = position; i < data.size() - 1
&& i < candleNum + position - 1; i++) {
KLineData entity = data.get(i);
float lineStartX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* index +
(dataSpacing * 0.75f / 2);
float lineEndX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* (index + 1) +
(dataSpacing * 0.75f / 2);

if (i >= 12) {
float startPSYY = (float) (downChartBottom - (entity.getPsy()
.getPsy() - downMinData) *
downDataRatio);
float endPSYY = (float) (downChartBottom - (data.get(i + 1)
.getPsy().getPsy() -
downMinData)
* downDataRatio);
// paint.setColor(colors[0]);
// canvas.drawLine(lineStartX, startPSYY, lineEndX, endPSYY, paint);

if (i == position || (position <= 12 && i == 12)) {
path.moveTo(lineStartX, startPSYY);
} else {
path.lineTo(lineEndX, endPSYY);
}
}
index++;
}
paint.setStyle(Style.STROKE);
paint.setColor(colors[0]);
canvas.drawPath(path, paint);
}

/**
* 绘制DMA
*
*
@param canvas
* @param paint
*/
private void drawDMA(Canvas canvas, Paint paint) {
int[] colors = {0xff187cef, 0xfff5a623};// 白,黄
downColors = colors;
int index = 0;
paint.setStrokeWidth(1);
Path path = new Path();
Path path1 = new Path();
for (int i = position; i < data.size() - 1
&& i < candleNum + position - 1; i++) {
KLineData entity = data.get(i);
float lineStartX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* index +
(dataSpacing * 0.75f / 2);
float lineEndX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* (index + 1) +
(dataSpacing * 0.75f / 2);

if (i >= 49) {
float startDIFY = (float) (downChartBottom - (entity.getDma()
.getDif() - downMinData) *
downDataRatio);
float endDIFY = (float) (downChartBottom - (data.get(i + 1)
.getDma().getDif() -
downMinData)
* downDataRatio);
// paint.setColor(colors[0]);
// canvas.drawLine(lineStartX, startDIFY, lineEndX, endDIFY, paint);
if (i == position || (position <= 49 && i == 49)) {
path.moveTo(lineStartX, startDIFY);
} else {
path.lineTo(lineEndX, endDIFY);
}
if (i >= 58) {
float startAMAY = (float) (downChartBottom - (entity
.getDma().getAma() -
downMinData) * downDataRatio);
float endAMAY = (float) (downChartBottom - (data.get(i + 1)
.getDma().getAma() -
downMinData)
* downDataRatio);
// paint.setColor(colors[1]);
// canvas.drawLine(lineStartX, startAMAY, lineEndX, endAMAY,
// paint);
if (i == position || (position <= 58 && i == 58)) {
path1.moveTo(lineStartX, startAMAY);
} else {
path1.lineTo(lineEndX, endAMAY);
}
}
}
index++;
}
paint.setStyle(Style.STROKE);
paint.setColor(colors[0]);
canvas.drawPath(path, paint);
paint.setColor(colors[1]);
canvas.drawPath(path1, paint);
}

/**
* 绘制TRIX
*
*
@param canvas
* @param paint
*/
private void drawTRIX(Canvas canvas, Paint paint) {
int[] colors = {0xff187cef, 0xfff5a623};// 白,黄
downColors = colors;
int index = 0;
paint.setStrokeWidth(1);
Path path = new Path();
Path path1 = new Path();
for (int i = position; i < data.size() - 1
&& i < candleNum + position - 1; i++) {
KLineData entity = data.get(i);
float lineStartX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* index +
(dataSpacing * 0.75f / 2);
float lineEndX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* (index + 1) +
(dataSpacing * 0.75f / 2);

if (i >= 33) {
float startTRIXY = (float) (downChartBottom - (entity.getTrix()
.getTrix() - downMinData) *
downDataRatio);
float endTRIXY = (float) (downChartBottom - (data.get(i + 1)
.getTrix().getTrix() -
downMinData)
* downDataRatio);
// paint.setColor(colors[0]);
// canvas.drawLine(lineStartX, startTRIXY, lineEndX, endTRIXY,
// paint);
if (i == position || (position <= 33 && i == 33)) {
path.moveTo(lineStartX, startTRIXY);
} else {
path.lineTo(lineEndX, endTRIXY);
}
if (i >= 52) {
float startMATRIXY = (float) (downChartBottom - (entity
.getTrix().getMaTrix() -
downMinData)
* downDataRatio);
float endMATRIXY = (float) (downChartBottom - (data
.get(i +
1).getTrix().getMaTrix() -
downMinData)
* downDataRatio);
// paint.setColor(colors[1]);
// canvas.drawLine(lineStartX, startMATRIXY, lineEndX,
// endMATRIXY, paint);
if (i == position || (position <= 52 && i == 52)) {
path1.moveTo(lineStartX, startMATRIXY);
} else {
path1.lineTo(lineEndX, endMATRIXY);
}
}
}
index++;
}
paint.setStyle(Style.STROKE);
paint.setColor(colors[0]);
canvas.drawPath(path, paint);
paint.setColor(colors[1]);
canvas.drawPath(path1, paint);
}

/**
* 绘制上表左边Y刻度
*
*
@param canvas 画布
*
@param paint 画笔
*/
private void drawUpAxisXTitle(Canvas canvas, TextPaint paint,
float dataWidth) {
paint.setColor(getResources().getColor(R.color.text_color_5c5f66));
if (!isDrawOutside) {
float YData = upMaxData;
float spacing = (upMaxData - upMinData) / (latitudesUpNum);
for (int i = 0; i < latitudesUpNum + 1; i++) {
if (i == 0) {
canvas.drawText(
Parse.getInstance().parse2String(upMaxData, num),
getStrokeWidth() + getStrokeLeft() + 2,
getStrokeWidth() + getStrokeTop() + textSize, paint);
} else if (i == latitudesUpNum) {
canvas.drawText(
Parse.getInstance().parse2String(upMinData, num),
getStrokeWidth() + getStrokeLeft() + 2,
(getStrokeWidth() + getStrokeTop() + upLatitudesSpacing
* latitudesUpNum) - 2, paint);
} else {
canvas.drawText(Parse.getInstance()
.parse2String(YData, num), getStrokeWidth()
+ getStrokeLeft() + 2, getStrokeWidth()
+ getStrokeTop() +
textSize / 2
+ upLatitudesSpacing *
i, paint);
}
YData -= spacing;
}
} else {
float YData = upMaxData;
float spacing = (upMaxData - upMinData) / (latitudesUpNum);
for (int i = 0; i < latitudesUpNum + 1; i++) {
// if (i < 2) {
// paint.setColor(getResources().getColor(R.color.z_red));
// } else if (i > 2) {
// paint.setColor(getResources().getColor(R.color.z_green));
// } else {
// paint.setColor(getResources()
// .getColor(R.color.z_list_title));
// }
if (i == 0) {
canvas.drawText(
Parse.getInstance().parse2String(YData, num),
getStrokeLeft()
- getStrokeWidth()
/ 2
- 2
- paint.measureText(Parse.getInstance()
.parse2String(YData, num)),
getStrokeWidth() + getStrokeTop() + textSize
+ upLatitudesSpacing * i, paint);
} else if (i == latitudesUpNum) {
canvas.drawText(
Parse.getInstance().parse2String(YData, num),
getStrokeLeft()
- getStrokeWidth()
/ 2
- 2
- paint.measureText(Parse.getInstance()
.parse2String(YData, num)),
(getStrokeWidth() + getStrokeTop() + upLatitudesSpacing
* i) - 2, paint);
} else {
canvas.drawText(
Parse.getInstance().parse2String(YData, num),
getStrokeLeft()
- getStrokeWidth()
/ 2
- 2
- paint.measureText(Parse.getInstance()
.parse2String(YData, num)),
getStrokeWidth() + getStrokeTop() + textSize / 2
+ upLatitudesSpacing * i, paint);
}
YData -= spacing;
}
}

}

/**
* 绘制下表Y轴X坐标
*
*
@param canvas
* @param paint
*/
private void drawDownAxisXTitle(Canvas canvas, TextPaint paint,
float dataWidth) {
paint.setColor(getResources().getColor(R.color.text_color_5c5f66));
float YData = downMinData;
float spacing = (downMaxData - downMinData) / (latitudesDownNum);

String text;

if (!isDrawOutside) {
for (int i = 0; i < latitudesDownNum + 1; i++) {
if (mIndexType == IndexType.VOL || mMainIndexType == MainIndexType.BOLL) {
text = Parse.getInstance().parse2CNStringWan(YData, 2,
false);
} else {
text = Parse.getInstance().parse2String(YData, num);
}
if (i == latitudesDownNum) {
canvas.drawText(text, getStrokeWidth() + getStrokeLeft()
+ 2, downChartBottom + textSize
- downLatitudesSpacing * i, paint);
} else if (i == 0) {
canvas.drawText(text, getStrokeWidth() + getStrokeLeft()
+ 2,
downChartBottom - downLatitudesSpacing * i - 2,
paint);
} else {
canvas.drawText(text, getStrokeWidth() + getStrokeLeft()
+ 2, downChartBottom + textSize / 2
- downLatitudesSpacing * i, paint);
}
YData += spacing;
}

} else {
for (int i = 0; i < latitudesDownNum + 1; i++) {
if (mIndexType == IndexType.VOL) {
text = Parse.getInstance().parse2CNStringWan(YData, 2,
false);
} else if (mIndexType == IndexType.MACD
// || mIndexType == IndexType.BOLL
|| mIndexType == IndexType.KDJ
|| mIndexType == IndexType.KD
|| mIndexType == IndexType.RSI
|| mIndexType == IndexType.BIAS
|| mIndexType == IndexType.BRAR
|| mIndexType == IndexType.CCI
|| mIndexType == IndexType.DMI
|| mIndexType == IndexType.CR
|| mIndexType == IndexType.PSY
|| mIndexType == IndexType.DMA
|| mIndexType == IndexType.TRIX) {
text = Parse.getInstance().parse2String(YData, num);
} else
text = Parse.getInstance().parse2CNStringWan(YData, 2,
false);
if (i == latitudesDownNum) {
canvas.drawText(text, getStrokeLeft() - getStrokeWidth()
/ 2 - 2 - paint.measureText(text),
downChartBottom
+ textSize - downLatitudesSpacing * i, paint);
} else if (i == 0) {
canvas.drawText(text, getStrokeLeft() - getStrokeWidth()
/ 2 - 2 - paint.measureText(text),
downChartBottom
- downLatitudesSpacing * i - 2, paint);
} else {
canvas.drawText(text, getStrokeLeft() - getStrokeWidth()
/ 2 - 2 - paint.measureText(text),
downChartBottom
+ textSize / 2 - downLatitudesSpacing * i, paint);
}
YData += spacing;
}
}
}

/**
* 绘制十字光标
*
*
@param canvas
* @param paint
*/
private void drawCrossLine(Canvas canvas, Paint paint, TextPaint tp) {
if (isShowCrossLine) {
paint.setStyle(Style.FILL);
paint.setColor(getContext().getResources().getColor(
R.color.cross_line_color));
paint.setStrokeWidth(1);
canvas.drawLine(getStrokeWidth() + getStrokeLeft(), crossY,
viewWidth - getStrokeWidth() - getStrokeRight(), crossY,
paint);
canvas.drawLine(crossX, getStrokeTop() + getStrokeWidth(), crossX,
upChartBottom, paint);
canvas.drawLine(crossX, upChartBottom + spacing, crossX,
downChartBottom, paint);

tp.setColor(Color.WHITE);
// float textWidth =
// tp.measureText(Parse.getInstance().parse2String(
// leftData));
float size = MyUtils.getInstance().dp2px(getContext(), 10);
paint.setStrokeWidth(textSize + 6);
String leftDatas = Parse.getInstance().parse2String(leftData, num);
float topS = crossY - (textSize + 6) / 2 <= getStrokeTop()
+ getStrokeWidth() ? getStrokeTop() +
getStrokeWidth()
+ (textSize + 6) / 2
: crossY;
topS = topS + (textSize + 6) / 2 >= upChartBottom ? upChartBottom
- (textSize + 6) / 2 : topS;
float dataWidth = tp.measureText(leftDatas);
if (!isDrawOutside) {
canvas.drawLine(getStrokeLeft() + getStrokeWidth() / 2
+ dataWidth + size, topS, getStrokeLeft()
+ getStrokeWidth() / 2, topS, paint);
canvas.drawText(leftDatas, getStrokeLeft() + getStrokeWidth()
/ 2 + size / 2,
topS + textSize / 2 - 3, tp);

/**
* 绘制时间
*/
String timeDatas;
if (isShowTime) {
timeDatas = MyUtils.getInstance().date2String("yyyy/MM/dd HH:mm", date);
} else {
if(data.get(0).getType() == T_D_TYPE){
timeDatas = MyUtils.getInstance().date2String("yyyy/MM/dd HH:mm:ss", date);
timeDatas = judgeTDNextDay(timeDatas);
}else {
timeDatas = MyUtils.getInstance().date2String("yyyy/MM/dd", date);

}
}
dataWidth = tp.measureText(timeDatas);
float timeY = upChartBottom + textSize / 2;
float drawTimeX = crossX - (dataWidth + size) / 2;
if (drawTimeX <= getStrokeLeft() + getStrokeWidth() / 2) {
drawTimeX = getStrokeLeft() + getStrokeWidth() / 2;
} else if (drawTimeX >= getWidth() - getStrokeRight()
- getStrokeWidth() / 2 - dataWidth - size) {
drawTimeX = getWidth() - getStrokeRight() - getStrokeWidth() / 2
- dataWidth - size;
}
canvas.drawLine(drawTimeX, timeY, drawTimeX + dataWidth + size, timeY, paint);
canvas.drawText(timeDatas, drawTimeX + size / 2, timeY + textSize / 2, tp);
} else {
size = MyUtils.getInstance().dp2px(getContext(), 1);
canvas.drawLine(getStrokeLeft() - getStrokeWidth() / 2
- dataWidth - size, topS, getStrokeLeft()
- getStrokeWidth() / 2, topS, paint);
canvas.drawText(leftDatas, getStrokeLeft() - getStrokeWidth()
/ 2 - dataWidth - size / 2,
topS + textSize / 2 - 3, tp);

/**
* 绘制时间
*/
String timeDatas;
if (isShowTime) {
timeDatas = MyUtils.getInstance().date2String("yyyy/MM/dd HH:mm", date);
} else {
timeDatas = MyUtils.getInstance().date2String("yyyy/MM/dd", date);
}
dataWidth = tp.measureText(timeDatas);
float timeY = upChartBottom + textSize / 2;
float drawTimeX = crossX - (dataWidth + size) / 2;
if (drawTimeX <= getStrokeLeft() + getStrokeWidth() / 2) {
drawTimeX = getStrokeLeft() + getStrokeWidth() / 2;
} else if (drawTimeX >= getWidth() - getStrokeRight()
- getStrokeWidth() / 2 - dataWidth - size) {
drawTimeX = getWidth() - getStrokeRight() - getStrokeWidth() / 2
- dataWidth - size;
}
canvas.drawLine(drawTimeX, timeY, drawTimeX + dataWidth + size, timeY, paint);
canvas.drawText(timeDatas, drawTimeX + size / 2, timeY + textSize / 2, tp);
}

}
}

/**
* 画箭头
*
*
@param sx
* @param sy
* @param ex
* @param ey
*/
public void drawAL(Canvas canvas, Paint paint, int sx, int sy, int ex, int ey) {
double H = 8; // 箭头高度
double L = 3.5; // 底边的一半
int x3 = 0;
int y3 = 0;
int x4 = 0;
int y4 = 0;
double awrad = Math.atan(L / H); // 箭头角度
double arraow_len = Math.sqrt(L * L + H * H); // 箭头的长度
double[] arrXY_1 = rotateVec(ex - sx, ey - sy, awrad, true, arraow_len);
double[] arrXY_2 = rotateVec(ex - sx, ey - sy, -awrad, true, arraow_len);
double x_3 = ex - arrXY_1[0]; // (x3,y3)是第一端点
double y_3 = ey - arrXY_1[1];
double x_4 = ex - arrXY_2[0]; // (x4,y4)是第二端点
double y_4 = ey - arrXY_2[1];
Double X3 = new Double(x_3);
x3 = X3.intValue();
Double Y3 = new Double(y_3);
y3 = Y3.intValue();
Double X4 = new Double(x_4);
x4 = X4.intValue();
Double Y4 = new Double(y_4);
y4 = Y4.intValue();
// 画线
canvas.drawLine(sx, sy, ex, ey, paint);
Path triangle = new Path();
triangle.moveTo(ex, ey);
triangle.lineTo(x3, y3);
triangle.lineTo(x4, y4);
triangle.close();
canvas.drawPath(triangle, paint);
}

// 计算
public double[] rotateVec(int px, int py, double ang, boolean isChLen, double newLen) {
double mathstr[] = new double[2];
// 矢量旋转函数,参数含义分别是x分量、y分量、旋转角、是否改变长度、新长度
double vx = px * Math.cos(ang) - py * Math.sin(ang);
double vy = px * Math.sin(ang) + py * Math.cos(ang);
if (isChLen) {
double d = Math.sqrt(vx * vx + vy * vy);
vx = vx / d * newLen;
vy = vy / d * newLen;
mathstr[0] = vx;
mathstr[1] = vy;
}
return mathstr;
}


@TargetApi(Build.VERSION_CODES.FROYO)
@Override
public boolean onTouchEvent(MotionEvent event) {
obtainVelocityTracker(event);
if (event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
releaseVelocityTracker();
}

switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
// 按下
if (data != null)
stopDeceleration();
downX = event.getX();
downY = event.getY();
if (downX < getStrokeLeft()
|| downX > getWidth() - getStrokeRight()) {
return super.onTouchEvent(event);
}
moveX = event.getRawX();
moveY = event.getRawY();
// 记录触摸起始点坐标
mTouchStartPoint.set(event.getX(), event.getY());
touchMode = 0;
// if (downX < getStrokeLeft() + getStrokeWidth() / 2) {
// return false;
// }
isMoved = false;
isReleased = false;
isStartMoved = false;
// isLeftRightMoved = false;
getParent().requestDisallowInterceptTouchEvent(true);
if (mRunnable != null) {
removeCallbacks(mRunnable);
postDelayed(mRunnable, 200);
}
return true;

case MotionEvent.ACTION_POINTER_DOWN:
// 多点
if (downX < getStrokeLeft()
|| downX > getWidth() - getStrokeRight()) {
return super.onTouchEvent(event);
}
if (data == null)
return false;
// oldDistance = spacing(event);

// 多点触摸一些起始性数据的记录
if (event.getPointerCount() >= 2) {

mSavedXDist = ChartUtils.getXDist(event);
mSavedDist = ChartUtils.spacing(event);

if (mSavedDist > 10f) {
isMoved = true;
touchMode = TOUCH_ZOOM;
}

// 取得多点触摸的两点的中点
ChartUtils.midPoint(mTouchPointCenter, event);
// 得到触摸所在的位置,保持缩放后,触摸位置不变
mTouchPosition = calculationTouchPosition(mTouchPointCenter);
mTouchCandleNum = candleNum;
mTouchStartPosition = position;
}

return true;

case MotionEvent.ACTION_MOVE:
// 触摸
if (downX < getStrokeLeft()
|| downX > getWidth() - getStrokeRight()) {
return super.onTouchEvent(event);
}
if (data == null)
return false;
// 子控件相对于父布局若达到滚动条件,则让父布局拦截触摸事件
if (Math.abs(event.getRawY() - moveY) > 50
&& Math.abs(event.getRawX() - moveX) < 150
&& touchMode == 0) {
isMoved = true;
getParent().requestDisallowInterceptTouchEvent(false);
}
// 左右边侧触摸时,忽略事件
// if (downX < getStrokeLeft() + getStrokeWidth() / 2) {
// return false;
// }
if (touchMode == TOUCH_ZOOM) {
// 处理缩放
return performZoom(event);
} else if (Math.abs(Math.abs(event.getX()) - Math.abs(downX)) > 50
&& Math.abs(Math.abs(event.getY()) - Math.abs(downY)) < 150
&& touchMode == 0) {
isMoved = true;
touchMode = TOUCH_DRAG;
}
if (touchMode == TOUCH_DRAG) {
return performDrag(event);
} else if (touchMode == TOUCH_ADSORPTION) {
return performAsorption(event);
}
return true;

case MotionEvent.ACTION_POINTER_UP:
if (downX < getStrokeLeft()
|| downX > getWidth() - getStrokeRight()) {
return super.onTouchEvent(event);
}
touchMode = 0;
ChartUtils.velocityTrackerPointerUpCleanUpIfNecessary(event,
mVelocityTracker);
return true;

case MotionEvent.ACTION_UP:
if (downX < getStrokeLeft()
|| downX > getWidth() - getStrokeRight()) {
return super.onTouchEvent(event);
}
// 对一次完整的触摸时间做计算统计,判断是否为快速手势事件
final VelocityTracker velocityTracker = mVelocityTracker;
final int pointerId = event.getPointerId(0);
velocityTracker.computeCurrentVelocity(1000,
ChartUtils.getMaximumFlingVelocity());
final float velocityY = velocityTracker.getYVelocity(pointerId);
final float velocityX = velocityTracker.getXVelocity(pointerId);
// 重置上一次fling的距离为0
mLastFlingDist = 0;
if (Math.abs(velocityX) > ChartUtils.getMinimumFlingVelocity()) {

if (touchMode == TOUCH_DRAG) {
mDecelerationLastTime = AnimationUtils
.currentAnimationTimeMillis();
mDecelerationCurrentPoint = new PointF(event.getX(),
event.getY());
mDecelerationVelocity = new PointF(velocityX, velocityY);

ChartUtils.postInvalidateOnAnimation(this);
}
}
if (touchMode == 0 && Math.abs(downX - event.getX()) < 30
&& Math.abs(downY - event.getY()) < 30) {
if (clickL != null)
clickL.click(this);
}
// 拿起
touchMode = 0;
getParent().requestDisallowInterceptTouchEvent(false);
isReleased = true;
isShowCrossLine = false;
if (l != null && data != null)
l.transferData(this, false, crossX, upColors, downColors, data,
(position + candleNum) >= data.size() ? data.size() - 1
: position +
candleNum, mIndexType, num);
invalidate();

releaseVelocityTracker();
return true;

default:
break;
}
return super.onTouchEvent(event);
}

/**
* (non-Javadoc)
*
*
@see View#computeScroll()
* <p/>
* 计算控件内容滚动。 此处的滚动并非视图的真正滚动,而是内容展示的滚动,根据手势记录下来的轨迹参数进行平滑的内容切换展示。
*/
@Override
public void computeScroll() {
if (data == null)
return;

int size = data.size();
if (mDecelerationVelocity.x == 0.f) {
// 滚动结束
return;
}

// 记录当前时间
final long currentTime = AnimationUtils.currentAnimationTimeMillis();

// 递减加速度范围(0-0.99],此值必须小于1且大于0的值,决定滚动的速度的快慢,越接近0速度越快
mDecelerationVelocity.x *= 0.9;

final float timeInterval = (float) (currentTime - mDecelerationLastTime) / 1000.f;

float distanceX = mDecelerationVelocity.x * timeInterval;

mDecelerationCurrentPoint.x += distanceX;

MotionEvent event = MotionEvent.obtain(currentTime, currentTime,
MotionEvent.ACTION_MOVE, mDecelerationCurrentPoint.x,
mDecelerationCurrentPoint.y, 0);
// 当前滚动的距离
float currFlingDist = event.getX() - mTouchStartPoint.x;
if (mLastFlingDist == 0)
mLastFlingDist = currFlingDist;
// 两次滚动距离的差,据此来计算滚动了多少个k线柱,两次滚动的值大于1个K线柱体的时候重绘视图
float fingDist = currFlingDist - mLastFlingDist;
if (Math.abs(fingDist) > dataSpacing) {
mLastFlingDist = currFlingDist;
if (fingDist < 0) {
isMoved = true;
// isLeftRightMoved = true;
if (size < candleNum) {
return;
} else {
int scaleCandle = (int) Math.abs(fingDist / dataSpacing);
for (int i = scaleCandle - 1; i >= 0; i--) {
position++;
if (position >= size - candleNum) {
position = size - candleNum;
invalidate();
break;
}
invalidate();
}
}
} else {
isMoved = true;
// isLeftRightMoved = true;
if (size < candleNum) {
return;
} else {
int scaleCandle = (int) Math.abs(fingDist / dataSpacing);
for (int i = scaleCandle - 1; i >= 0; i--) {
position--;
if (position <= 0) {
position = 0;
invalidate();
break;
}
invalidate();
}
}
}
}

event.recycle();
mDecelerationLastTime = currentTime;
if (Math.abs(mDecelerationVelocity.x) >= 1) {
ChartUtils.postInvalidateOnAnimation(this); // This causes
// computeScroll to
// fire, recommended for
// this by Google
} else {
stopDeceleration();
}
}

/**
* 减速运行结束,停止滚动
*/
public void stopDeceleration() {
mDecelerationVelocity = new PointF(0.f, 0.f);
}

private void obtainVelocityTracker(MotionEvent event) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
}

private void releaseVelocityTracker() {
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}

/**
* 处理拖拽视图事件。
*
*
@param event
* @return
*/
private boolean performDrag(MotionEvent event) {
// 左右滑动事件
float indexX = event.getRawX() - moveX;
if (indexX < 0 - dataSpacing / 2) {
moveX = event.getRawX();
isMoved = true;
// isLeftRightMoved = true;
if (this.data.size() < candleNum) {
return false;
}

int scaleCandle = (int) Math.abs(indexX / dataSpacing);
if (scaleCandle == 0)
scaleCandle = 1;
position += scaleCandle;
if (position >= this.data.size() - candleNum) {
position = this.data.size() - candleNum;
}
invalidate();
return true;
} else if (indexX > dataSpacing / 2) {
moveX = event.getRawX();
isMoved = true;
// isLeftRightMoved = true;
if (this.data.size() < candleNum) {
return false;
}
int scaleCandle = (int) Math.abs(indexX / dataSpacing);
if (scaleCandle == 0)
scaleCandle = 1;
position -= scaleCandle;
if (position <= 0) {
position = 0;
}
invalidate();
if (isLoadMore && position == 0) {
isLoadMore = false;
if (onLoadMoreListener != null)
onLoadMoreListener.onLoadMore();
}
return true;
}
return true;
}

/**
* 处理十字光标视图事件。
*
*
@param event
* @return
*/
private boolean performAsorption(MotionEvent event) {
if (isStartMoved) {
if (data != null && data.size() > 0) {
calculationCrossLine(event);
return true;
}
}
return true;
}

/**
* 多点触摸,根据手势进行放大或者缩小。
*
*
@param event
*/
@TargetApi(Build.VERSION_CODES.ECLAIR)
private boolean performZoom(MotionEvent event) {
if (event.getPointerCount() >= 2) {
float totalDist = ChartUtils.spacing(event);
if (totalDist > 10f) {
float xDist = ChartUtils.getXDist(event);
// x轴方向 scale
float scaleX = xDist / mSavedXDist;

// 是否缩小
boolean isZoomingOut = (scaleX < 1);
// Log.d(VIEW_LOG_TAG,
// "touchPostion:"+mTouchPosition+",xDist:"+xDist+",mSavedXDist:"+mSavedXDist+",isZoomingOut:"+isZoomingOut+",scaleX:"+scaleX);
if (!isZoomingOut) {
if (candleNum <= minCandleNum) {
// 无法继续放大
return false;
}
} else {
if (candleNum >= maxCandleNum) {
// 无法继续缩小
return false;
}
}
isMoved = true;
// Log.d(VIEW_LOG_TAG,
// "position:"+position+",candleNum:"+candleNum);
// 计算缩放后的position位置
if (mTouchPosition != -1)
position = (int) (mTouchPosition - (mTouchPosition - mTouchStartPosition)
/ scaleX);
// 防止position超出边界
if (position < 0)
position = 0;
// 计算缩放后的candelNum大小
candleNum = (int) (mTouchCandleNum / scaleX);

invalidate();
}
}
return true;
}

/**
* 计算十字光标位置
*
*
@param event
*/
private void calculationCrossLine(MotionEvent event) {
float startX = getStrokeWidth() + getStrokeLeft();
int index = 0;
for (int i = position; i < data.size() && i < candleNum + position; i++) {
KLineData entity = data.get(i);
// 柱形图
float openY = upChartBottom
- ((float) entity.getOpen() - upMinData) * upDataRatio;
float closeY = upChartBottom
- ((float) entity.getClose() - upMinData) * upDataRatio;
// 中线
// float highY = upChartBottom - (entity.getHigh() - upMinData)
// * upDataRatio;
// float lowY = upChartBottom - (entity.getLow() - upMinData)
// * upDataRatio;

float endX = getStrokeWidth() + getStrokeLeft()
+ (dataSpacing * (index + 1) - dataSpacing * 0.25f);

if (event.getX() > startX && event.getX() < endX) {
isShowCrossLine = true;
crossX = (endX - startX) / 2 + startX;
if (openY < closeY) {// 绿色
crossY = closeY;
} else if (openY > closeY) {// 红色
crossY = closeY;
} else {// 红色
crossY = openY;
}
leftData = Parse.getInstance().parseFloat(
Parse.getInstance()
.parse2String(entity.getClose(), num));
date = entity.getTime() * 1000;
if (l != null) {
l.transferData(this, true, crossX, upColors, downColors,
data, i, mIndexType, num);
}
invalidate();
return;
}
startX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* (index + 1);
index++;
}
}

/**
* 计算触摸所在的数据位置
*/
private int calculationTouchPosition(PointF point) {
float startX = getStrokeWidth() + getStrokeLeft();
int index = 0;
for (int i = position; i < data.size() && i < candleNum + position && i >= 0; i++) {

KLineData entity = data.get(i);
// 柱形图
// float openY = upChartBottom
// - ((float) entity.getOpen() - upMinData) * upDataRatio;
// float closeY = upChartBottom
// - ((float) entity.getClose() - upMinData) * upDataRatio;
// 中线
// float highY = upChartBottom - (entity.getHigh() - upMinData)
// * upDataRatio;
// float lowY = upChartBottom - (entity.getLow() - upMinData)
// * upDataRatio;

float endX = getStrokeWidth() + getStrokeLeft()
+ (dataSpacing * (index + 1) - dataSpacing * 0.25f);

if (point.x > startX && point.x < endX) {
return i;
}
startX = getStrokeWidth() + getStrokeLeft() + dataSpacing
* (index + 1);
index++;
}
return -1;
}


/**
* 获取字体大小(像素单位)
*
*
@return
*/
public float getTextSize() {
return textSize;
}

/**
* 设置字体大小(像素单位)
*
*
@param textSize
*/
public void setTextSize(float textSize) {
this.textSize = textSize;
}

/**
* 获取上表与下表间距
*
*
@return
*/
public float getSpacing() {
return spacing;
}

/**
* 设置上表与下表间距
*
*
@param spacing
*/
public void setSpacing(float spacing) {
if (spacing < MyUtils.getInstance().dp2px(getContext(),
DEFAULT_TEXT_SIZE)) {
spacing = MyUtils.getInstance().dp2px(getContext(),
DEFAULT_TEXT_SIZE) * 2;
} else {
this.spacing = spacing;
}
}

/**
* 获取上表底部与控件顶部距离(是负值)
*
*
@return
*/
public float getUpChartBottom() {
return upChartBottom;
}

/**
* 获取
*
*
@return
*/
public float getUpChartHeight() {
return Math.abs(upChartHeight);
}

/**
* 获取下表高度
*
*
@return
*/
public float getDownChartHeight() {
return downChartHeight;
}

/**
* 设置数据
*
*
@param data
*/
public synchronized void setData(Vector<KLineData> data, IndexType type, MainIndexType mainIndexType) {

if (this.data == null) {
if (data == null || data.size() < candleNum) {
position = 0;
} else {
position = data.size() - candleNum;
}
} else {
if (data == null || data.size() < candleNum) {
position = 0;
} else if (position > data.size() - candleNum) {
position = data.size() - candleNum;
} else if (position < 0) {
position = 0;
} else if (isLoadMore) {
position += oldSize;
oldSize = 0;
if (position > data.size() - candleNum) {
position = data.size() - candleNum;
}
}
}

notifyRunnable.setData(data, type, mainIndexType);
executorService.execute(notifyRunnable);
}

/**
* 设置时间显示样式(true:显示yyyy/MM/dd HH:mm;false:显示yyyy/MM/dd)
*
*
@param isShowTime
*/

public void setShowTime(boolean isShowTime) {
this.isShowTime = isShowTime;
}

/**
* 清除K线
*/
public void clean() {
this.data = null;
invalidate();
}

/**
* 更新数据、指标的线程
*
*
@author dingrui
*/
class NotifyRunnable implements Runnable {

private Vector<KLineData> dataOld;
private IndexType type;
private MainIndexType mainIndexType;

public NotifyRunnable() {
dataOld = null;
type = IndexType.VOL;
}

public void setData(Vector<KLineData> data, IndexType type, MainIndexType mainIndexType) {
this.dataOld = data;
this.type = type;
this.mainIndexType = mainIndexType;
}

public IndexType getType() {
return type;
}

@Override
public void run() {
// TODO Auto-generated method stub
//加入同步锁
synchronized (this) {
if (null == dataOld) {
return;
}
Vector<KLineData> data = new Vector<>();
for (int i = 0; i < dataOld.size(); i++) {
KLineData oldItem = dataOld.get(i);
KLineData item = new KLineData();
item.setType(oldItem.getType());
item.setTime(oldItem.getTime());
item.setClose(oldItem.getClose());
item.setOpen(oldItem.getOpen());
item.setLowPrice(oldItem.getLowPrice());
item.setHighPrice(oldItem.getHighPrice());
item.setCjl(oldItem.getCjl());
item.setCje(oldItem.getCje());
data.add(item);
}
if (mainIndexType == MainIndexType.BOLL) {
data = IndexCalculation.calculationBOLL(data);
}
if (type == IndexType.VOL) {
data = IndexCalculation.calculationVol(data);
} else if (type == IndexType.MACD) {
// 计算MACD
data = IndexCalculation.calculationMACD(data);
}/* else if (type == IndexType.BOLL) {
// 计算布林指标
data = IndexCalculation.calculationBOLL(data);
data = IndexCalculation.calculationVol(data);
}*/
else if (type == IndexType.KDJ || type == IndexType.KD) {
// 计算KDJ
data = IndexCalculation.calculationKDJ(data);
} else if (type == IndexType.RSI) {
// 计算RSI
data = IndexCalculation.calculationRSI(data);
} else if (type == IndexType.BIAS) {
// 计算BIAS
data = IndexCalculation.calculationBIAS(data);
} else if (type == IndexType.BRAR) {
// 计算BRAR
data = IndexCalculation.calculationBRAR(data);
} else if (type == IndexType.CCI) {
// 计算CCI
data = IndexCalculation.calculationCCI(data);
} else if (type == IndexType.DMI) {
// 计算DMI
data = IndexCalculation.calculationDMI(data);
} else if (type == IndexType.CR) {
// 计算CR
data = IndexCalculation.calculationCR(data);
} else if (type == IndexType.PSY) {
// 计算PSY
data = IndexCalculation.calculationPSY(data);
} else if (type == IndexType.DMA) {
// 计算DMA
data = IndexCalculation.calculationDMA(data);
} else if (type == IndexType.TRIX) {
// 计算TRIX
data = IndexCalculation.calculationTRIX(data);
}

Bundle bundle = new Bundle();
bundle.putSerializable("data", data);
if (mainIndexType == MainIndexType.MA) {
bundle.putInt("mainindex", 0);
} else if (mainIndexType == MainIndexType.BOLL) {
bundle.putInt("mainindex", 1);
}
Message msg = handler.obtainMessage();
msg.what = 0x1;
msg.setData(bundle);
msg.obj = type;
handler.sendMessage(msg);
}
}
}

public IndexType getIndexType() {
return notifyRunnable.getType();
}

/**
* 是否设置了将坐标绘制在框外
*
*
@return
*/
public boolean isDrawOutside() {
return isDrawOutside;
}

/**
* 设置是否将两边坐标值绘制在边框线外(注:如果设置成true,必须设置strokeLeft和strokeRight)
*
*
@param strokeLeft 左边距
*
@param strokeRight 右边距
*/
public void setDrawOutside(boolean isDrawOutside, float strokeLeft,
float strokeRight) {
this.isDrawOutside = isDrawOutside;
if (isDrawOutside == false) {
setStrokeLeft(1.0f);
setStrokeRight(1.0f);
} else {
setStrokeLeft(strokeLeft);
setStrokeRight(strokeRight);
}
invalidate();
}

/**
* 设置当前显示k线数量
*
*
@param candleNum
*/
public void setCandleNum(int candleNum) {
this.candleNum = candleNum;
if (data == null || data.size() < candleNum) {
position = 0;
} else {
position = data.size() - candleNum;
}
}

/**
* 设置当前显示K线数量上限
*
*
@param maxCandleNum
*/
public void setMaxCandleNum(int maxCandleNum) {
this.maxCandleNum = maxCandleNum;
}

/**
* 设置当前显示K线数量下限
*
*
@param minCandleNum
*/
public void setMinCandleNum(int minCandleNum) {
this.minCandleNum = minCandleNum;
}

/**
* 加载完成并开启加载更多接口
*
*
@param oldDataSize 加载更多后的数据长度
*/
public void loadMoreFinish(int oldDataSize) {
isLoadMore = true;
oldSize = oldDataSize;
}

/**
* 小数保留位数
*
*
@param num
*/
public void setNum(int num) {
this.num = num;
invalidate();
}

/**
* 设置接口
*
*
@param l
*/
public void setKLineListener(KLineListener l) {
this.l = l;
}

/**
* 接口
*
*
@author dingrui
*/
public interface KLineListener {

public void transferData(View view, boolean isMoved, float touchX,
int[] upColors, int[] downColors, Vector<KLineData> data,
int position, IndexType mIndexType, int num);
}

/**
* 设置点击事件接口
*
*
@param l
*/
public void setOnKLineChartClickListener(OnKLineChartClickListener l) {
this.clickL = l;
}

/**
* 单击事件接口
*
*
@author dingrui
*/
public interface OnKLineChartClickListener {
public void click(View v);
}

/**
* 设置加载更多接口
*
*
@param onLoadMoreListener
*/
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
this.onLoadMoreListener = onLoadMoreListener;
}

/**
* 加载更多接口
*/
public interface OnLoadMoreListener {
public void onLoadMore();
}
}
package com.kedll.kedelllibrary.stock;

import java.io.Serializable;

public class KLineData implements Serializable {
    /**
     * 区分交易品种
     */
    private int type;

    /**
     * 是否绘制时间和经线
     */
    private boolean isDrawLongitude;

    /**
     * 时间
     */
    private long time;
    /**
     * 开盘价
     */
    private double open;
    /**
     * 收盘价
     */
    private double close;
    /**
     * 最高价
     */
    private double highPrice;
    /**
     * 最低价
     */
    private double lowPrice;
    /**
     * 成交额原始数据(非累加)
     */
    private double cje;
    /**
     * 成交量原始数据(非累加)
     */
    private double cjl;
    /**
     * 涨跌
     */
    private double change;
    /**
     * 涨跌幅
     */
    private double changep;
    /**
     * 均线
     */
    private DayMovingAverage dayMovingAverage;

    /** 指标相关 */
    /**
     * 成交量
     */
    private Volume volume;
    /**
     * 指数平滑异同平均线(MACD指标)
     */
    private MACD macd;
    /**
     * 动向指标
     */
    private DMI dmi;
    /**
     * 威廉指标
     */
    private WR wr;
    /**
     * 布林线
     */
    private BOLL boll;
    /**
     * 随机指标(KDJ)
     */
    private KDJ kdj;
    /**
     * OBV指标(统计成交量变动的趋势来推测股价趋势)
     */
    private OBV obv;
    /**
     * 强弱指标
     */
    private RSI rsi;
    /**
     * 停损转向操作点指标
     */
    private SAR sar;
    /**
     * 乖离率(BIAS)是测量股价偏离均线大小程度的指标
     */
    private BIAS bias;
    /**
     * 情绪指标(BRAR)也称为人气意愿指标
     */
    private BRAR brar;
    /**
     * 顺势指标
     */
    private CCI cci;
    /**
     * 能量指标
     */
    private CR cr;
    /**
     * 心理线(PSY)指标是研究投资者对股市涨跌产生心理波动的情绪指标
     */
    private PSY psy;
    /**
     * 平行线差指标
     */
    private DMA dma;
    /**
     * 三重指数平滑平均线(TRIX)属于长线指标
     */
    private TRIX trix;

    public KLineData() {

    }

    /**
     * 构造函数
     *
     * @param time      时间
     * @param open      开盘价
     * @param close     收盘价
     * @param highPrice 最高价
     * @param lowPrice  最低价
     * @param cje       成交额
     * @param cjl       成交量
     */
    public KLineData(long time, double open, double close, double highPrice,
                     double lowPrice, double cje, double cjl) {
        this.time = time;
        this.open = open;
        this.close = close;
        this.highPrice = highPrice;
        this.lowPrice = lowPrice;
        this.cje = cje;
        this.cjl = cjl;
    }

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    public boolean isDrawLongitude() {
        return isDrawLongitude;
    }

    public void setDrawLongitude(boolean isDrawLongitude) {
        this.isDrawLongitude = isDrawLongitude;
    }

    public long getTime() {
        return time;
    }

    public void setTime(long time) {
        this.time = time;
    }

    public double getOpen() {
        return open;
    }

    public void setOpen(double open) {
        this.open = open;
    }

    public double getClose() {
        return close;
    }

    public void setClose(double close) {
        this.close = close;
    }

    public double getHighPrice() {
        return highPrice;
    }

    public void setHighPrice(double highPrice) {
        this.highPrice = highPrice;
    }

    public double getLowPrice() {
        return lowPrice;
    }

    public void setLowPrice(double lowPrice) {
        this.lowPrice = lowPrice;
    }

    public double getCje() {
        return cje;
    }

    public void setCje(double cje) {
        this.cje = cje;
    }

    public double getCjl() {
        return cjl;
    }

    public void setCjl(double cjl) {
        this.cjl = cjl;
    }

    public double getChange() {
        return change;
    }

    public void setChange(double change) {
        this.change = change;
    }

    public double getChangep() {
        return changep;
    }

    public void setChangep(double changep) {
        this.changep = changep;
    }

    public DayMovingAverage getDayMovingAverage() {
        return dayMovingAverage == null ? new DayMovingAverage(0, 0, 0)
                                        : dayMovingAverage;
    }

    public void setDayMovingAverage(DayMovingAverage dayMovingAverage) {
        this.dayMovingAverage = dayMovingAverage;
    }

    public Volume getVolume() {
        return volume == null ? new Volume(0, 0, 0, 0) : volume;
    }

    public void setVolume(Volume volume) {
        this.volume = volume;
    }

    public MACD getMacd() {
        return macd;
    }

    public void setMacd(MACD macd) {
        this.macd = macd;
    }

    public DMI getDmi() {
        return dmi;
    }

    public void setDmi(DMI dmi) {
        this.dmi = dmi;
    }

    public WR getWr() {
        return wr;
    }

    public void setWr(WR wr) {
        this.wr = wr;
    }

    public BOLL getBoll() {
        return boll;
    }

    public void setBoll(BOLL boll) {
        this.boll = boll;
    }

    public KDJ getKdj() {
        return kdj;
    }

    public void setKdj(KDJ kdj) {
        this.kdj = kdj;
    }

    public OBV getObv() {
        return obv;
    }

    public void setObv(OBV obv) {
        this.obv = obv;
    }

    public RSI getRsi() {
        return rsi;
    }

    public void setRsi(RSI rsi) {
        this.rsi = rsi;
    }

    public SAR getSar() {
        return sar;
    }

    public void setSar(SAR sar) {
        this.sar = sar;
    }

    public BIAS getBias() {
        return bias;
    }

    public void setBias(BIAS bias) {
        this.bias = bias;
    }

    public BRAR getBrar() {
        return brar;
    }

    public void setBrar(BRAR brar) {
        this.brar = brar;
    }

    public CCI getCci() {
        return cci;
    }

    public void setCci(CCI cci) {
        this.cci = cci;
    }

    public CR getCr() {
        return cr;
    }

    public void setCr(CR cr) {
        this.cr = cr;
    }

    public PSY getPsy() {
        return psy;
    }

    public void setPsy(PSY psy) {
        this.psy = psy;
    }

    public DMA getDma() {
        return dma;
    }

    public void setDma(DMA dma) {
        this.dma = dma;
    }

    public TRIX getTrix() {
        return trix;
    }

    public void setTrix(TRIX trix) {
        this.trix = trix;
    }

    /**
     * 均线
     *
     * @author dingrui
     */
    public static class DayMovingAverage implements Serializable {
        /**
         * 5日均线
         */
        private double ma5;
        /**
         * 10日均线
         */
        private double ma10;
        /**
         * 20日均线
         */
        private double ma20;

        public DayMovingAverage() {

        }

        public DayMovingAverage(double ma5, double ma10, double ma20) {
            this.ma5 = ma5;
            this.ma10 = ma10;
            this.ma20 = ma20;
        }

        public double getMa5() {
            return ma5;
        }

        public void setMa5(double ma5) {
            this.ma5 = ma5;
        }

        public double getMa10() {
            return ma10;
        }

        public void setMa10(double ma10) {
            this.ma10 = ma10;
        }

        public double getMa20() {
            return ma20;
        }

        public void setMa20(double ma20) {
            this.ma20 = ma20;
        }

    }

    /**
     * 成交量指标
     *
     * @author dingrui
     */
    public static class Volume implements Serializable {
        /**
         * 成交量
         */
        private double num;
        /**
         * 均线
         */
        private double ma5;
        private double ma10;
        private double ma20;

        public Volume(double num, double ma5, double ma10, double ma20) {
            this.num = num;
            this.ma5 = ma5;
            this.ma10 = ma10;
            this.ma20 = ma20;
        }

        public double getNum() {
            return num;
        }

        public void setNum(double num) {
            this.num = num;
        }

        public double getMa5() {
            return ma5;
        }

        public void setMa5(double ma5) {
            this.ma5 = ma5;
        }

        public double getMa10() {
            return ma10;
        }

        public void setMa10(double ma10) {
            this.ma10 = ma10;
        }

        public double getMa20() {
            return ma20;
        }

        public void setMa20(double ma20) {
            this.ma20 = ma20;
        }

    }

    /**
     * 指数平滑异同平均线(MACD指标)
     *
     * @author dingrui
     */
    public static class MACD implements Serializable {
        private double diff;
        private double dea;
        private double macd;

        public MACD(double diff, double dea, double macd) {
            this.diff = diff;
            this.dea = dea;
            this.macd = macd;
        }

        public double getDiff() {
            return diff;
        }

        public void setDiff(double diff) {
            this.diff = diff;
        }

        public double getDea() {
            return dea;
        }

        public void setDea(double dea) {
            this.dea = dea;
        }

        public double getMacd() {
            return macd;
        }

        public void setMacd(double macd) {
            this.macd = macd;
        }
    }

    /**
     * 动向指标
     *
     * @author dingrui
     */
    public static class DMI implements Serializable {
        private double pdi;
        private double mdi;
        private double adx;
        private double adxr;

        public DMI(double pdi, double mdi, double adx, double adxr) {
            this.pdi = pdi;
            this.mdi = mdi;
            this.adx = adx;
            this.adxr = adxr;
        }

        public double getPdi() {
            return pdi;
        }

        public void setPdi(double pdi) {
            this.pdi = pdi;
        }

        public double getMdi() {
            return mdi;
        }

        public void setMdi(double mdi) {
            this.mdi = mdi;
        }

        public double getAdx() {
            return adx;
        }

        public void setAdx(double adx) {
            this.adx = adx;
        }

        public double getAdxr() {
            return adxr;
        }

        public void setAdxr(double adxr) {
            this.adxr = adxr;
        }
    }

    /**
     * 威廉指标
     *
     * @author dingrui
     */
    public class WR implements Serializable {
        private double wr1;
        private double wr2;

        public WR(double wr1, double wr2) {
            this.wr1 = wr1;
            this.wr2 = wr2;
        }

        public double getWr1() {
            return wr1;
        }

        public void setWr1(double wr1) {
            this.wr1 = wr1;
        }

        public double getWr2() {
            return wr2;
        }

        public void setWr2(double wr2) {
            this.wr2 = wr2;
        }
    }

    /**
     * 布林线
     *
     * @author dingrui
     */
    public static class BOLL implements Serializable {
        private double upper;
        private double mID;
        private double lower;

        public BOLL(double upper, double mID, double lower) {
            this.upper = upper;
            this.mID = mID;
            this.lower = lower;
        }

        public double getUpper() {
            return upper;
        }

        public void setUpper(double upper) {
            this.upper = upper;
        }

        public double getmID() {
            return mID;
        }

        public void setmID(double mID) {
            this.mID = mID;
        }

        public double getLower() {
            return lower;
        }

        public void setLower(double lower) {
            this.lower = lower;
        }
    }

    /**
     * 随机指标(KDJ)
     *
     * @author dingrui
     */
    public static class KDJ implements Serializable {
        private double K;
        private double D;
        private double J;

        public KDJ(double K, double D, double J) {
            this.K = K;
            this.D = D;
            this.J = J;
        }

        public double getK() {
            return K;
        }

        public void setK(double k) {
            K = k;
        }

        public double getD() {
            return D;
        }

        public void setD(double d) {
            D = d;
        }

        public double getJ() {
            return J;
        }

        public void setJ(double j) {
            J = j;
        }
    }

    /**
     * OBV指标(统计成交量变动的趋势来推测股价趋势)
     *
     * @author dingrui
     */
    public class OBV implements Serializable {
        private double obv;

        public OBV(double obv) {
            this.obv = obv;
        }

        public double getObv() {
            return obv;
        }

        public void setObv(double obv) {
            this.obv = obv;
        }
    }

    /**
     * 强弱指标
     *
     * @author dingrui
     */
    public static class RSI implements Serializable {
        private double rsi6;
        private double rsi12;
        private double rsi24;

        public RSI(double rsi6, double rsi12, double rsi24) {
            this.rsi6 = rsi6;
            this.rsi12 = rsi12;
            this.rsi24 = rsi24;
        }

        public double getRsi6() {
            return rsi6;
        }

        public void setRsi6(double rsi6) {
            this.rsi6 = rsi6;
        }

        public double getRsi12() {
            return rsi12;
        }

        public void setRsi12(double rsi12) {
            this.rsi12 = rsi12;
        }

        public double getRsi24() {
            return rsi24;
        }

        public void setRsi24(double rsi24) {
            this.rsi24 = rsi24;
        }
    }

    /**
     * 停损转向操作点指标
     *
     * @author dingrui
     */
    public class SAR implements Serializable {
        private double sar;

        public SAR(double sar) {
            this.sar = sar;
        }

        public double getSar() {
            return sar;
        }

        public void setSar(double sar) {
            this.sar = sar;
        }
    }

    /**
     * 乖离率(BIAS)是测量股价偏离均线大小程度的指标
     *
     * @author dingrui
     */
    public static class BIAS implements Serializable {
        private double bias1;// 5
        private double bias2;// 10
        private double bias3;// 20

        public BIAS(double bias1, double bias2, double bias3) {
            this.bias1 = bias1;
            this.bias2 = bias2;
            this.bias3 = bias3;
        }

        public double getBias1() {
            return bias1;
        }

        public void setBias1(double bias1) {
            this.bias1 = bias1;
        }

        public double getBias2() {
            return bias2;
        }

        public void setBias2(double bias2) {
            this.bias2 = bias2;
        }

        public double getBias3() {
            return bias3;
        }

        public void setBias3(double bias3) {
            this.bias3 = bias3;
        }

    }

    /**
     * 情绪指标(BRAR)也称为人气意愿指标
     *
     * @author dingrui
     */
    public static class BRAR implements Serializable {
        private double br;
        private double ar;

        public BRAR(double br, double ar) {
            this.br = br;
            this.ar = ar;
        }

        public double getBr() {
            return br;
        }

        public void setBr(double br) {
            this.br = br;
        }

        public double getAr() {
            return ar;
        }

        public void setAr(double ar) {
            this.ar = ar;
        }

    }

    /**
     * 顺势指标
     *
     * @author dingrui
     */
    public static class CCI implements Serializable {
        private double cci;

        public CCI(double cci) {
            this.cci = cci;
        }

        public double getCci() {
            return cci;
        }

        public void setCci(double cci) {
            this.cci = cci;
        }
    }

    /**
     * 能量指标
     *
     * @author dingrui
     */
    public static class CR implements Serializable {
        private double cr;
        private double ma1;
        private double ma2;
        private double ma3;

        public CR(double cr, double ma1, double ma2, double ma3) {
            this.cr = cr;
            this.ma1 = ma1;
            this.ma2 = ma2;
            this.ma3 = ma3;
        }

        public double getCr() {
            return cr;
        }

        public void setCr(double cr) {
            this.cr = cr;
        }

        public double getMa1() {
            return ma1;
        }

        public void setMa1(double ma1) {
            this.ma1 = ma1;
        }

        public double getMa2() {
            return ma2;
        }

        public void setMa2(double ma2) {
            this.ma2 = ma2;
        }

        public double getMa3() {
            return ma3;
        }

        public void setMa3(double ma3) {
            this.ma3 = ma3;
        }

    }

    /**
     * 心理线(PSY)指标是研究投资者对股市涨跌产生心理波动的情绪指标
     *
     * @author dingrui
     */
    public static class PSY implements Serializable {
        private double psy;

        public PSY(double psy) {
            this.psy = psy;
        }

        public double getPsy() {
            return psy;
        }

        public void setPsy(double psy) {
            this.psy = psy;
        }
    }

    /**
     * 平行线差指标
     *
     * @author dingrui
     */
    public static class DMA implements Serializable {
        private double dif;
        private double ama;

        public DMA(double dif, double ama) {
            this.dif = dif;
            this.ama = ama;
        }

        public double getDif() {
            return dif;
        }

        public void setDif(double dif) {
            this.dif = dif;
        }

        public double getAma() {
            return ama;
        }

        public void setAma(double ama) {
            this.ama = ama;
        }
    }

    /**
     * 三重指数平滑平均线(TRIX)属于长线指标
     *
     * @author dingrui
     */
    public static class TRIX implements Serializable {
        private double trix;
        private double maTrix;

        public TRIX(double trix, double maTrix) {
            this.trix = trix;
            this.maTrix = maTrix;
        }

        public double getTrix() {
            return trix;
        }

        public void setTrix(double trix) {
            this.trix = trix;
        }

        public double getMaTrix() {
            return maTrix;
        }

        public void setMaTrix(double maTrix) {
            this.maTrix = maTrix;
        }
    }
}
作者 east
Android 12月 8,2020

Android绘制股票k线图系列2-绘制分时图



import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.PathEffect;
import android.graphics.Shader;
import android.graphics.Typeface;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;



import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TimeChartView extends GridChartView {

    /**
     * 分时数据
     */
    private Vector<TimeData> data;
    /**
     * 时间段数据
     */
    private List<TRADETIME> tradetimes;
    /**
     * 昨收价
     */
    private float preClose;

    /**
     * 记录列表中设置到上表中的数据的最大值
     */
    private float upMaxData;
    /**
     * 记录列表中设置到上表中的数据的最小值
     */
    private float upMinData;
    /**
     * 记录列表中设置到下表中的数据最大值
     */
    private float downMaxData;
    /**
     * 记录列表中设置到下表中的数据最小值
     */
    private float downMinData;
    /**
     * 上表与下表中间的日期时间文字的默认大小
     */
    protected final float DEFAULT_textSize = 9;
    /**
     * 文字大小
     */
    protected float textSize;
    /**
     * 上表与下表间距
     */
    protected float spacing;

    /**
     * 默认经线条数
     */
    private final int DEFAULT_LONGITUDE_NUM = 3;
    /**
     * 默认经线类型(24小时)
     */
    private final int DEFAULT_THEME = LongitudesTheme.theme0;
    /**
     * 默认上表纬线条数
     */
    private final int DEFAULT_LATITUDE_UP_NUM = 4;
    /**
     * 默认下表纬线条数
     */
    private final int DEFAULT_LATITUDE_DOWN_NUM = 2;
    /**
     * 默认经线颜色
     */
    private final int DEFAULT_LONGITUDE_COLOR = 0xffb7b7b7;

    /**
     * 默认纬线颜色
     */
    private final int MIDDLE_LONGITUDE_LATITUDE_COLOR = 0xffff4700;
    /**
     * 中心的经纬线颜色
     */
    private final int DEFAULT_LATITUDE_COLOR = 0xffeeeeee;
    /**
     * 默认经、纬线宽
     */
    private final float DEFAULT_LATITUDE_LONGITUDE_WIDTH = 0.8f;


    /**
     * 经线间距
     */
    protected float longitudesSpacing;
    /**
     * 纬线间距
     */
    protected float latitudesSpacing;
    /**
     * 经线条数
     */
    private int longitudesNum = DEFAULT_LONGITUDE_NUM;
    /**
     * 经线类型
     */
    private int longitudesTheme = DEFAULT_THEME;
    /**
     * 上表纬线条数
     */
    protected int latitudesUpNum = DEFAULT_LATITUDE_UP_NUM;
    /**
     * 下表纬线条数
     */
    protected int latitudesDownNum = DEFAULT_LATITUDE_DOWN_NUM;
    /**
     * 经线颜色
     */
    private int longitudesColor = DEFAULT_LONGITUDE_COLOR;
    private int midLongitudesColor = 0xffff4700;
    /**
     * 纬线颜色
     */
    private int latitudesColor = DEFAULT_LATITUDE_COLOR;
    /**
     * 经、纬线宽
     */
    private float latLongWidth = DEFAULT_LATITUDE_LONGITUDE_WIDTH;
    /**
     * 是否绘制经线
     */
    private boolean isDrawLongitudes;
    /**
     * 是否绘制纬线
     */
    private boolean isDrawLatitudes;

    /**
     * 上表底部
     */
    protected float upChartBottom;
    /**
     * 下表底部
     */
    protected float downChartBottom;
    /**
     * 上表高度
     */
    protected float upChartHeight;
    /**
     * 下表高度
     */
    protected float downChartHeight;

    /**
     * 分时默认最大时间
     */
    private final int MAX_DATE = 60 * 4 + 1;
    /**
     * 分时最大时间(默认24个小时)
     */
    private int maxDate = MAX_DATE;

    /**
     * 各个数据点的间距
     */
    private float dataSpacing;

    /**
     * 上表的尺寸与数据比例
     */
    private float upRatio;
    /**
     * 下表的尺寸与数据比例
     */
    private float downRatio;
    /**
     * 下表柱形图宽
     */
    private float columnWidth;

    /**
     * 是否显示十字光标
     */
    private boolean isShowCross;
    /**
     * 十字光标X坐标
     */
    private float crossX;
    /**
     * 十字光标Y坐标
     */
    private float crossY;

    /**
     * 长按事件的Runnable
     */
    private Runnable mRunnable;
    /**
     * 手指是否离开屏幕
     */
    private boolean isReleased;
    /**
     * 手指是否移动了
     */
    private boolean isMoved;
    /**
     * 是否可以执行OnTouch中的Move事件了
     */
    private boolean isStartMoved;
    /**
     * 按下时的X坐标
     */
    private float touchDownX;
    /**
     * 按下时的Y坐标
     */
    private float touchDownY;
    /**
     * 触摸点 X
     */
    private float movedX;
    /**
     * 触摸点 Y
     */
    private float movedY;

    private float moveRawX;// 触摸中的X坐标
    private float moveRawY;// 触摸中的Y坐标
    /**
     * 设置是否将两边坐标值绘制在边框线外(注:如果设置成true,必须设置strokeLeft和strokeRight)
     */
    private boolean isDrawOutside;

    /**
     * 传递数据的接口
     */
    private OnTimeListener l;
    /**
     * 单击事件接口
     */
    private OnTimeChartClickListener mOnTimeChartClickListener;

    /**
     * 左边要显示当前十字光标中心点的数据
     */
    private float leftData;
    /**
     * 右边要显示当前十字光标中心点的数据
     */
    private float rightData;
    /**
     * 底部要显示当前十字光标中心点点数据
     */
    private float bottomData;
    /**
     * 中间时间
     */
    private long time;

    ExecutorService executorService = Executors.newCachedThreadPool();

    /**
     * 小数保留位数
     */
    private int num = 2;// 默认保留两位

    /**
     * 经线类型
     *
     * @author dingrui
     */
    public static class LongitudesTheme {
        public static final int theme0 = 0;// 9:30-11:30(2h) 13:00-15:00(2h)
        public static final int theme1 = 1;// 6:00-次日6:00(24h)
        public static final int theme2 = 2;// 昨日20:00-2:30(6.5h),9:00-11:30(2.5h),13:30-15:30(2h)
        public static final int theme3 = 3;// 8:00-次日2:00(18h)
        public static final int theme4 = 4;// 9:00-10:15(1.5h),10:30-11:30(1h),13:30-15:00(1.5h),21:00-次日2:30(5.5h)
        public static final int theme5 = 5;// 9:00-10:15(1.25h),10:30-11:30(1h),13:30-15:00(1.5h)
        public static final int theme6 = 6;// 9:00-10:15(1.25h),10:30-11:30(1h),13:30-15:00(1.5h),21:00-23:30(2.5h)
        public static final int theme7 = 7;// 9:00-10:15(1.25h),10:30-11:30(1h),13:30-15:00(1.5h),21:00-23:00(2h)
        public static final int theme8 = 8;// 9:00-10:15(1.25h),10:30-11:30(1h),13:30-15:00(1.5h),21:00-次日1:00(4h)
    }



    private boolean isDrawDownChart = true; //是否画下表

    // private TimeListThread mTimeListThread;

    Handler handler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
                case 0x1:
                    postInvalidate();
                    break;
                case 0x2:
                    Bundle bundle = msg.getData();
                    data = (Vector<TimeData>) bundle.getSerializable("data");
                    tradetimes = (List<TRADETIME>) bundle.getSerializable("tradetimes");
                    upMaxData = bundle.getFloat("upMaxData");
                    upMinData = bundle.getFloat("upMinData");
                    downMinData = bundle.getFloat("downMinData");
                    downMaxData = bundle.getFloat("downMaxData");
                    preClose = bundle.getFloat("preClose");
                    if (!isShowCross)
                        postInvalidate();
                    break;
                default:
                    break;
            }
        }
    };

    public TimeChartView(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
        init();
    }

    public TimeChartView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
        init();
    }

    public TimeChartView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // TODO Auto-generated constructor stub
        init();
    }

    /**
     * 初始化
     */
    private void init() {
        upMaxData = 0;
        downMaxData = 0;
        isShowCross = false;
        textSize = MyUtils.getInstance().dp2px(getContext(), DEFAULT_textSize);
        spacing = textSize * 2;// 比文字高度高一半
        isDrawLongitudes = true;
        isDrawLatitudes = true;
        isDrawOutside = false;

        mRunnable = new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                if (isMoved || isReleased)
                    return;
                isStartMoved = true;
            }
        };
    }


    @Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);
        float viewHight = getHeight();
        float viewWidth = getWidth();
        // 文字画笔
        TextPaint tp = new TextPaint();
        tp.setTextSize(textSize);
        //    tp.setTypeface(Typeface.DEFAULT_BOLD);
        int upLength = Parse.getInstance().parse2String(upMaxData, num)
                .length();
        int downLength = Parse.getInstance().parse2CNString(downMaxData)
                .length();
        int defaultLength = "0000.00".length();
        float dataWidth = 0;
        if (upLength > downLength) {
            if (defaultLength > upLength) {
                dataWidth = tp.measureText("0000.00");
            } else {
                dataWidth = tp.measureText(Parse.getInstance().parse2String(
                        upMaxData, num));
            }
        } else {
            if (defaultLength > downLength) {
                dataWidth = tp.measureText("0000.00");
            } else {
                dataWidth = tp.measureText(Parse.getInstance().parse2CNString(
                        downMaxData));
            }
        }
        if (spacing == 0.0f) {
            setStrokeBottom(textSize);
        }
        if(!isDrawDownChart) {
            latitudesDownNum = 0;
        }
        latitudesSpacing = (viewHight - getStrokeWidth() * 2 - spacing
                - getStrokeTop() - getStrokeBottom())
                / (latitudesUpNum + latitudesDownNum);

        upChartHeight = latitudesSpacing * latitudesUpNum;
        upChartBottom = latitudesSpacing * latitudesUpNum + getStrokeTop()
                + getStrokeWidth();

        downChartHeight = latitudesSpacing * latitudesDownNum;
        downChartBottom = viewHight - getStrokeBottom() - getStrokeWidth();
        columnWidth = (getWidth() - getStrokeWidth() * 2 - getStrokeLeft() - getStrokeRight())
                / maxDate;
        if (columnWidth > 2.0f) {
            columnWidth = 2.0f;
        }
        dataSpacing = (getWidth() - getStrokeWidth() * 2 - getStrokeLeft()
                - getStrokeRight() - 2.0f)
                / (maxDate - 1);

        if (upMaxData >= preClose && upMinData <= preClose) {
            if (upMaxData - preClose >= preClose - upMinData) {
                upMinData = preClose - (upMaxData - preClose);
            } else {
                upMaxData = preClose + (preClose - upMinData);
            }
        } else if (upMaxData >= preClose && upMinData >= preClose) {
            upMinData = preClose - (upMaxData - preClose);
        } else {
            upMaxData = preClose + (preClose - upMinData);
        }

        upRatio = upChartHeight / (upMaxData - upMinData);

        downRatio = downChartHeight / (downMaxData - downMinData);

        // 画笔
        Paint paint = new Paint();

        if (isDrawLatitudes) {
            // 绘制纬线
            drawLatitudes(canvas, paint);
        }
        if (isDrawLongitudes)
        // 绘制经线
        {
            if (tradetimes != null && tradetimes.size() > 0) {
                drawLongitudes1(canvas, paint);
            } else {
                drawLongitudes(canvas, paint);
            }
        }

        paint.reset();
        // 绘制上表折线
        drawUpLine(canvas, paint);
//        drawCubicUpLine(canvas, paint);

        // 绘制上表Y坐标上的X数据
        drawUpAxisXTitle(canvas, tp, dataWidth);

        if(isDrawDownChart) {
            // 绘制下表柱形图
            drawDownColumnChart(canvas, paint);

            // 绘制下表刻度
            drawDownAxisXTitle(canvas, tp, dataWidth);
        }

        // 绘制时间
        // drawTime(canvas, tp);

        // 绘制十字线
        drawCrossLine(canvas, paint);

    }

    /**
     * 绘制纬线
     *
     * @param canvas 画布
     * @param paint
     */
    private void drawLatitudes(Canvas canvas, Paint paint) {
        paint.reset();
        PathEffect pe = new DashPathEffect(new float[]{4, 4}, 1);
        paint.setColor(latitudesColor);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(latLongWidth);
        paint.setAntiAlias(true);
        Path path = new Path();
        this.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        for (int i = 1; i <= latitudesUpNum; i++) {
            if(i == (latitudesUpNum + 1) / 2){
                paint.setColor(MIDDLE_LONGITUDE_LATITUDE_COLOR);
                paint.setStrokeWidth(1);
            }else {
                paint.setColor(latitudesColor);
                paint.setStrokeWidth(latLongWidth);
            }
            //         paint1.setColor(MIDDLE_LONGITUDE_LATITUDE_COLOR);
            if (i == latitudesUpNum || i == latitudesUpNum / 2) {
                paint.setPathEffect(pe);

            }
            else {
                paint.setPathEffect(pe);
            }

            canvas.drawLine(getStrokeLeft(), latitudesSpacing * i
                            + getStrokeWidth() + getStrokeTop(), getWidth()
                            -
                            getStrokeRight(),
                    latitudesSpacing * i + getStrokeWidth()
                            + getStrokeTop(), paint);
        /*    path.moveTo(getStrokeLeft(), latitudesSpacing * i
                    + getStrokeWidth() + getStrokeTop());
            path.lineTo(getWidth()
                            -
                            getStrokeRight(),
                    latitudesSpacing * i + getStrokeWidth()
                            + getStrokeTop());
          */
        }
        if(isDrawDownChart) {
            for (int i = 1; i <= latitudesDownNum; i++) {
                if (i == latitudesDownNum)
                    paint.setPathEffect(null);
                else
                    paint.setPathEffect(pe);

                canvas.drawLine(getStrokeLeft(),
                        (getHeight() - getStrokeWidth() - getStrokeBottom())
                                - latitudesSpacing * i, getWidth()
                                - getStrokeRight(),
                        (getHeight() - getStrokeWidth() - getStrokeBottom())
                                - latitudesSpacing * i, paint);

            }
        }

    }

    /**
     * 绘制经线
     *
     * @param canvas 画布
     * @param paint
     */
    private void drawLongitudes(Canvas canvas, Paint paint) {
        Paint paint1 = new Paint();
        PathEffect pe = new DashPathEffect(new float[] { 4, 4 }, 1);
        TextPaint tp = new TextPaint();
        tp.setTextSize(textSize / 4 * 3);
        tp.setTypeface(Typeface.DEFAULT_BOLD);
        paint1.setColor(longitudesColor);
        paint1.setStyle(Paint.Style.STROKE);
        paint1.setStrokeWidth(latLongWidth);
        paint1.setAntiAlias(true);
        paint1.setPathEffect(pe);
        Path path1 = new Path();
        float width = getWidth() - getStrokeWidth() * 2 - getStrokeLeft()
                - getStrokeRight() - 2.0f;
        if (longitudesTheme == LongitudesTheme.theme0) {
            // 4H
            longitudesSpacing = width / (longitudesNum + 1);
            for (int i = 1; i <= longitudesNum; i++) {
          /*      canvas.drawLine(longitudesSpacing * i + getStrokeWidth()
                                + getStrokeLeft(), getStrokeWidth() + getStrokeTop(),
                        longitudesSpacing * i + getStrokeWidth()
                        + getStrokeLeft(), latitudesSpacing
                                           * latitudesUpNum + getStrokeWidth()
                                           + getStrokeTop(), paint); */
                if(i == latitudesUpNum / 2){
                    paint1.setColor(midLongitudesColor);
                }else{
                    paint1.setColor(longitudesColor);
                }
                path1.moveTo(longitudesSpacing * i + getStrokeWidth()
                        + getStrokeLeft(), getStrokeWidth() + getStrokeTop());
                path1.lineTo(longitudesSpacing * i + getStrokeWidth()
                        + getStrokeLeft(), latitudesSpacing
                        * latitudesUpNum + getStrokeWidth()
                        + getStrokeTop());

             /*   canvas.drawLine(longitudesSpacing * i + getStrokeWidth()
                                + getStrokeLeft(), getHeight() - getStrokeWidth()
                                - getStrokeBottom(), longitudesSpacing * i
                                + getStrokeWidth() +
                                getStrokeLeft(),
                        (getHeight()
                                - getStrokeWidth() - getStrokeBottom())
                                - latitudesSpacing * latitudesDownNum, paint); */
                path1.moveTo(longitudesSpacing * i + getStrokeWidth()
                        + getStrokeLeft(), getHeight() - getStrokeWidth()
                        - getStrokeBottom());
                path1.lineTo(longitudesSpacing * i
                                + getStrokeWidth() +
                                getStrokeLeft(),
                        (getHeight()
                                - getStrokeWidth() - getStrokeBottom())
                                - latitudesSpacing * latitudesDownNum);
            }
            canvas.drawPath(path1, paint1);
            ArrayList<String> date = new ArrayList<String>();// 大小必须比经线数多2
            date.add("09:30");
            date.add("10:30");
            date.add("11:30/13:00");
            date.add("14:00");
            date.add("15:00");
            drawTime(canvas, tp, date);
        } else if (longitudesTheme == LongitudesTheme.theme1) {
            // 24H
            longitudesSpacing = width / (longitudesNum + 1);
            for (int i = 1; i <= longitudesNum; i++) {
                // if (i == longitudesNum / 2 + 1)
                // paint.setPathEffect(null);
                // else
                // paint.setPathEffect(pe);
                canvas.drawLine(longitudesSpacing * i + getStrokeWidth()
                                + getStrokeLeft(), getStrokeWidth() + getStrokeTop(),
                        longitudesSpacing * i + getStrokeWidth()
                                + getStrokeLeft(), latitudesSpacing
                                * latitudesUpNum + getStrokeWidth()
                                + getStrokeTop(), paint);
                if(isDrawDownChart) {
                    canvas.drawLine(longitudesSpacing * i + getStrokeWidth()
                                    + getStrokeLeft(), getHeight() - getStrokeWidth()
                                    - getStrokeBottom(), longitudesSpacing * i
                                    + getStrokeWidth() +
                                    getStrokeLeft(),
                            (getHeight()
                                    - getStrokeWidth() - getStrokeBottom())
                                    - latitudesSpacing * latitudesDownNum, paint);
                }
            }
            ArrayList<String> date = new ArrayList<String>();// 大小必须比经线数多2
            date.add("06:00");
            date.add("12:00");
            date.add("18:00");
            date.add("00:00");
            date.add("06:00");
            drawTime(canvas, tp, date);
        } else if (longitudesTheme == LongitudesTheme.theme2) {
            // 昨日20:00-2:30(6.5h),9:00-11:30(2.5h),13:30-15:30(2h)
            float total = 6.5f + 2.5f + 2f;
            float no1 = width / total * 6.5f;
            float no2 = width / total * (6.5f + 2.5f);
            for (int i = 0; i < longitudesNum; i++) {
                float x;
                if (i == 0) {
                    x = no1;
                } else
                    x = no2;
                canvas.drawLine(getStrokeLeft() + getStrokeWidth() / 2 + x,
                        getStrokeWidth() + getStrokeTop(), getStrokeLeft()
                                + getStrokeWidth() / 2 + x,
                        latitudesSpacing
                                * latitudesUpNum + getStrokeWidth()
                                + getStrokeTop(), paint);
                if(isDrawDownChart) {
                    canvas.drawLine(getStrokeLeft() + getStrokeWidth() / 2 + x,
                            getHeight() - getStrokeWidth() - getStrokeBottom(),
                            getStrokeLeft() + getStrokeWidth() / 2 + x,
                            (getHeight() - getStrokeWidth() - getStrokeBottom())
                                    - latitudesSpacing * latitudesDownNum, paint);
                }
            }

            tp.setColor(getResources().getColor(R.color.text_color_5c5f66));
            tp.setAntiAlias(true);
            ArrayList<String> date = new ArrayList<String>();// 大小必须比经线数多2
            date.add("20:00");
            date.add("02:30/09:00");
            date.add("13:30");
            date.add("15:30");
            float Y = 0.0f;
            if (spacing != 0.0f) {
                Y = upChartBottom + textSize;
            } else {
                Y = getHeight();
            }
            for (int i = 0; i < date.size(); i++) {
                if (i == date.size() - 1) {
                    canvas.drawText(date.get(i),
                            getWidth() - getStrokeWidth() - getStrokeRight()
                                    - 2 - tp.measureText(date.get(i)), Y, tp);
                } else if (i == 0) {
                    canvas.drawText(date.get(i), getStrokeLeft()
                            + getStrokeWidth() + 2, Y, tp);
                } else {
                    float x;
                    if (i == 1) {
                        x = no1;
                    } else {
                        x = no2;
                    }
                    canvas.drawText(date.get(i), getStrokeWidth() / 2
                            + getStrokeLeft() + x - tp.measureText(date.get(i))
                            / 2, Y, tp);
                }
            }
        } else if (longitudesTheme == LongitudesTheme.theme3) {
            // 8:00-次日2:00(18h)
            longitudesSpacing = width / (longitudesNum + 1);
            for (int i = 1; i <= longitudesNum; i++) {
                canvas.drawLine(longitudesSpacing * i + getStrokeWidth()
                                + getStrokeLeft(), getStrokeWidth() + getStrokeTop(),
                        longitudesSpacing * i + getStrokeWidth()
                                + getStrokeLeft(), latitudesSpacing
                                * latitudesUpNum + getStrokeWidth()
                                + getStrokeTop(), paint);
                if(isDrawDownChart) {
                    canvas.drawLine(longitudesSpacing * i + getStrokeWidth()
                                    + getStrokeLeft(), getHeight() - getStrokeWidth()
                                    - getStrokeBottom(), longitudesSpacing * i
                                    + getStrokeWidth() +
                                    getStrokeLeft(),
                            (getHeight()
                                    - getStrokeWidth() - getStrokeBottom())
                                    - latitudesSpacing * latitudesDownNum, paint);
                }
            }
            ArrayList<String> date = new ArrayList<String>();// 大小必须比经线数多2
            date.add("08:00");
            date.add("14:00");
            date.add("20:00");
            date.add("02:00");
            drawTime(canvas, tp, date);
        } else if (longitudesTheme == LongitudesTheme.theme4) {
            // 9:00-10:15(1.5h),10:30-11:30(1h),13:30-15:00(1.5h),21:00-次日2:30(5.5h)
            float total = 1.5f + 1f + 1.5f + 5.5f;
            float no1 = width / total * 1.5f;
            float no2 = width / total * (1.5f + 1f);
            float no3 = width / total * (1.5f + 1f + 1.5f);
            for (int i = 0; i < longitudesNum; i++) {
                float x;
                if (i == 0) {
                    x = no1;
                } else if (i == 1)
                    x = no2;
                else
                    x = no3;
                canvas.drawLine(getStrokeLeft() + getStrokeWidth() / 2 + x,
                        getStrokeWidth() + getStrokeTop(), getStrokeLeft()
                                + getStrokeWidth() / 2 + x,
                        latitudesSpacing
                                * latitudesUpNum + getStrokeWidth()
                                + getStrokeTop(), paint);
                if(isDrawDownChart) {
                    canvas.drawLine(getStrokeLeft() + getStrokeWidth() / 2 + x,
                            getHeight() - getStrokeWidth() - getStrokeBottom(),
                            getStrokeLeft() + getStrokeWidth() / 2 + x,
                            (getHeight() - getStrokeWidth() - getStrokeBottom())
                                    - latitudesSpacing * latitudesDownNum, paint);
                }
            }

            tp.setColor(getResources().getColor(R.color.text_color_5c5f66));
            tp.setAntiAlias(true);
            ArrayList<String> date = new ArrayList<String>();// 大小必须比经线数多2
            date.add("09:00");
            date.add("10:30");
            date.add("13:30");
            date.add("21:00");
            date.add("02:00");
            float Y = 0.0f;
            if (spacing != 0.0f) {
                Y = upChartBottom + textSize;
            } else {
                Y = getHeight();
            }
            for (int i = 0; i < date.size(); i++) {
                if (i == date.size() - 1) {
                    canvas.drawText(date.get(i),
                            getWidth() - getStrokeWidth() - getStrokeRight()
                                    - 2 - tp.measureText(date.get(i)), Y, tp);
                } else if (i == 0) {
                    canvas.drawText(date.get(i), getStrokeLeft()
                            + getStrokeWidth() + 2, Y, tp);
                } else {
                    float x;
                    if (i == 1) {
                        x = no1;
                    } else if (i == 2) {
                        x = no2;
                    } else {
                        x = no3;
                    }
                    canvas.drawText(date.get(i), getStrokeWidth() / 2
                            + getStrokeLeft() + x - tp.measureText(date.get(i))
                            / 2, Y, tp);
                }
            }
        } else if (longitudesTheme == LongitudesTheme.theme5) {
            // 9:00-10:15(1.25h),10:30-11:30(1h),13:30-15:00(1.5h)
            float total = 1.25f + 1f + 1.5f;
            float no1 = width / total * 1.25f;
            float no2 = width / total * (1.25f + 1f);
            for (int i = 0; i < longitudesNum; i++) {
                float x;
                if (i == 0) {
                    x = no1;
                } else
                    x = no2;
                canvas.drawLine(getStrokeLeft() + getStrokeWidth() / 2 + x,
                        getStrokeWidth() + getStrokeTop(), getStrokeLeft()
                                + getStrokeWidth() / 2 + x,
                        latitudesSpacing
                                * latitudesUpNum + getStrokeWidth()
                                + getStrokeTop(), paint);
                if(isDrawDownChart) {
                    canvas.drawLine(getStrokeLeft() + getStrokeWidth() / 2 + x,
                            getHeight() - getStrokeWidth() - getStrokeBottom(),
                            getStrokeLeft() + getStrokeWidth() / 2 + x,
                            (getHeight() - getStrokeWidth() - getStrokeBottom())
                                    - latitudesSpacing * latitudesDownNum, paint);
                }
            }

            tp.setColor(getResources().getColor(R.color.text_color_5c5f66));
            tp.setAntiAlias(true);
            ArrayList<String> date = new ArrayList<String>();// 大小必须比经线数多2
            date.add("09:00");
            date.add("10:30");
            date.add("13:30");
            date.add("15:00");
            float Y = 0.0f;
            if (spacing != 0.0f) {
                Y = upChartBottom + textSize;
            } else {
                Y = getHeight();
            }
            for (int i = 0; i < date.size(); i++) {
                if (i == date.size() - 1) {
                    canvas.drawText(date.get(i),
                            getWidth() - getStrokeWidth() - getStrokeRight()
                                    - 2 - tp.measureText(date.get(i)), Y, tp);
                } else if (i == 0) {
                    canvas.drawText(date.get(i), getStrokeLeft()
                            + getStrokeWidth() + 2, Y, tp);
                } else {
                    float x;
                    if (i == 1) {
                        x = no1;
                    } else {
                        x = no2;
                    }
                    canvas.drawText(date.get(i), getStrokeWidth() / 2
                            + getStrokeLeft() + x - tp.measureText(date.get(i))
                            / 2, Y, tp);
                }
            }
        } else if (longitudesTheme == LongitudesTheme.theme6) {
            // 9:00-10:15(1.25h),10:30-11:30(1h),13:30-15:00(1.5h),21:00-23:30(2.5h)
            float total = 1.25f + 1f + 1.5f + 2.5f;
            float no1 = width / total * 1.25f;
            float no2 = width / total * (1.25f + 1f);
            float no3 = width / total * (1.25f + 1f + 1.5f);
            for (int i = 0; i < longitudesNum; i++) {
                float x;
                if (i == 0) {
                    x = no1;
                } else if (i == 1)
                    x = no2;
                else
                    x = no3;
                canvas.drawLine(getStrokeLeft() + getStrokeWidth() / 2 + x,
                        getStrokeWidth() + getStrokeTop(), getStrokeLeft()
                                + getStrokeWidth() / 2 + x,
                        latitudesSpacing
                                * latitudesUpNum + getStrokeWidth()
                                + getStrokeTop(), paint);
                if(isDrawDownChart) {
                    canvas.drawLine(getStrokeLeft() + getStrokeWidth() / 2 + x,
                            getHeight() - getStrokeWidth() - getStrokeBottom(),
                            getStrokeLeft() + getStrokeWidth() / 2 + x,
                            (getHeight() - getStrokeWidth() - getStrokeBottom())
                                    - latitudesSpacing * latitudesDownNum, paint);
                }
            }

            tp.setColor(getResources().getColor(R.color.text_color_5c5f66));
            tp.setAntiAlias(true);
            ArrayList<String> date = new ArrayList<String>();// 大小必须比经线数多2
            date.add("09:00");
            date.add("10:30");
            date.add("13:30");
            date.add("21:00");
            date.add("23:30");
            float Y = 0.0f;
            if (spacing != 0.0f) {
                Y = upChartBottom + textSize;
            } else {
                Y = getHeight();
            }
            for (int i = 0; i < date.size(); i++) {
                if (i == date.size() - 1) {
                    canvas.drawText(date.get(i),
                            getWidth() - getStrokeWidth() - getStrokeRight()
                                    - 2 - tp.measureText(date.get(i)), Y, tp);
                } else if (i == 0) {
                    canvas.drawText(date.get(i), getStrokeLeft()
                            + getStrokeWidth() + 2, Y, tp);
                } else {
                    float x;
                    if (i == 1) {
                        x = no1;
                    } else if (i == 2) {
                        x = no2;
                    } else
                        x = no3;
                    canvas.drawText(date.get(i), getStrokeWidth() / 2
                            + getStrokeLeft() + x - tp.measureText(date.get(i))
                            / 2, Y, tp);
                }
            }
        } else if (longitudesTheme == LongitudesTheme.theme7) {
            // 9:00-10:15(1.25h),10:30-11:30(1h),13:30-15:00(1.5h),21:00-23:00(2h)
            float total = 1.25f + 1f + 1.5f + 2f;
            float no1 = width / total * 1.25f;
            float no2 = width / total * (1.25f + 1f);
            float no3 = width / total * (1.25f + 1f + 1.5f);
            for (int i = 0; i < longitudesNum; i++) {
                float x;
                if (i == 0) {
                    x = no1;
                } else if (i == 1)
                    x = no2;
                else
                    x = no3;
                canvas.drawLine(getStrokeLeft() + getStrokeWidth() / 2 + x,
                        getStrokeWidth() + getStrokeTop(), getStrokeLeft()
                                + getStrokeWidth() / 2 + x,
                        latitudesSpacing
                                * latitudesUpNum + getStrokeWidth()
                                + getStrokeTop(), paint);
                if(isDrawDownChart) {
                    canvas.drawLine(getStrokeLeft() + getStrokeWidth() / 2 + x,
                            getHeight() - getStrokeWidth() - getStrokeBottom(),
                            getStrokeLeft() + getStrokeWidth() / 2 + x,
                            (getHeight() - getStrokeWidth() - getStrokeBottom())
                                    - latitudesSpacing * latitudesDownNum, paint);
                }
            }

            tp.setColor(getResources().getColor(R.color.text_color_5c5f66));
            tp.setAntiAlias(true);
            ArrayList<String> date = new ArrayList<String>();// 大小必须比经线数多2
            date.add("09:00");
            date.add("10:30");
            date.add("13:30");
            date.add("21:00");
            date.add("23:00");
            float Y = 0.0f;
            if (spacing != 0.0f) {
                Y = upChartBottom + textSize;
            } else {
                Y = getHeight();
            }
            for (int i = 0; i < date.size(); i++) {
                if (i == date.size() - 1) {
                    canvas.drawText(date.get(i),
                            getWidth() - getStrokeWidth() - getStrokeRight()
                                    - 2 - tp.measureText(date.get(i)), Y, tp);
                } else if (i == 0) {
                    canvas.drawText(date.get(i), getStrokeLeft()
                            + getStrokeWidth() + 2, Y, tp);
                } else {
                    float x;
                    if (i == 1) {
                        x = no1;
                    } else if (i == 2) {
                        x = no2;
                    } else
                        x = no3;
                    canvas.drawText(date.get(i), getStrokeWidth() / 2
                            + getStrokeLeft() + x - tp.measureText(date.get(i))
                            / 2, Y, tp);
                }
            }
        } else if (longitudesTheme == LongitudesTheme.theme8) {
            // 9:00-10:15(1.25h),10:30-11:30(1h),13:30-15:00(1.5h),21:00-次日1:00(4h)
            float total = 1.25f + 1f + 1.5f + 4f;
            float no1 = width / total * 1.25f;
            float no2 = width / total * (1.25f + 1f);
            float no3 = width / total * (1.25f + 1f + 1.5f);
            for (int i = 0; i < longitudesNum; i++) {
                float x;
                if (i == 0) {
                    x = no1;
                } else if (i == 1)
                    x = no2;
                else
                    x = no3;
                canvas.drawLine(getStrokeLeft() + getStrokeWidth() / 2 + x,
                        getStrokeWidth() + getStrokeTop(), getStrokeLeft()
                                + getStrokeWidth() / 2 + x,
                        latitudesSpacing
                                * latitudesUpNum + getStrokeWidth()
                                + getStrokeTop(), paint);
                if(isDrawDownChart) {
                    canvas.drawLine(getStrokeLeft() + getStrokeWidth() / 2 + x,
                            getHeight() - getStrokeWidth() - getStrokeBottom(),
                            getStrokeLeft() + getStrokeWidth() / 2 + x,
                            (getHeight() - getStrokeWidth() - getStrokeBottom())
                                    - latitudesSpacing * latitudesDownNum, paint);
                }
            }

            tp.setColor(getResources().getColor(R.color.text_color_5c5f66));
            tp.setAntiAlias(true);
            ArrayList<String> date = new ArrayList<String>();// 大小必须比经线数多2
            date.add("09:00");
            date.add("10:30");
            date.add("13:30");
            date.add("21:00");
            date.add("01:00");
            float Y = 0.0f;
            if (spacing != 0.0f) {
                Y = upChartBottom + textSize;
            } else {
                Y = getHeight();
            }
            for (int i = 0; i < date.size(); i++) {
                if (i == date.size() - 1) {
                    canvas.drawText(date.get(i),
                            getWidth() - getStrokeWidth() - getStrokeRight()
                                    - 2 - tp.measureText(date.get(i)), Y, tp);
                } else if (i == 0) {
                    canvas.drawText(date.get(i), getStrokeLeft()
                            + getStrokeWidth() + 2, Y, tp);
                } else {
                    float x;
                    if (i == 1) {
                        x = no1;
                    } else if (i == 2) {
                        x = no2;
                    } else
                        x = no3;
                    canvas.drawText(date.get(i), getStrokeWidth() / 2
                            + getStrokeLeft() + x - tp.measureText(date.get(i))
                            / 2, Y, tp);
                }
            }
        }
    }

    /**
     * 绘制经线
     *
     * @param canvas 画布
     * @param paint
     */
    private void drawLongitudes1(Canvas canvas, Paint paint) {
        PathEffect pe = new DashPathEffect(new float[] { 4, 4 }, 1);
        TextPaint tp = new TextPaint();
        tp.setTextSize(textSize / 2);
        tp.setTypeface(Typeface.DEFAULT_BOLD);
        paint.reset();
        paint.setColor(longitudesColor);
        paint.setStrokeWidth(latLongWidth);
        paint.setAntiAlias(true);
        paint.setPathEffect(pe);
        Path path = new Path();
        this.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        float width = getWidth() - getStrokeWidth() * 2 - getStrokeLeft()
                - getStrokeRight() - 2.0f;
        float total = 0.0f;
        ArrayList<String> date = new ArrayList<String>();// 大小必须比经线数多2
        for (int i = 0; i < tradetimes.size() + 1; i++) {
            if (i != tradetimes.size())
                total += tradetimes.get(i).getTimeSlot();
            if (i == 0 && i != tradetimes.size())
                date.add(MyUtils.getInstance().date2String("HH:mm", tradetimes.get(i).getOpenTimeMillis()));
            else if (i == tradetimes.size() && i != 0)
                date.add(MyUtils.getInstance().date2String("HH:mm", tradetimes.get(
                        i - 1).getEndTimeMillis()));
            else if (i != 0) {
                date.add(
                        MyUtils.getInstance().date2String("HH:mm", tradetimes.get(
                                i - 1).getEndTimeMillis()) + "/" +
                                MyUtils.getInstance().date2String("HH:mm", tradetimes.get(i).getOpenTimeMillis()));
            }

        }

        float index = 0.0f;
        for (int i = 0; i < tradetimes.size(); i++) {
            float x = width / total * index;
            if (i != 0) {
                if(i == (tradetimes.size() ) / 2){
                    //    paint.setColor(MIDDLE_LONGITUDE_LATITUDE_COLOR);
                    paint.setColor(longitudesColor);
                }else {
                    paint.setColor(longitudesColor);
                }
                canvas.drawLine(getStrokeLeft() + getStrokeWidth() / 2 + x,
                        getStrokeWidth() + getStrokeTop(), getStrokeLeft()
                                + getStrokeWidth() / 2 + x,
                        latitudesSpacing
                                * latitudesUpNum + getStrokeWidth()
                                + getStrokeTop(), paint);
                if(isDrawDownChart) {
                    canvas.drawLine(getStrokeLeft() + getStrokeWidth() / 2 + x,
                            getHeight() - getStrokeWidth() - getStrokeBottom(),
                            getStrokeLeft() + getStrokeWidth() / 2 + x,
                            (getHeight() - getStrokeWidth() - getStrokeBottom())
                                    - latitudesSpacing * latitudesDownNum, paint);
                }

            }
            index += tradetimes.get(i).getTimeSlot();

        }
        canvas.drawPath(path, paint);

        tp.setColor(getResources().getColor(R.color.text_color_5c5f66));
        tp.setAntiAlias(true);
        float Y = 0.0f;
        if (spacing != 0.0f) {
            Y = upChartBottom + textSize;
        } else {
            Y = getHeight();
        }
        index = 0.0f;
        for (int i = 0; i < date.size(); i++) {
            float x = width / total * index;
            if (i == date.size() - 1) {
                canvas.drawText(date.get(i),
                        getWidth() - getStrokeWidth() - getStrokeRight()
                                - 2 - tp.measureText(date.get(i)), Y, tp);
            } else if (i == 0) {
                canvas.drawText(date.get(i), getStrokeLeft()
                        + getStrokeWidth() + 2, Y, tp);
            } else {
                canvas.drawText(date.get(i), getStrokeWidth() / 2
                        + getStrokeLeft() + x - tp.measureText(date.get(i))
                        / 2, Y, tp);
            }
            if (i != date.size() - 1)
                index += tradetimes.get(i).getTimeSlot();
        }
    }

    /**
     * 绘制上表中的折线
     *
     * @param canvas
     */
    private void drawUpLine(Canvas canvas, Paint paint) {
        if (data == null || data.size() == 0)
            return;
        paint.setStrokeWidth(1);
        paint.setAntiAlias(true);
        float startWhiteX = getStrokeWidth() + getStrokeLeft() + 1f;
        float startWhiteY = (float) (upChartBottom - (data.get(0).getClose() - upMinData)
                * upRatio);
        paint.setColor(getContext().getResources().getColor(R.color.timechart_line_yellow));
      /*  */
        int dataSize = data.size();
        if (dataSize == 1) {
            canvas.drawCircle(startWhiteX, startWhiteY, 3, paint);
        } else {
            Path path = new Path();
            Path path1 = new Path();
            for (int i = 1; i < data.size() && i < maxDate; i++) {
                float endWhiteY = (float) (upChartBottom - (data.get(i)
                        .getClose() - upMinData) *
                        upRatio);
                float endWhiteX = getStrokeWidth() + getStrokeLeft() + 1f
                        + dataSpacing * i;
//                canvas.drawLine(startWhiteX, startWhiteY, endWhiteX, endWhiteY,
//                        paint);
                if (i == 1) {
                    path.moveTo(startWhiteX, startWhiteY);
                    path1.moveTo(startWhiteX, startWhiteY);
                } else {
                    path.lineTo(startWhiteX, startWhiteY);
                    path1.lineTo(startWhiteX, startWhiteY);
                }
                if (i == data.size() - 1 || i == maxDate - 1) {
                    path.lineTo(endWhiteX, endWhiteY);
                    path1.lineTo(endWhiteX, endWhiteY);
                    path1.lineTo(endWhiteX, upChartBottom);
                    path1.lineTo(getStrokeWidth() + getStrokeLeft() + 1f,
                            upChartBottom);
                    path1.lineTo(
                            getStrokeWidth() + getStrokeLeft() + 1f,
                            (float) (upChartBottom - (data.get(0).getClose() - upMinData)
                                    * upRatio));
                }
                startWhiteX = endWhiteX;
                startWhiteY = endWhiteY;
            }
            path1.close();
            paint.setStyle(Style.STROKE);
            canvas.drawPath(path, paint);
            Paint LinerPaint = new Paint();
            LinearGradient lg=new LinearGradient(0,0,0,getHeight(),getContext().getResources().getColor(R.color.timechart_line_yellow_start),getContext().getResources().getColor(R.color.timechart_line_yellow_end), Shader.TileMode.MIRROR);
            LinerPaint.setShader(lg);
            LinerPaint.setStyle(Style.FILL);
            LinerPaint.setAlpha(80);
            canvas.drawPath(path1, LinerPaint);
        }
        paint.setColor(getContext().getResources()
                .getColor(R.color.timechart_yellow));
        startWhiteX = getStrokeWidth() + getStrokeLeft() + 1f;
        startWhiteY = (float) (upChartBottom - (data.get(0).getStockMeanLine() - upMinData)
                * upRatio);
        if (dataSize == 1) {
            canvas.drawCircle(startWhiteX, startWhiteY, 2, paint);
        } else {
            Path path = new Path();
            for (int i = 1; i < data.size() && i < maxDate; i++) {
                float endWhiteY = (float) (upChartBottom - (data.get(i)
                        .getStockMeanLine() -
                        upMinData) * upRatio);
                float endWhiteX = getStrokeWidth() + getStrokeLeft() + 1f
                        + dataSpacing * i;
   //             Log.w("TimeChartView", "startWhiteX=" + startWhiteX + "  startWhiteY=" + startWhiteY + "  endWhiteX=" + endWhiteX + "  endWhiteY=" + endWhiteY);
//                canvas.drawLine(startWhiteX, startWhiteY, endWhiteX, endWhiteY,
//                        paint);
                if (i == 1) {
                    path.moveTo(startWhiteX, startWhiteY);
                } else {
                    path.lineTo(startWhiteX, startWhiteY);
                }
                if (i == data.size() - 1 || i == maxDate - 1) {
                    path.lineTo(endWhiteX, endWhiteY);
                }
                startWhiteX = endWhiteX;
                startWhiteY = endWhiteY;
            }
            paint.setStyle(Style.STROKE);
            canvas.drawPath(path, paint);
        }
    }

    Path path = new Path();
    Path fillpath = new Path();

    /**
     * 绘制上表中的折线。 使曲线更加平滑
     *
     * @param canvas
     */
    private void drawCubicUpLine(Canvas canvas, Paint paint) {
        if (data == null)
            return;
        paint.setStrokeWidth(1);
        paint.setAntiAlias(true);
        float startWhiteX = getStrokeWidth() + getStrokeLeft() + 1f;
        float startWhiteY = (float) (upChartBottom - (data.get(0).getClose() - upMinData)
                * upRatio);
        paint.setColor(getContext().getResources().getColor(R.color.line_blue));
        int dataSize = data.size();
        // 画分时线
        if (dataSize == 1) {
            canvas.drawCircle(startWhiteX, startWhiteY, 3, paint);
        } else {
            path.reset();
            float prevDx = 0f;
            float prevDy = 0f;
            float curDx = 0f;
            float curDy = 0f;
            float preWhiteX = startWhiteX;
            float preWhiteY = startWhiteY;
            float endWhiteX = 0f;
            float endWhiteY = 0f;
            path.moveTo(startWhiteX, startWhiteY);
            for (int i = 1; i < data.size() && i < maxDate; i++) {

                endWhiteX = getStrokeWidth() + getStrokeLeft() + 1f
                        + dataSpacing * i;
                endWhiteY = (float) (upChartBottom - (data.get(i).getClose() - upMinData)
                        * upRatio);
                // paint.setColor(getContext().getResources().getColor(
                // R.color.line_blue));

                prevDx = (startWhiteX - preWhiteX) * 0.2f;
                prevDy = (startWhiteY - preWhiteY) * 0.2f;
                curDx = (endWhiteX - startWhiteX) * 0.2f;
                curDy = (endWhiteY - startWhiteY) * 0.2f;

                path.cubicTo(startWhiteX + prevDx, startWhiteY + prevDy,
                        endWhiteX - curDx, endWhiteY - curDy, endWhiteX,
                        endWhiteY);
                // if (i == 1)
                // path.moveTo(startWhiteX, startWhiteY);
                // path.lineTo(startWhiteX, startWhiteY);
                // if (i == data.size() - 1 || i == maxDate - 1) {
                // path.lineTo(endWhiteX, endWhiteY);
                // path.lineTo(endWhiteX, upChartBottom);
                // path.lineTo(getStrokeWidth() + getStrokeLeft() + 1f,
                // upChartBottom);
                // path.lineTo(getStrokeWidth() + getStrokeLeft() + 1f,
                // (float) (upChartBottom - (data.get(0).getClose() - upMinData)
                // * upRatio));
                // }

                preWhiteX = startWhiteX;
                preWhiteY = startWhiteY;
                startWhiteX = endWhiteX;
                startWhiteY = endWhiteY;
            }
            fillpath.reset();
            fillpath.addPath(path);
            drawFill(canvas, paint, fillpath, getStrokeWidth()
                    + getStrokeLeft() + 1f, endWhiteX, upChartBottom);
            // path.close();
            // paint.setStyle(Style.FILL);
            // paint.setAlpha(50);
            // canvas.drawPath(path, paint);
            paint.setStyle(Style.STROKE);
            canvas.drawPath(path, paint);
        }

        // 画分时均线
        paint.setColor(getContext().getResources()
                .getColor(R.color.line_yellow));
        startWhiteX = getStrokeWidth() + getStrokeLeft() + 1f;
        startWhiteY = (float) (upChartBottom - (data.get(0).getStockMeanLine() - upMinData)
                * upRatio);
        if (dataSize == 1) {
            canvas.drawCircle(startWhiteX, startWhiteY, 2, paint);
        } else {
            for (int i = 1; i < data.size() && i < maxDate; i++) {
                float endWhiteY = (float) (upChartBottom - (data.get(i)
                        .getStockMeanLine() -
                        upMinData) * upRatio);
                float endWhiteX = getStrokeWidth() + getStrokeLeft() + 1f
                        + dataSpacing * i;
                canvas.drawLine(startWhiteX, startWhiteY, endWhiteX, endWhiteY,
                        paint);
                startWhiteX = endWhiteX;
                startWhiteY = endWhiteY;
            }
        }
    }

    protected void drawFill(Canvas canvas, Paint paint, Path spline,
                            float from, float to, float bottom) {

        spline.lineTo(to, bottom);
        spline.lineTo(from, bottom);
        spline.close();

        paint.setStyle(Style.FILL);
        paint.setAlpha(50);
        canvas.drawPath(spline, paint);
        paint.setAlpha(255);
    }

    /**
     * 绘制上表Y轴文字
     *
     * @param canvas
     * @param paint
     */
    private void drawUpAxisXTitle(Canvas canvas, TextPaint paint,
                                  float dataWidth) {
        paint.setColor(getResources().getColor(R.color.text_color_5c5f66));
        paint.setAntiAlias(true);
        if (!isDrawOutside) {
            float YData = upMaxData;
            float zDF = 0;
            if (preClose != 0)
                zDF = ((YData - preClose) / preClose) * 100;
            float spacing = (upMaxData - upMinData) / (latitudesUpNum);
            for (int i = 0; i < latitudesUpNum + 1; i++) {
                String zdf = Parse.getInstance().parse2String(zDF) + "%";
                if ("-0.00%".equals(zdf))
                    zdf = "0.00%";
                if (i > 2)
                    paint.setColor(getContext().getResources().getColor(
                            R.color.green1));
                else if (i < 2)
                    paint.setColor(getContext().getResources().getColor(
                            R.color.red));
                else
                    paint.setColor(getContext().getResources().getColor(
                            R.color.text_color_5c5f66));
                if (i == 0) {
                    canvas.drawText(
                            Parse.getInstance().parse2String(upMaxData, num),
                            getStrokeWidth() + getStrokeLeft() + 2,
                            getStrokeWidth() + getStrokeTop() + textSize, paint);
                    // 绘制右边刻度
                    canvas.drawText(zdf,
                            getWidth() - getStrokeRight() - getStrokeWidth()
                                    / 2 - paint.measureText(zdf) - 2,
                            getStrokeWidth() + getStrokeTop() + textSize
                                    + latitudesSpacing * i, paint);
                } else if (i == latitudesUpNum) {
                    canvas.drawText(
                            Parse.getInstance().parse2String(upMinData, num),
                            getStrokeWidth() + getStrokeLeft() + 2,
                            (getStrokeWidth() + getStrokeTop() + latitudesSpacing
                                    * latitudesUpNum) - 2, paint);
                    // 绘制右边的
                    canvas.drawText(
                            zdf,
                            getWidth() - getStrokeRight() - getStrokeWidth()
                                    / 2 - paint.measureText(zdf) - 2,
                            (getStrokeWidth() + getStrokeTop() + latitudesSpacing
                                    * i) - 2, paint);
                } else {
                    canvas.drawText(Parse.getInstance()
                            .parse2String(YData, num), getStrokeWidth()
                            + getStrokeLeft() + 2, getStrokeWidth()
                            + getStrokeTop() +
                            textSize / 2 +
                            latitudesSpacing
                                    * i, paint);
                    // 绘制右边的
                    canvas.drawText(zdf,
                            getWidth() - getStrokeRight() - getStrokeWidth()
                                    / 2 - paint.measureText(zdf) - 2,
                            getStrokeWidth() + getStrokeTop() + textSize / 2
                                    + latitudesSpacing * i, paint);
                }
                YData -= spacing;
                if (preClose != 0)
                    zDF = ((YData - preClose) / preClose) * 100;
            }

        } else {
            float YData = upMaxData;
            float zDF = 0;
            if (preClose != 0)
                zDF = ((YData - preClose) / preClose) * 100;
            float spacing = (upMaxData - upMinData) / (latitudesUpNum);
            for (int i = 0; i < latitudesUpNum + 1; i++) {
                String zdf = Parse.getInstance().parse2String(zDF) + "%";
                if ("-0.00%".equals(zdf))
                    zdf = "0.00%";
                if (i > 2)
                    paint.setColor(getContext().getResources().getColor(
                            R.color.green1));
                else if (i < 2)
                    paint.setColor(getContext().getResources().getColor(
                            R.color.red));
                else
                    paint.setColor(getContext().getResources().getColor(
                            R.color.text_color_5c5f66));
                if (i == 0) {
                    canvas.drawText(
                            Parse.getInstance().parse2String(YData, num),
                            getStrokeLeft()
                                    - getStrokeWidth()
                                    / 2
                                    - 2
                                    - paint.measureText(Parse.getInstance()
                                    .parse2String(YData, num)),
                            getStrokeWidth() + getStrokeTop() + textSize
                                    + latitudesSpacing * i, paint);
                    // 绘制右边刻度
                    canvas.drawText(zdf, getWidth() - getStrokeRight() + 2,
                            getStrokeWidth() + getStrokeTop() + textSize
                                    + latitudesSpacing * i, paint);
                } else if (i == latitudesUpNum) {
                    canvas.drawText(
                            Parse.getInstance().parse2String(YData, num),
                            getStrokeLeft()
                                    - getStrokeWidth()
                                    / 2
                                    - 2
                                    - paint.measureText(Parse.getInstance()
                                    .parse2String(YData, num)),
                            (getStrokeWidth() + getStrokeTop() + latitudesSpacing
                                    * i) - 2, paint);
                    // 绘制右边的
                    canvas.drawText(
                            zdf,
                            getWidth() - getStrokeRight() + 2,
                            (getStrokeWidth() + getStrokeTop() + latitudesSpacing
                                    * i) - 2, paint);
                } else {
                    canvas.drawText(
                            Parse.getInstance().parse2String(YData, num),
                            getStrokeLeft()
                                    - getStrokeWidth()
                                    / 2
                                    - 2
                                    - paint.measureText(Parse.getInstance()
                                    .parse2String(YData, num)),
                            getStrokeWidth() + getStrokeTop() + textSize / 2
                                    + latitudesSpacing * i, paint);
                    // 绘制右边的
                    canvas.drawText(zdf, getWidth() - getStrokeRight() + 2,
                            getStrokeWidth() + getStrokeTop() + textSize / 2
                                    + latitudesSpacing * i, paint);
                }
                YData -= spacing;
                if (preClose != 0)
                    zDF = ((YData - preClose) / preClose) * 100;
            }
        }
    }

    /**
     * 绘制下表柱形图
     *
     * @param canvas 画布
     */
    private void drawDownColumnChart(Canvas canvas, Paint paint) {
        if (data == null)
            return;
        paint.setStrokeWidth(columnWidth);
        paint.setAntiAlias(true);
        // Map<String, Object> map = timesList.get(0);
        // float zuoShow = Float.parseFloat(map.get("zuoShow").toString());
        for (int i = 0; i < data.size() && i < maxDate; i++) {
            float chengJiaoLiang = (float) data.get(i).getCjl();
            paint.setColor(data.get(i).getColor());
            float x = getStrokeWidth() + getStrokeLeft() + 1.0f + dataSpacing
                    * i;
            canvas.drawLine(x, downChartBottom, x, downChartBottom
                    - chengJiaoLiang * downRatio, paint);
        }
    }

    /**
     * 绘制下表Y轴X坐标
     *
     * @param canvas
     * @param paint
     * @param dataWidth
     */
    private void drawDownAxisXTitle(Canvas canvas, TextPaint paint,
                                    float dataWidth) {
        paint.setColor(getResources().getColor(R.color.text_color_5c5f66));
        paint.setAntiAlias(true);
        if (!isDrawOutside) {
            float YData = downMinData;
            float spacing = (downMaxData - downMinData) / (latitudesDownNum);
            for (int i = 0; i < latitudesDownNum + 1; i++) {
                if (i == latitudesDownNum) {
                    canvas.drawText(
                            Parse.getInstance().parse2CNStringWan(YData, 2,
                                    false), getStrokeWidth() + getStrokeLeft()
                                    + 2, downChartBottom + textSize
                                    - latitudesSpacing * i, paint);
                } else if (i == 0) {
                    canvas.drawText("0"
                    /* Parse.getInstance().parse2CNString(YData) */,
                            getStrokeWidth() + getStrokeLeft() + 2,
                            downChartBottom - latitudesSpacing * i - 2, paint);
                } else {
                    canvas.drawText(
                            Parse.getInstance().parse2CNStringWan(YData, 2,
                                    false), getStrokeWidth() + getStrokeLeft()
                                    + 2, downChartBottom + textSize / 2
                                    - latitudesSpacing * i, paint);
                }
                YData += spacing;
            }

        } else {
            float YData = downMinData;
            float spacing = (downMaxData - downMinData) / (latitudesDownNum);
            for (int i = 0; i < latitudesDownNum + 1; i++) {
                if (i == latitudesDownNum) {
                    canvas.drawText(
                            Parse.getInstance().parse2CNStringWan(YData, 2,
                                    false),
                            getStrokeLeft()
                                    - getStrokeWidth()
                                    / 2
                                    - 2
                                    - paint.measureText(Parse.getInstance()
                                    .parse2CNStringWan(YData, 2, false)),
                            downChartBottom + textSize - latitudesSpacing * i,
                            paint);
                } else if (i == 0) {
                    canvas.drawText(
                            "0"
                            /* Parse.getInstance().parse2CNString(YData) */,
                            getStrokeLeft() - getStrokeWidth() / 2 - 2
                                    - paint.measureText("0"/*
                                                             * Parse.getInstance(
															 * )
															 * .parse2CNString(
															 * YData)
															 */),
                            downChartBottom - latitudesSpacing * i - 2, paint);
                } else {
                    canvas.drawText(
                            Parse.getInstance().parse2CNStringWan(YData, 2,
                                    false),
                            getStrokeLeft()
                                    - getStrokeWidth()
                                    / 2
                                    - 2
                                    - paint.measureText(Parse.getInstance()
                                    .parse2CNStringWan(YData, 2, false)),
                            downChartBottom + textSize / 2 - latitudesSpacing
                                    * i, paint);
                }
                YData += spacing;
            }
        }
    }

    /**
     * 绘制时间
     *
     * @param canvas
     * @param paint
     */
    private void drawTime(Canvas canvas, TextPaint paint, ArrayList<String> date) {
        paint.setColor(getResources().getColor(R.color.text_color_5c5f66));
        paint.setAntiAlias(true);
        float Y = 0.0f;
        if (spacing != 0.0f) {
            Y = upChartBottom + textSize;
        } else {
            Y = getHeight();
        }
        for (int i = 0; i < date.size(); i++) {
            if (i == date.size() - 1) {
                canvas.drawText(date.get(i),
                        getWidth() - getStrokeWidth() - getStrokeRight() - 2
                                - paint.measureText(date.get(i)), Y, paint);
            } else if (i == 0) {
                canvas.drawText(date.get(i), getStrokeLeft() + getStrokeWidth()
                        + 2, Y, paint);
            } else {
                // if ("18:00".equals(date.get(i))) {
                // canvas.drawText(
                // date.get(i),
                // getStrokeWidth() + getStrokeLeft()
                // + longitudesSpacing
                // * ((longitudesNum + 2 - 1) / 2)
                // - paint.measureText(date.get(i)) / 2, Y,
                // paint);
                // } else {
                canvas.drawText(date.get(i),
                        getStrokeWidth() + getStrokeLeft() + longitudesSpacing
                                * i -
                                paint.measureText(date.get(i)) / 2, Y,
                        paint);
                // }
            }
        }
    }

    /**
     * 绘制十字光标
     *
     * @param canvas 画布
     */
    private void drawCrossLine(Canvas canvas, Paint paint) {
        if (!isShowCross)
            return;
        paint.setStrokeWidth(1);
        paint.setColor(getContext().getResources().getColor(
                R.color.cross_line_color));
        paint.setAntiAlias(true);
        int rSpacing = 12;
        // 绘制十字光标横线
        canvas.drawLine(getStrokeLeft() + getStrokeWidth(), crossY, crossX
                - rSpacing >= getStrokeLeft() +
                getStrokeWidth() ?
                crossX
                        - rSpacing : getStrokeLeft() +
                getStrokeWidth(), crossY, paint);
        // 绘制十字光标横线
        canvas.drawLine(crossX + rSpacing <= getWidth() - getStrokeWidth()
                        - getStrokeRight() ? crossX + rSpacing : getWidth()
                        -
                        getStrokeWidth() -
                        getStrokeRight(), crossY,
                getWidth()
                        - getStrokeWidth() - getStrokeRight(), crossY, paint);
        // 绘制上表十字光标竖线
        canvas.drawLine(crossX, getStrokeWidth() + getStrokeTop(), crossX,
                crossY - rSpacing >= getStrokeWidth() + getStrokeTop() ? crossY
                        - rSpacing
                        : getStrokeWidth() +
                        getStrokeTop(), paint);
        // 绘制上表十字光标竖线
        canvas.drawLine(crossX, crossY + rSpacing <= upChartBottom ? crossY
                + rSpacing
                : upChartBottom, crossX, upChartBottom, paint);
        if(isDrawDownChart) {
            // 绘制下表十字光标竖线
            canvas.drawLine(crossX, getHeight() - getStrokeWidth()
                            - getStrokeBottom(), crossX, downChartBottom - downChartHeight,

                    paint);
        }
        // 绘制十字光标交叉小圆点
        Paint p = new Paint();
        p.setColor(getContext().getResources().getColor(R.color.cross_line_color));
        p.setAntiAlias(true);
        p.setAlpha(150);
        canvas.drawCircle(crossX, crossY, 12, p);
        p.setAlpha(255);
        canvas.drawCircle(crossX, crossY, 6, p);

        // 绘制十字光标中心数据
        TextPaint tp = new TextPaint();
        tp.setColor(Color.WHITE);
        tp.setTextSize(textSize);
        tp.setAntiAlias(true);
        tp.setTypeface(Typeface.DEFAULT_BOLD);

        paint.setColor(getResources().getColor(R.color.cross_line_color));
        paint.setStrokeWidth(textSize + 6);
        String leftDatas = Parse.getInstance().parse2String(leftData, num);
        String rightDatas = Parse.getInstance().parse2String(rightData) + "%";
        if (!isDrawOutside) {
            float topS = crossY - (textSize + 6) / 2 <= getStrokeTop()
                    + getStrokeWidth() ? getStrokeTop() +
                    getStrokeWidth()
                    + (textSize + 6) / 2
                    : crossY;
            topS = topS + (textSize + 6) / 2 >= upChartBottom ? upChartBottom
                    - (textSize + 6) / 2 : topS;
            float size = MyUtils.getInstance().dp2px(getContext(), 10);
            float dataWidth = tp.measureText(leftDatas);
            canvas.drawLine(getStrokeLeft() + getStrokeWidth() / 2 + dataWidth
                            + size, topS, getStrokeLeft() + getStrokeWidth() / 2, topS,
                    paint);
            canvas.drawText(leftDatas, getStrokeLeft() + getStrokeWidth() / 2
                    + size / 2, topS + textSize / 2 - 3, tp);

            if (rightData > 0.0f) {
                rightDatas = "+" + rightDatas;
            }
            dataWidth = tp.measureText(rightDatas);
            canvas.drawLine(getWidth() - getStrokeRight() - getStrokeWidth()
                            / 2 - dataWidth - size, topS,
                    getWidth() - getStrokeWidth()
                            / 2 - getStrokeRight(), topS, paint);
            canvas.drawText(rightDatas, getWidth() - getStrokeRight()
                    - getStrokeWidth() / 2 - dataWidth - size / 2, topS
                    + textSize /
                    2 - 3, tp);

            String bottomDatas = Parse.getInstance().parse2CNStringWan(
                    bottomData, 2, false);
            dataWidth = tp.measureText(bottomDatas);
            float Y = downChartBottom - textSize / 2 - 3;
            float drawX = crossX - (dataWidth + size) / 2;
            if (drawX <= getStrokeLeft() + getStrokeWidth() / 2) {
                drawX = getStrokeLeft() + getStrokeWidth() / 2;
            } else if (drawX >= getWidth() - getStrokeRight()
                    - getStrokeWidth() / 2 - dataWidth - size) {
                drawX = getWidth() - getStrokeRight() - getStrokeWidth() / 2
                        - dataWidth - size;
            }
            canvas.drawLine(drawX, Y, drawX + dataWidth + size, Y, paint);
            canvas.drawText(bottomDatas, drawX + size / 2, Y + textSize / 2, tp);

            /**
             * 绘制时间
             */
            String timeDatas = MyUtils.getInstance().date2String("HH:mm", time);
            dataWidth = tp.measureText(timeDatas);
            float timeY = upChartBottom + textSize / 2;
            float drawTimeX = crossX - (dataWidth + size) / 2;
            if (drawTimeX <= getStrokeLeft() + getStrokeWidth() / 2) {
                drawTimeX = getStrokeLeft() + getStrokeWidth() / 2;
            } else if (drawTimeX >= getWidth() - getStrokeRight()
                    - getStrokeWidth() / 2 - dataWidth - size) {
                drawTimeX = getWidth() - getStrokeRight() - getStrokeWidth() / 2
                        - dataWidth - size;
            }
            canvas.drawLine(drawTimeX, timeY, drawTimeX + dataWidth + size, timeY, paint);
            canvas.drawText(timeDatas, drawTimeX + size / 2, timeY + textSize / 2, tp);
        } else {
            float topS = crossY - (textSize + 6) / 2 <= getStrokeTop()
                    + getStrokeWidth() ? getStrokeTop() +
                    getStrokeWidth()
                    + (textSize + 6) / 2
                    : crossY;
            topS = topS + (textSize + 6) / 2 >= upChartBottom ? upChartBottom
                    - (textSize + 6) / 2 : topS;
            float size = MyUtils.getInstance().dp2px(getContext(), 1);
            float dataWidth = tp.measureText(leftDatas);
            canvas.drawLine(getStrokeLeft() - getStrokeWidth() / 2 - dataWidth
                            - size, topS, getStrokeLeft() - getStrokeWidth() / 2, topS,
                    paint);
            canvas.drawText(leftDatas, getStrokeLeft() - getStrokeWidth() / 2
                    - dataWidth - size / 2, topS + textSize / 2 - 3, tp);

            if (rightData > 0.0f) {
                rightDatas = "+" + rightDatas;
            }
            dataWidth = tp.measureText(rightDatas);
            canvas.drawLine(getWidth() - getStrokeRight(), topS, getWidth()
                    - getStrokeRight() + dataWidth +
                    size, topS, paint);
            canvas.drawText(rightDatas, getWidth() - getStrokeRight() + size
                            / 2,
                    topS + textSize / 2 - 3, tp);


            String bottomDatas = Parse.getInstance().parse2CNStringWan(
                    bottomData, 2, false);
            dataWidth = tp.measureText(bottomDatas);
            float Y = downChartBottom - textSize / 2 - 3;
            float drawX = crossX - (dataWidth + size) / 2;
            if (drawX <= getStrokeLeft() + getStrokeWidth() / 2) {
                drawX = getStrokeLeft() + getStrokeWidth() / 2;
            } else if (drawX >= getWidth() - getStrokeRight()
                    - getStrokeWidth() / 2 - dataWidth - size) {
                drawX = getWidth() - getStrokeRight() - getStrokeWidth() / 2
                        - dataWidth - size;
            }
            canvas.drawLine(drawX, Y, drawX + dataWidth + size, Y, paint);
            canvas.drawText(bottomDatas, drawX + size / 2, Y + textSize / 2, tp);

            /**
             * 绘制时间
             */
            String timeDatas = MyUtils.getInstance().date2String("HH:mm", time);
            dataWidth = tp.measureText(timeDatas);
            float timeY = upChartBottom + textSize / 2;
            float drawTimeX = crossX - (dataWidth + size) / 2;
            if (drawTimeX <= getStrokeLeft() + getStrokeWidth() / 2) {
                drawTimeX = getStrokeLeft() + getStrokeWidth() / 2;
            } else if (drawTimeX >= getWidth() - getStrokeRight()
                    - getStrokeWidth() / 2 - dataWidth - size) {
                drawTimeX = getWidth() - getStrokeRight() - getStrokeWidth() / 2
                        - dataWidth - size;
            }
            canvas.drawLine(drawTimeX, timeY, drawTimeX + dataWidth + size, timeY, paint);
            canvas.drawText(timeDatas, drawTimeX + size / 2, timeY + textSize / 2, tp);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 按下
                touchDownX = event.getX();
                touchDownY = event.getY();
                if (touchDownX < getStrokeLeft()
                        || touchDownX > getWidth() - getStrokeRight()) {
                    return super.onTouchEvent(event);
                }
                movedX = touchDownX;
                movedY = touchDownY;
                moveRawX = event.getRawX();
                moveRawY = event.getRawY();
                isMoved = false;
                isReleased = false;
                isStartMoved = false;
                if (mRunnable != null) {
                    removeCallbacks(mRunnable);
                    postDelayed(mRunnable, 200);
                }
                getParent().requestDisallowInterceptTouchEvent(true);
                return true;

            case MotionEvent.ACTION_POINTER_DOWN:
                // 多点
                if (touchDownX < getStrokeLeft()
                        || touchDownX > getWidth() - getStrokeRight()) {
                    return super.onTouchEvent(event);
                }
                return true;

            case MotionEvent.ACTION_MOVE:
                // 触摸中
                if (touchDownX < getStrokeLeft()
                        || touchDownX > getWidth() - getStrokeRight()) {
                    return super.onTouchEvent(event);
                }
                // 子控件相对于父布局若达到滚动条件,则让父布局拦截触摸事件


             /*   if (Math.abs(event.getRawY() - moveRawY) > 50 && Math.abs(event.getRawX() - moveRawX) < 150) {
                    isMoved = true;
                    getParent().requestDisallowInterceptTouchEvent(false);
                }*/
                isMoved = false;
                if (isStartMoved && !isMoved) {
                    movedX = event.getX();
                    movedY = event.getY();
                    viewCrossLine();
                }
                break;

            case MotionEvent.ACTION_UP:
                // 拿起
                if (touchDownX < getStrokeLeft()
                        || touchDownX > getWidth() - getStrokeRight()) {
                    return super.onTouchEvent(event);
                }
                if (!isStartMoved && !isMoved) {
                    if (Math.abs(Math.abs(event.getX()) - Math.abs(touchDownX)) < 30
                            && Math.abs(Math.abs(event.getY())
                            - Math.abs(touchDownY)) < 30) {
                        if (mOnTimeChartClickListener != null)
                            mOnTimeChartClickListener.click(this);
                    }
                }
                isReleased = true;
                isShowCross = false;
                isStartMoved = false;
                if (l != null)
                    l.isMove(this, isShowCross, crossX);
                invalidate();
                return true;

            default:
                break;
        }
        return super.onTouchEvent(event);
    }

    /**
     * 设置数据
     *
     * @param data        分时数据
     * @param upMaxData   最大价
     * @param upMinData   最小价
     * @param downMinData 成交量数据
     * @param downMinData 最小成交量
     * @param preClose    昨收价
     * @param isHighLow   是否将最高最低价作为基准,否则取收盘价中的最高最低
     */
    public void setTimesList(Vector<TimeData> data, List<TRADETIME> tradetimes, float upMaxData,
                             float upMinData, float downMinData, float preClose,
                             boolean isHighLow) {
        // handler.removeCallbacks(mTimeListThread);
        // mTimeListThread.setData(data, upMaxData, upMinData, downMinData,
        // preClose, isHighLow);
        // handler.post(mTimeListThread);
        /*
         * if (!isAlive(mTimeListThread)) { mTimeListThread = new
		 * TimeListThread(data, upMaxData, upMinData, downMinData, preClose,
		 * isHighLow); mTimeListThread.start(); }
		 */
        executorService.execute(new TimeListThread(data, tradetimes, upMaxData, upMinData,
                downMinData, preClose, isHighLow));
    }

    /**
     * 计算
     *
     * @author dingrui
     */
    boolean running = false;

    class TimeListThread implements Runnable {

        private Vector<TimeData> dataOld;
        private List<TRADETIME> tradetimesOld;// 时间段
        private float upMaxData;
        private float upMinData;
        private float downMinData;
        private float downMaxData;
        private float preClose;
        private boolean isHighLow;// 是否直接取最高最低

        public TimeListThread(Vector<TimeData> data, List<TRADETIME> tradetimes, float upMaxData,
                              float upMinData, float downMinData, float preClose,
                              boolean isHighLow) {
            this.dataOld = data;
            this.tradetimesOld = tradetimes;
            this.upMaxData = upMaxData;
            this.upMinData = upMinData;
            this.downMinData = downMinData;
            this.preClose = preClose;
            this.isHighLow = isHighLow;
        }

        @Override
        public void run() {
            if (running)
                return;
            synchronized (this) {
            if (tradetimesOld == null || tradetimesOld.size() <= 0) {
                return;
            }
                running = true;
                List<Long> timeMillis = new ArrayList<>();
                float timeS = 0.0f;
                List<TRADETIME> tradetimes = new ArrayList<>();
                for (int i = 0; i < tradetimesOld.size(); i++) {
                    TRADETIME oldItem = tradetimesOld.get(i);
                    TRADETIME item = new TRADETIME();
                    item.setOpenTime(oldItem.getOpenTime());
                    item.setEndTime(oldItem.getEndTime());
                    tradetimes.add(item);
                }
                for (int i = 0; i < tradetimes.size(); i++) {
                    String openTime = Parse.getInstance().isNull(tradetimes.get(i).getOpenTime());
                    String endTime = Parse.getInstance().isNull(tradetimes.get(i).getEndTime());
                    int openH, openM;
                    int endH, endM;
                    if (openTime.length() == 1) {
                        openH = 0;
                        openM = 0;
                    } else if (openTime.length() == 3) {
                        openH = Parse.getInstance().parseInt(openTime.substring(0, 1));
                        openM = Parse.getInstance().parseInt(openTime.substring(1, openTime.length()));
                    } else {
                        openH = Parse.getInstance().parseInt(openTime.substring(0, 2));
                        openM = Parse.getInstance().parseInt(openTime.substring(2, openTime.length()));
                    }
                    if (endTime.length() == 1) {
                        endH = 0;
                        endM = 0;
                    } else if (endTime.length() == 3) {
                        endH = Parse.getInstance().parseInt(endTime.substring(0, 1));
                        endM = Parse.getInstance().parseInt(endTime.substring(1, endTime.length()));
                    } else {
                        endH = Parse.getInstance().parseInt(endTime.substring(0, 2));
                        endM = Parse.getInstance().parseInt(endTime.substring(2, endTime.length()));
                    }

                    float min;
                    int hour;
                    int openH1 = openH, openM1 = openM;
                    int endH1 = endH, endM1 = endM;
                    if (endM1 < openM1) {
                        min = (endM1 + 60 - openM1) / 60.0f;
                        endH1 = endH1 - 1;
                    } else {
                        min = (endM1 - openM1) / 60.0f;
                    }
                    if (endH1 <= openH1) {
                        hour = endH1 + 24 - openH1;
                        tradetimes.get(i).setOpenTimes(String.format("2016-01-01 %02d:%02d:00", openH, openM));
                        tradetimes.get(i).setEndTimes(String.format("2016-01-02 %02d:%02d:00", endH, endM));
                    } else {
                        hour = endH1 - openH1;
                        tradetimes.get(i).setOpenTimes(String.format("2016-01-02 %02d:%02d:00", openH, openM));
                        tradetimes.get(i).setEndTimes(String.format("2016-01-02 %02d:%02d:00", endH, endM));
                    }
                    String minS = String.format("%.2f", hour + min);
                    tradetimes.get(i).setTimeSlot(Parse.getInstance().parseFloat(minS));
                    timeS += tradetimes.get(i).getTimeSlot();
                    for (long j = tradetimes.get(i).getOpenTimeMillis();
                         j <= tradetimes.get(i).getEndTimeMillis(); j += 60000) {// 60000毫秒=1分钟
                        if (i == 0) {
                            timeMillis.add(j);
                        } else {
                            if (j != tradetimes.get(i).getOpenTimeMillis()) {
                                timeMillis.add(j);
                            }
                        }
                    }
                }
                maxDate = (int) (timeS * 60) + 1;
                Vector<TimeData> data = new Vector<>();
                try {
                    if (dataOld != null && dataOld.size() > 0 && timeMillis.size() > 0) {
                        for (int i = 0; i < dataOld.size() && i < timeMillis.size(); i++) {
                            TimeData oldItem = dataOld.get(i);
                            double close;
                            if (i > 0) {
                                close = data.get(i - 1).getClose();
                            } else {
                                close = preClose;
                            }
                            TimeData item = new TimeData();
                            item.setTime(timeMillis.get(i));
                            item.setClose(oldItem.getClose());
                            item.setCjlYS(oldItem.getCjlYS());
                            item.setCjeYS(oldItem.getCjeYS());
                            item.setCjl(oldItem.getCjl());
                            item.setCje(oldItem.getCje());
                            item.setStockMeanLine(oldItem.getStockMeanLine());
                            double zde = oldItem.getClose() - preClose;
                            double zdf = zde / preClose * 100;
                            item.setZde(zde);
                            item.setZdf(zdf);
                            if (item.getClose() > close) {
                                item.setColor(getResources().getColor(R.color.red));
                            } else if (item.getClose() < close) {
                                item.setColor(getResources().getColor(R.color.green));
                            } else {
                                item.setColor(Color.WHITE);
                            }
                            data.add(item);
                        }
                        if (!isHighLow) {
                            upMaxData = (float) data.get(0).getClose();
                            upMinData = upMaxData;
                        }
                        downMaxData = (float) data.get(0).getCjl();
                        for (int i = 1; i < data.size() && i < maxDate; i++) {
                            if (!isHighLow) {
                                upMaxData = (float) (data.get(i).getClose() < upMaxData ? upMaxData
                                        : data.get(i).getClose());
                                upMinData = (float) (data.get(i).getClose() > upMinData ? upMinData
                                        : data.get(i).getClose());

                                upMaxData = (float) (data.get(i).getStockMeanLine() < upMaxData ? upMaxData
                                        : data.get(i).getStockMeanLine());
                                upMinData = (float) (data.get(i).getStockMeanLine() > upMinData ? upMinData
                                        : data.get(i).getStockMeanLine());
                            }
                            // 下表相关
                            if (data.size() == 0) {
                                return;
                            }
                            downMaxData = (float) (data.get(i).getCjl() < downMaxData ? downMaxData
                                    : data.get(i).getCjl());
                        }
                    } else {
                        downMaxData = 0;
                    }
                } catch (ArrayIndexOutOfBoundsException ex) {
                    ex.printStackTrace();
                }

                Bundle bundle = new Bundle();
                bundle.putSerializable("data",
                        data);
                bundle.putSerializable("tradetimes", (Serializable) tradetimes);
                bundle.putFloat("upMaxData", upMaxData);
                bundle.putFloat("upMinData", upMinData);
                bundle.putFloat("downMinData", downMinData);
                bundle.putFloat("downMaxData", downMaxData);
                bundle.putFloat("preClose", preClose);


//            TimeChartView.this.data = data;
//            TimeChartView.this.upMaxData = upMaxData;
//            TimeChartView.this.upMinData = upMinData;
//            TimeChartView.this.downMinData = downMinData;
//            TimeChartView.this.downMaxData = downMaxData;
//            TimeChartView.this.preClose = preClose;
//            if (!isShowCross)
//                postInvalidate();
                Message msg = handler.obtainMessage();
                msg.what = 0x2;
                msg.setData(bundle);
                handler.sendMessage(msg);
                running = false;
            }
        }
    }

    /**
     * 设置上表与下表中间的日期与时间的文字大小
     *
     * @param textSize 像素单位
     * @return
     */
    public GridChartView settextSize(float textSize) {
        this.textSize = textSize;
        return this;
    }

    /**
     * 获取上表与下表中间的日期与时间的文字大小
     *
     * @return 像素单位
     */
    public float gettextSize() {
        return textSize;
    }

    /**
     * 设置上表纬线条数
     *
     * @param latitudesUpNum
     * @return
     */
    public GridChartView setLatitudesUpNum(int latitudesUpNum) {
        if (latitudesUpNum < 3) {
            latitudesUpNum = DEFAULT_LATITUDE_UP_NUM;
        }
        this.latitudesUpNum = latitudesUpNum;
        return this;
    }

    /**
     * 设置经纬线颜色
     *
     * @param longitudesColor
     * @return
     */
    public GridChartView setLongitudesColor(int longitudesColor) {
        this.longitudesColor = longitudesColor;
        return this;
    }

    /**
     * 设置经、纬线宽度
     *
     * @param latLongWidth
     * @return
     */
    public GridChartView setLatLongWidth(float latLongWidth) {
        if (latLongWidth < DEFAULT_LATITUDE_LONGITUDE_WIDTH) {
            latLongWidth = DEFAULT_LATITUDE_LONGITUDE_WIDTH;
        }
        this.latLongWidth = latLongWidth;
        return this;
    }

    /**
     * 返回是否绘制经线
     *
     * @return
     */
    public boolean isDrawLongitudes() {
        return isDrawLongitudes;
    }

    /**
     * 获取上表高度
     *
     * @return
     */
    public float getUpChartHeight() {
        return Math.abs(upChartHeight);
    }

    /**
     * 设置是否绘制经线
     *
     * @param isDrawLongitudes true绘制,false不绘制
     * @return
     */
    public GridChartView setDrawLongitudes(boolean isDrawLongitudes) {
        this.isDrawLongitudes = isDrawLongitudes;
        return this;
    }

    /**
     * 返回是否绘制纬线
     *
     * @return
     */
    public boolean isDrawLatitudes() {
        return isDrawLatitudes;
    }

    /**
     * 设置是否显示纬线
     *
     * @param isDrawLatitudes true绘制,false不绘制
     * @return
     */
    public GridChartView setDrawLatitudes(boolean isDrawLatitudes) {
        this.isDrawLatitudes = isDrawLatitudes;
        return this;
    }

    /**
     * 设置上表与下表间距(如果设置值小于默认字体大小,则设置成默认字体大小大5像素;如果设置值大于默认字体大小+5大小,那么字体也随之变化;
     * 如果设置值等于0.0f,那么时间文字将绘制则底部,并且上表与下表间距为0,底部高度为默认字体高度)
     *
     * @param spacing
     */
    public void setSpacing(float spacing) {
        if (spacing < DEFAULT_textSize && spacing > 0) {
            spacing = DEFAULT_textSize * 2;
        } else if (spacing > DEFAULT_textSize * 2) {
            this.spacing = spacing;
            textSize = spacing / 2;
        } else {
            this.spacing = spacing;
        }
    }

    /**
     * 设置经线类型
     *
     * @param longitudesTheme
     */
  /*  public void setLongitudesTheme(int longitudesTheme) {
        if (longitudesTheme < DEFAULT_THEME
                && longitudesTheme > LongitudesTheme.theme8) {
            longitudesTheme = DEFAULT_THEME;
        }
        if (longitudesTheme == LongitudesTheme.theme0) {
            longitudesNum = DEFAULT_LONGITUDE_NUM;
            maxDate = MAX_DATE;
        } else if (longitudesTheme == LongitudesTheme.theme1) {
            longitudesNum = DEFAULT_LONGITUDE_NUM;
            maxDate = 60 * 24;
        } else if (longitudesTheme == LongitudesTheme.theme2) {
            longitudesNum = 2;
            maxDate = 11 * 60;
        } else if (longitudesTheme == LongitudesTheme.theme3) {
            longitudesNum = 2;
            maxDate = 18 * 60;
        } else if (longitudesTheme == LongitudesTheme.theme4) {
            longitudesNum = DEFAULT_LONGITUDE_NUM;
            maxDate = (int) (9.5 * 60);
        } else if (longitudesTheme == LongitudesTheme.theme5) {
            longitudesNum = 2;
            maxDate = (int) (3.75 * 60);
        } else if (longitudesTheme == LongitudesTheme.theme6) {
            longitudesNum = DEFAULT_LONGITUDE_NUM;
            maxDate = (int) (6.25 * 60);
        } else if (longitudesTheme == LongitudesTheme.theme7) {
            longitudesNum = DEFAULT_LONGITUDE_NUM;
            maxDate = (int) (5.75 * 60);
        } else if (longitudesTheme == LongitudesTheme.theme8) {
            longitudesNum = DEFAULT_LONGITUDE_NUM;
            maxDate = (int) (7.75 * 60);
        }
        this.longitudesTheme = longitudesTheme;
    } */

    /**
     * 设置是否将两边坐标值绘制在边框线外(注:如果设置成true,必须设置strokeLeft和strokeRight)
     *
     * @param isDrawOutside
     * @param strokeLeft    左边距
     * @param strokeRight   右边距
     */
    public void setDrawOutside(boolean isDrawOutside, float strokeLeft,
                               float strokeRight) {
        this.isDrawOutside = isDrawOutside;
        if (isDrawOutside == false) {
            setStrokeLeft(1.0f);
            setStrokeRight(1.0f);
        } else {
            setStrokeLeft(strokeLeft);
            setStrokeRight(strokeRight);
        }
        invalidate();
    }

    /**
     * 设置小数保留位数
     *
     * @param num
     */
    public void setNum(int num) {
        this.num = num;
        invalidate();
    }

    /**
     * 设置接口
     */
    public void setOnTimeListener(OnTimeListener l) {
        this.l = l;
    }

    /**
     * 通知外部接口
     *
     * @author 锐
     */
    public interface OnTimeListener extends OnTimeDataChangeListener {

        /**
         * 是否显示指标详情
         */
        void isMove(View view, boolean isMove, float TouchX);
    }

    public interface OnTimeDataChangeListener {
        /**
         * 传递数据
         *
         * @param view
         * @param data
         * @param position
         * @param preClose 昨收价
         */
        void listener(View view, Vector<TimeData> data, int position, double preClose, int num);
    }


    /**
     * 计算十字光标位置
     */
    private void viewCrossLine() {
        // TODO Auto-generated method stub
        if (data != null && data.size() > 0) {
            getParent().requestDisallowInterceptTouchEvent(true);

            movedX = movedX - (getStrokeWidth() + getStrokeLeft() + 1f);
            if (movedX < 0f) {
                movedX = 0f;
            } else if (movedX >
                    getWidth() - (getStrokeWidth() + getStrokeLeft() + getStrokeRight() + 2f)) {
                movedX = getWidth() - (getStrokeWidth() + getStrokeLeft() + getStrokeRight() + 2f);
            }
            int index = (int) (movedX / dataSpacing);

            if (data == null || data.size() == 0) {
                return;
            } else if (index > data.size() - 1) {
                index = data.size() - 1;
            }
            crossX = getStrokeWidth() + getStrokeLeft() + 1f + dataSpacing
                    * index;
            crossY = (float) (upChartBottom - (data.get(index).getClose() - upMinData)
                    * upRatio);
            leftData = (float) data.get(index).getClose();
            rightData = (float) data.get(index).getZdf();
            bottomData = (float) data.get(index).getCjl();
            time = data.get(index).getTime();
            isShowCross = true;
            invalidate();
            if (l != null) {
                l.listener(this, data, index, preClose, num);
                l.isMove(this, isShowCross, crossX);
            }

//            for (int i = 0; i < data.size() - 1 && i < maxDate - 1; i++) {
//                float Y = (float) (upChartBottom - (data.get(i).getClose() - upMinData)
//                                                   * upRatio);
//                float endY = (float) (upChartBottom - (data.get(i + 1)
//                                                               .getClose() - upMinData) * upRatio);
//                float X = getStrokeWidth() + getStrokeLeft() + 1f + dataSpacing
//                                                                    * i;
//                float endX = getStrokeWidth() + getStrokeLeft() + 1f
//                             + dataSpacing * (i + 1);
//                float spacing = endX - X;
//                float positionX = movedX - X;
//                if (movedX >= X && movedX < endX) {
//                    isShowCross = true;
//                    if (positionX <= spacing / 2) {
//                        crossX = X;
//                        crossY = Y;
//                        leftData = (float) data.get(i).getClose();
//                        rightData = (float) data.get(i).getZdf();
//                        bottomData = (float) data.get(i).getCjl();
//                        time = data.get(i).getTime();
//                        if (l != null) {
//                            l.listener(this, data, i, preClose);
//                            l.isMove(this, isShowCross, crossX);
//                        }
//                    } else {
//                        if (data.size() == 0) {
//                            return;
//                        }
//                        crossX = endX;
//                        crossY = endY;
//                        leftData = (float) data.get(i + 1).getClose();
//                        rightData = (float) data.get(i + 1).getZdf();
//                        bottomData = (float) data.get(i + 1).getCjl();
//                        time = data.get(i + 1).getTime();
//                        if (l != null) {
//                            l.listener(this, data, i + 1, preClose);
//                            l.isMove(this, isShowCross, crossX);
//                        }
//                    }
//                    invalidate();
//                    return;
//                } else if (movedX >= endX
//                           && (i + 1 == data.size() - 1 || i + 1 == maxDate - 1)) {
//                    isShowCross = true;
//                    crossX = endX;
//                    crossY = endY;
//                    leftData = (float) data.get(i + 1).getClose();
//                    rightData = (float) data.get(i + 1).getZdf();
//                    bottomData = (float) data.get(i + 1).getCjl();
//                    time = data.get(i + 1).getTime();
//                    if (l != null) {
//                        l.listener(this, data, i + 1, preClose);
//                        l.isMove(this, isShowCross, crossX);
//                    }
//                    invalidate();
//                    return;
//                }
//            }
        }
    }

    public void setOnTimeChartClickListener(OnTimeChartClickListener l) {
        this.mOnTimeChartClickListener = l;
    }

    /**
     * 单击事件
     *
     * @author dingrui
     */
    public interface OnTimeChartClickListener {
        public void click(View v);
    }

    /**
     * 判断线程是否活动
     *
     * @param thread
     * @return
     */
    private boolean isAlive(Thread thread) {
        if (thread == null)
            return false;
        else {
            if (thread.isAlive())
                return true;
            else
                return false;
        }
    }

    public boolean isDrawDownChart() {
        return isDrawDownChart;
    }

    public void setDrawDownChart(boolean drawDownChart) {
        isDrawDownChart = drawDownChart;
    }

}
作者 east
Android 12月 8,2020

Android绘制股票k线图系列1-绘制网格线背景

public class GridChartView extends View {

	/** 当前控件高度 */
	private float viewHight;
	/** 当前控件宽度 */
	private float viewWidth;
	/** 边框宽度 */
	private float strokeWidth;
	/** 默认边框宽度 */
	private final float DEFAULT_STROKEWIDTH = 0.8f;
	/** 边框颜色 */
	private int strokeColor;
	/** 默认边框颜色 */
	private final int DEFAULT_STROKECOLOR = 0xffeeeeee;

	/** 是否显示边框(左边框、右边框、顶部边框、底部边框) */
	private boolean isLeftVisible;// 左
	private boolean isRightVisible;// 右
	private boolean isTopVisible;// 顶部
	private boolean isBottomVisible;// 底部

	/** 边框边距 */
	private float strokeLeft;// 左边距
	private float strokeRight;// 右边距
	private float strokeTop;// 顶边距
	private float strokeBottom;// 底边距
	private final float DEFAULT_MARGIN = 1.0f;// 默认边距
	protected SurfaceHolder mHolder;
	protected Canvas canvas;

	public GridChartView(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
	}

	public GridChartView(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
		init();
	}

	public GridChartView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		// TODO Auto-generated constructor stub
		init();
	}

	private void init() {
		strokeWidth = DEFAULT_STROKEWIDTH;
		strokeColor = DEFAULT_STROKECOLOR;
		isLeftVisible = true;
		isRightVisible = true;
		isTopVisible = true;
		isBottomVisible = true;
		strokeLeft = DEFAULT_MARGIN;
		strokeRight = DEFAULT_MARGIN;
		strokeTop = DEFAULT_MARGIN;
		strokeBottom = DEFAULT_MARGIN;



	}

	@Override
	protected void dispatchDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		super.dispatchDraw(canvas);
		viewHight = getHeight();
		viewWidth = getWidth();

		// 绘制边框
		drawFrame(canvas);

	}



	//
	// @Override
	// protected void onDraw(Canvas canvas) {
	// // TODO Auto-generated method stub
	// super.onDraw(canvas);
	// viewHight = getHeight();
	// viewWidth = getWidth();
	//
	// // 绘制边框
	// drawFrame(canvas);
	//
	// }

	/**
	 * 绘制边框
	 * 
	 * @param canvas
	 *            画布
	 */
	protected void drawFrame(Canvas canvas) {
		Paint paint = new Paint();
		paint.setStrokeWidth(strokeWidth);
		paint.setColor(strokeColor);
		paint.setAntiAlias(true);

		if (isLeftVisible)
			canvas.drawLine(strokeLeft + strokeWidth / 2, strokeTop, strokeLeft
					+ strokeWidth / 2, viewHight - strokeBottom, paint);// 绘制左边竖线
		if (isRightVisible)
			canvas.drawLine(viewWidth - strokeRight - strokeWidth / 2,
					strokeTop, viewWidth - strokeRight - strokeWidth / 2,
					viewHight - strokeBottom, paint);// 绘制右边竖线
		if (isTopVisible)
			canvas.drawLine(strokeLeft, strokeTop + strokeWidth / 2, viewWidth
					- strokeRight, strokeTop + strokeWidth / 2, paint);// 绘制顶部横线
		if (isBottomVisible)
			canvas.drawLine(strokeLeft, viewHight - strokeBottom - strokeWidth
					/ 2, viewWidth - strokeRight, viewHight - strokeBottom
					- strokeWidth / 2, paint);// 绘制底部横线
	}

	/**
	 * 设置边框宽度
	 * 
	 * @param strokeWidth
	 */
	public GridChartView setStrokeWidth(float strokeWidth) {
		if (strokeWidth < 1) {
			strokeWidth = DEFAULT_STROKEWIDTH;
		}
		this.strokeWidth = strokeWidth;
		return this;
	}

	/**
	 * 获取边框宽度
	 * 
	 * @return strokeWidth
	 */
	public float getStrokeWidth() {
		return strokeWidth;
	}

	/**
	 * 设置边框颜色
	 * 
	 * @param strokeColor
	 */
	public GridChartView setStrokeColor(int strokeColor) {
		this.strokeColor = strokeColor;
		return this;
	}

	/**
	 * 获取边框颜色
	 * 
	 * @return strokeColor
	 */
	public int getStrokeColor() {
		return strokeColor;
	}

	/**
	 * 设置左边框是否显示
	 * 
	 * @param isVisible
	 *            true显示,false不显示
	 * @return
	 */
	public GridChartView setStrokeLeftVisibility(boolean isVisible) {
		this.isLeftVisible = isVisible;
		return this;
	}

	/**
	 * 设置右边框是否显示
	 * 
	 * @param isVisible
	 *            true显示,false不显示
	 * @return
	 */
	public GridChartView setStrokeRightVisibility(boolean isVisible) {
		this.isRightVisible = isVisible;
		return this;
	}

	/**
	 * 设置顶部边框是否显示
	 * 
	 * @param isVisible
	 *            true显示,false不显示
	 * @return
	 */
	public GridChartView setStrokeTopVisibility(boolean isVisible) {
		this.isTopVisible = isVisible;
		return this;
	}

	/**
	 * 设置底部边框是否显示
	 * 
	 * @param isVisible
	 *            true显示,false不显示
	 * @return
	 */
	public GridChartView setStrokeBottomVisibility(boolean isVisible) {
		this.isBottomVisible = isVisible;
		return this;
	}

	/**
	 * 设置边框左边距
	 * 
	 * @param margin
	 * @return
	 */
	public GridChartView setStrokeLeft(float margin) {
		if (margin < 1) {
			margin = DEFAULT_MARGIN;
		}
		this.strokeLeft = margin;
		return this;
	}

	/**
	 * 获取边框左边距
	 * 
	 * @return
	 */
	public float getStrokeLeft() {
		return strokeLeft;
	}

	/**
	 * 设置边框右边距
	 * 
	 * @param margin
	 * @return
	 */
	public GridChartView setStrokeRight(float margin) {
		if (margin < 1) {
			margin = DEFAULT_MARGIN;
		}
		this.strokeRight = margin;
		return this;
	}

	/**
	 * 获取边框右边距
	 * 
	 * @return
	 */
	public float getStrokeRight() {
		return strokeRight;
	}

	/**
	 * 设置边框顶边距
	 * 
	 * @param margin
	 * @return
	 */
	public GridChartView setStrokeTop(float margin) {
		if (margin < 1) {
			margin = DEFAULT_MARGIN;
		}
		this.strokeTop = margin;
		return this;
	}

	/**
	 * 获取边框顶边距
	 * 
	 * @return
	 */
	public float getStrokeTop() {
		return strokeTop;
	}

	/**
	 * 设置边框底边距
	 * 
	 * @param margin
	 * @return
	 */
	public GridChartView setStrokeBottom(float margin) {
		if (margin < 1) {
			margin = DEFAULT_MARGIN;
		}
		this.strokeBottom = margin;
		return this;
	}

	/**
	 * 获取边框底边距
	 * 
	 * @return
	 */
	public float getStrokeBottom() {
		return strokeBottom;
	}

}
作者 east
Java 12月 7,2020

汉字转换位汉语拼音工具类

public final class Cn2Spell {

private Cn2Spell() {

}

/**
* 汉字转换位为语拼音首字母,英文字符不变
*
*
@param chines 汉字
*
@return 拼音
*/
public static String converterToFirstSpell(String chines) {
if (StringUtils.isBlank(chines)) {
return chines;
}
String pinyinName = "";
char[] nameChar = chines.toCharArray();
HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
for (int i = 0; i < nameChar.length; i++) {
if (nameChar[i] > 128) {
try {
pinyinName += PinyinHelper.toHanyuPinyinStringArray(nameChar[i], defaultFormat)[0].charAt(0);
} catch (BadHanyuPinyinOutputFormatCombination e) {
e.printStackTrace();
}
} else {
pinyinName += nameChar[i];
}
}
return pinyinName.toUpperCase();
}

/**
* 汉字转换为汉语拼音,英文字符不变
*
*
@param chines 汉字
*
@return 拼音
*/
public static String converterToSpell(String chines) {
if (StringUtils.isBlank(chines)) {
return chines;
}
String pinyinName = "";
int index = 0;
char[] nameChar = chines.toCharArray();
HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
for (int i = 0; i < nameChar.length; i++) {
if (nameChar[i] > 128) {
try {
if (index == 0 && i != 0) {
pinyinName += "_" + PinyinHelper.toHanyuPinyinStringArray(nameChar[i], defaultFormat)[0];
} else {
pinyinName += PinyinHelper.toHanyuPinyinStringArray(nameChar[i], defaultFormat)[0];
}
index++;
} catch (BadHanyuPinyinOutputFormatCombination e) {
e.printStackTrace();
}
} else {
if (index > 0) {
pinyinName += "_" + nameChar[i];
index = 0;
} else {
pinyinName += nameChar[i];
}

}
}
return pinyinName.toUpperCase();
}

/**
* 判断是否有中文
*
*
@param str
*
@return
*/
public static boolean isChineseChar(String str) {
boolean temp = false;
// 即[\\u4e00-\\u9fa5]字符,因代码检查不通过,需如下处理
StringBuilder sb = new StringBuilder();
sb.append("[\\u").append("4e00-\\u").append("9fa5]");

Pattern p = Pattern.compile(sb.toString());
Matcher m = p.matcher(str);

if (m.find()) {
temp = true;
}
return temp;
}
}
作者 east
Java 12月 7,2020

中文转数字Java工具类


import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;


public final class CnNumericToArabicUtil {

/**
* 无参构造函数
*/
private CnNumericToArabicUtil() {
}

/**
* 中文数字数组
*/
private static final Character[] CN_NUMERIC = { '一', '二', '三', '四', '五', '六', '七', '八', '九', '壹', '贰', '叁', '肆',
'伍', '陆', '柒', '捌', '玖', '○', 'O', '零', '十', '百', '千', '拾', '佰', '仟', '万', '亿' };

private static Map<Character, Integer> cnNumeric = null;

static {
cnNumeric = new HashMap<Character, Integer>(40, 0.85f);
for (int j = 0; j < 9; j++) {
cnNumeric.put(CN_NUMERIC[j], j + 1);
}
for (int j = 9; j < 18; j++) {
cnNumeric.put(CN_NUMERIC[j], j - 8);
}
for (int j = 18; j < 21; j++) {
cnNumeric.put(CN_NUMERIC[j], 0);
}

cnNumeric.put(' ', 0);
cnNumeric.put('两', 2);
cnNumeric.put('十', 10);
cnNumeric.put('拾', 10);
cnNumeric.put('百', 100);
cnNumeric.put('佰', 100);
cnNumeric.put('千', 1000);
cnNumeric.put('仟', 1000);
cnNumeric.put('万', 10000);
cnNumeric.put('亿', 100000000);
}

/**
* 中文数字转换为阿拉伯数字<br>
* TODO 该方法不完善的地方为只能纯的中文数字或者纯的阿拉伯数字,如果互相掺杂,得到的结果不准确,后期再完善<br>
*
*
@param cn 中文数字
*
@return int
*/
public static int cnNumericToArabic(String cn) {

// 中文数字参数为空判断,为空时返回0
if (StringUtils.isEmpty(cn)) {
return 0;
}

cn = cn.trim();

// 阿拉伯数字,类型转换后,直接返回结果
if (NumberUtils.isNumber(cn)) {
return Integer.parseInt(cn);
}

if (cn.length() == 1) {
return isCnNumeric(cn.charAt(0));
}

cn = cn.replace('佰', '百').replace('仟', '千').replace('拾', '十').replace('零', ' ');

// 结果值
int val = 0;

// 根据中文单位,将中文数字转换为阿拉伯数字,得到结果数组
// 亿
String[] cnNumericToArabicByCnUnitResults = cnNumericToArabicByCnUnit(cn, '亿', 100000000);
cn = cnNumericToArabicByCnUnitResults[0];
val += Integer.parseInt(cnNumericToArabicByCnUnitResults[1]);

// 万
cnNumericToArabicByCnUnitResults = cnNumericToArabicByCnUnit(cn, '万', 10000);
cn = cnNumericToArabicByCnUnitResults[0];
val += Integer.parseInt(cnNumericToArabicByCnUnitResults[1]);

// 千
cnNumericToArabicByCnUnitResults = cnNumericToArabicByCnUnit(cn, '千', 1000);
cn = cnNumericToArabicByCnUnitResults[0];
val += Integer.parseInt(cnNumericToArabicByCnUnitResults[1]);

// 百
cnNumericToArabicByCnUnitResults = cnNumericToArabicByCnUnit(cn, '百', 100);
cn = cnNumericToArabicByCnUnitResults[0];
val += Integer.parseInt(cnNumericToArabicByCnUnitResults[1]);

// 十
int ten = -1;
ten = cn.lastIndexOf('十');
if (ten > -1) {
if (ten == 0) {
val += 1 * 10;
} else {
val += cnNumericToArabic(cn.substring(0, ten)) * 10;
}
if (ten < cn.length() - 1) {
cn = cn.substring(ten + 1, cn.length());
} else {
cn = "";
}
}

cn = cn.trim();
for (int j = 0; j < cn.length(); j++) {
val += isCnNumeric(cn.charAt(j));
}
return val;
}

/**
* 中文数字转阿拉伯数字<BR>
* 如果字符串中文数字表达不全或错误,则只会解析部分(例如三十二百五六返回32)
*
*
@param numStr 待转换的字符串
*
@param numArrays 阿拉伯数值数组
*
@return
*/
public static String cnNumericToArabic(String numStr, char[] numArrays) {

if (StringUtils.isEmpty(numStr)) {
return "";
}

StringBuffer strRs = new StringBuffer();
boolean isFirst = (null == numArrays || numArrays.length == 0);
numStr = filterStr(numStr);
Pattern pattern = Pattern.compile("^\\d+$");

if (pattern.matcher(numStr).find()) {
return numStr;
}
char[] args = numStr.toCharArray();
int index = 0;
for (int i = UNITS_2.length - 1; i > 0; i--) {
index = numStr.indexOf(UNITS_2[i]);

if (index > 0) {
if (null == numArrays || numArrays.length == 0) {
numArrays = new char[Arrays.binarySearch(UNITS_2, args[index]) * 4];
Arrays.fill(numArrays, '0');
}
cnNumericToArabic(numStr.substring(index), numArrays);
break;
}
}

try {
process(numStr, numArrays, strRs, isFirst, index);

} catch (Exception e) {
return "";
}
// 避免只有单位没数字 比如十三这个“十”单位前没数字,根据习惯默认为一
return strRs.toString().replaceAll("[十,百,千,万,亿]", "1");
}

/**
* 检查是否为中文字符
*
*
@param ch 中文字符
*
@return boolean true:是中文数字;false:不是中文数字
*/
private static int isCnNumeric(char ch) {
Integer i = cnNumeric.get(ch);
if (i == null) {
return 0;
}
return i.intValue();
}

/**
* 根据中文单位,将中文数字转换为阿拉伯数字
*
*
@param cn 中文数字
*
@param cnUnit 中文单位,如:亿、万、千、百、十
*
@param unitVal 单位值,如:亿-100000000
*
@return String[] 字符串数组,格式为:{处理后的中文数字、结果值}
*/
private static String[] cnNumericToArabicByCnUnit(String cn, char cnUnit, int unitVal) {
// 中文数字为空判断,当为空时直接返回空字符串和结果值为0的数组
if (StringUtils.isEmpty(cn)) {
return new String[] { "", "0" };
}

// 结果值
int val = 0;
// 中文单位所在位置
int unitPos = cn.lastIndexOf(cnUnit);
if (unitPos > -1) {
// 中文数字转换为阿拉伯数字
val += cnNumericToArabic(cn.substring(0, unitPos)) * unitVal;
if (unitPos < cn.length() - 1) {
cn = cn.substring(unitPos + 1, cn.length());
} else {
cn = "";
}

if (cn.length() == 1) {
int arbic = isCnNumeric(cn.charAt(0));
if (arbic <= 10) {
val += arbic * unitVal * 0.1;
}
cn = "";
}
}
return new String[] { cn, "" + val };
}

/** 中文数值 */
private static final Character[] CN_NUMBER_1 = { 'O', '一', '二', '三', '四', '五', '六', '七', '八', '九' };

/** 中文数值 */
private static final Character[] CN_NUMBER_2 = { '零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖' };

/** 阿拉伯数值 */
private static final Character[] ARABIC_NUMBER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };

/** 用于辅助定位单位 */
private static final String UNITS_1_STR = " 十百千";

/** 单位一 */
private static final Character[] UNITS_1 = { ' ', '十', '百', '千' };

/** 单位一的同义词 */
private static final Character[] UNITS_1_T = { ' ', '拾', '佰', '仟' };

/** 单位2 */
private static final Character[] UNITS_2 = { ' ', '万', '亿' };

/**
* 从字符串中提取满足当前可转换为阿拉伯数字的字符串
*
*
@param str 待转换的字符串
*
@return
*/
public static String getCnNumericStr(String str) {
StringBuffer regx = new StringBuffer("([");
for (Character c : CN_NUMBER_1) {
regx.append(c.charValue());
}
for (Character c : CN_NUMBER_2) {
regx.append(c.charValue());
}
for (Character c : ARABIC_NUMBER) {
regx.append(c.charValue());
}
for (Character c : UNITS_1) {
regx.append(String.valueOf(c.charValue()).trim());
}
for (Character c : UNITS_1_T) {
regx.append(String.valueOf(c.charValue()).trim());
}
for (Character c : UNITS_2) {
regx.append(String.valueOf(c.charValue()).trim());
}
regx.append("两Oo○");
regx.append("]+)");
Pattern pattern = Pattern.compile(regx.toString());
Matcher matcher = pattern.matcher(str);
if (matcher.find()) {
return matcher.group();
}
return "";
}

/**
* 从字符串中提取满足当前可转换为阿拉伯数字的字符串,并将转换后的中文数字用阿拉伯数字替换掉原来字符串中的数值,并且返回
*
*
@param str 待转换的字符串
*
@return
*/
public static String transCnNumericStr(String str) {
if (StringUtils.isEmpty(str)) {
return "";
}
StringBuffer regx = new StringBuffer("([");
for (Character c : CN_NUMBER_1) {
regx.append(c.charValue());
}
for (Character c : CN_NUMBER_2) {
regx.append(c.charValue());
}
for (Character c : ARABIC_NUMBER) {
regx.append(c.charValue());
}
for (Character c : UNITS_1) {
regx.append(String.valueOf(c.charValue()).trim());
}
for (Character c : UNITS_1_T) {
regx.append(String.valueOf(c.charValue()).trim());
}
for (Character c : UNITS_2) {
regx.append(String.valueOf(c.charValue()).trim());
}
regx.append("两Oo○");
regx.append("]+)");
Pattern pattern = Pattern.compile(regx.toString());

Matcher matcher = pattern.matcher(str);
String value = "";
if (matcher.find()) {
value = matcher.group();
}
return str.replaceFirst(regx.toString(), CnNumericToArabicUtil.cnNumericToArabic(value, null));
}

/**
* 进行解析处理
*
*
@param numStr 需要解析的数值字符串
*
@param numArrays 数值对应的数组
*
@param strRs 结果
*
@param isFirst 是否第一次遍历
*
@param index 分隔索引
*
@throws Exception
*/
private static void process(String numStr, char[] numArrays, StringBuffer strRs, boolean isFirst, int index)
throws Exception {
char[] args;

if (isFirst) {
firstParse(numStr, numArrays, strRs, index);
} else {
// 大于万
if (index > 0) {
args = numStr.substring(0, index).toCharArray();
numStr = numStr.substring(0, index);
} else {
args = numStr.toCharArray();
}
// 找到起始位置
int start = numArrays.length - Arrays.binarySearch(UNITS_2, args[0]) * 4;

for (int i = 1, j = 0; i < UNITS_1.length; i++) {
j = numStr.indexOf(UNITS_1[i]);
// 包含十, 百, 千单位
if (j > 0) {
numArrays[start + (3 - i)] = args[j - 1];

if (i == 1 && j + 1 < args.length) {
numArrays[start + (3 - i) + 1] = args[j + 1];
}
} else {
// 找上级(比如十没找到,则找百)
for (int ii = 1 + i; ii < UNITS_1.length; ii++) {
j = numStr.indexOf(UNITS_1[ii]);
// 找到上级
if (j > 0) {
numArrays[start + (3 - ii)] = args[j - 1];
break;
}
}
// 如果没有任何上级,且当前索引必须小于千,则按十-千填入数字
if (j < 0 && args.length - 1 - i >= 0 && i < 3) {
// 填入当前位置的数值
if (NumberUtils.isNumber(String.valueOf(args[args.length - 1 - i]))) {
numArrays[start + (3 - i)] = args[args.length - 1 - i];
}
// 填入当前位置后面的数值
if (NumberUtils.isNumber(String.valueOf(args[args.length - i]))) {
numArrays[start + (3 - i) + 1] = args[args.length - i];
}
}
}
}
}
}

/**
* 第一次解析
*
*
@param numStr 需要解析的数值字符串
*
@param numArrays 数值对应的数组
*
@param strRs 结果
*
@param index 为万级别的索引
*/
private static void firstParse(String numStr, char[] numArrays, StringBuffer strRs, int index) {
char[] args;
// 数值不超过5位
if (index <= 0) {
index = numStr.length();
}
args = numStr.substring(0, index).toCharArray();

if (null != args && args.length > 1) {
// 第二位为单位(十百千)
int k = UNITS_1_STR.indexOf(args[1]);
// 此位为单位(十百千),则创建此段空数组,准备填入数值
if (k > 0) {
char[] arrays = new char[k + 1];
// 默认为0
Arrays.fill(arrays, '0');
// 从十百千依次开始
for (int i = 1, j = 0; k > 0 && i < UNITS_1.length; i++, k--) {
j = numStr.substring(0, index).indexOf(UNITS_1[i]);
// 此字符串包含十百千,j的前一位肯定是数字
if (j > 0) {
// 在对应的数组位置填上其数值
arrays[arrays.length - 1 - i] = args[j - 1];
// i为1说明当前处于十位,则需要补全个位数
if (i == 1 && j + 1 < args.length) {
arrays[arrays.length - i] = args[j + 1];
}
// 没找到单位
} else {
// 找上级(比如十没找到,则找百)
for (int ii = 1 + i; ii < UNITS_1.length; ii++) {
j = numStr.substring(0, index).indexOf(UNITS_1[ii]);
// 找到上级
if (j > 0) {
// 如果上级索引位置后面还有数字个数大于等于理论上的数字减一,则填入当前索引位的最后一位
if ((args.length - j - 1) >= (ii - 1)) {
arrays[arrays.length - i] = args[args.length - i];
}
}

}
}
}

strRs.append(String.valueOf(arrays));

if (null != numArrays) {
strRs.append(String.valueOf(numArrays));
}
} else {
// 不规则的情况(单位前没数字) 十二、百三十
strRs.append(numStr.substring(0, index));
if (null != numArrays) {
strRs.append(String.valueOf(numArrays));
}
}
} else {
// 解析位只有1位,则判断其是否为单位属性
if (null != args && args.length == 1) {
int k = UNITS_1_STR.indexOf(args[0]);
if (k > 0) {
char[] arrays = new char[k + 1];
Arrays.fill(arrays, '0');
arrays[0] = '1';
strRs.append(String.valueOf(arrays));
}
}
// 如果不包含单位,则直接添加此数值
if (!UNITS_1_STR.contains(numStr.substring(0, index))) {
strRs.append(numStr.substring(0, index));
}

if (null != numArrays) {
strRs.append(String.valueOf(numArrays));
}
}
}

/**
* 将中文数值转换为阿拉伯数字
*
*
@param numStr 待过滤的字符串
*
@return
*/
private static String filterStr(String numStr) {
numStr = numStr.replace('O', ARABIC_NUMBER[0].charValue());
numStr = numStr.replace('○', ARABIC_NUMBER[0].charValue());
numStr = numStr.replace('两', ARABIC_NUMBER[2].charValue());
for (int i = 0; i < ARABIC_NUMBER.length; i++) {
numStr = numStr.replace(CN_NUMBER_1[i].charValue(), ARABIC_NUMBER[i].charValue());
numStr = numStr.replace(CN_NUMBER_2[i].charValue(), ARABIC_NUMBER[i].charValue());
}
for (int i = 1; i < UNITS_1.length; i++) {
numStr = numStr.replace(UNITS_1_T[i].charValue(), UNITS_1[i].charValue());
}
return numStr;
}
}
作者 east

上一 1 2 3 下一个

关注公众号“大模型全栈程序员”回复“小程序”获取1000个小程序打包源码。回复”chatgpt”获取免注册可用chatgpt。回复“大数据”获取多本大数据电子书

标签

AIGC AI创作 bert chatgpt github GPT-3 gpt3 GTP-3 hive mysql O2O tensorflow UI控件 不含后台 交流 共享经济 出行 图像 地图定位 外卖 多媒体 娱乐 小程序 布局 带后台完整项目 开源项目 搜索 支付 效率 教育 日历 机器学习 深度学习 物流 用户系统 电商 画图 画布(canvas) 社交 签到 联网 读书 资讯 阅读 预订

官方QQ群

小程序开发群:74052405

大数据开发群: 952493060

近期文章

  • 详解Python当中的pip常用命令
  • AUTOSAR如何在多个供应商交付的配置中避免ARXML不兼容?
  • C++thread pool(线程池)设计应关注哪些扩展性问题?
  • 各类MCAL(Microcontroller Abstraction Layer)如何与AUTOSAR工具链解耦?
  • 如何设计AUTOSAR中的“域控制器”以支持未来扩展?
  • C++ 中避免悬挂引用的企业策略有哪些?
  • 嵌入式电机:如何在低速和高负载状态下保持FOC(Field-Oriented Control)算法的电流控制稳定?
  • C++如何在插件式架构中使用反射实现模块隔离?
  • C++如何追踪内存泄漏(valgrind/ASan等)并定位到业务代码?
  • C++大型系统中如何组织头文件和依赖树?

文章归档

  • 2025年6月
  • 2025年5月
  • 2025年4月
  • 2025年3月
  • 2025年2月
  • 2025年1月
  • 2024年12月
  • 2024年11月
  • 2024年10月
  • 2024年9月
  • 2024年8月
  • 2024年7月
  • 2024年6月
  • 2024年5月
  • 2024年4月
  • 2024年3月
  • 2023年11月
  • 2023年10月
  • 2023年9月
  • 2023年8月
  • 2023年7月
  • 2023年6月
  • 2023年5月
  • 2023年4月
  • 2023年3月
  • 2023年1月
  • 2022年11月
  • 2022年10月
  • 2022年9月
  • 2022年8月
  • 2022年7月
  • 2022年6月
  • 2022年5月
  • 2022年4月
  • 2022年3月
  • 2022年2月
  • 2022年1月
  • 2021年12月
  • 2021年11月
  • 2021年9月
  • 2021年8月
  • 2021年7月
  • 2021年6月
  • 2021年5月
  • 2021年4月
  • 2021年3月
  • 2021年2月
  • 2021年1月
  • 2020年12月
  • 2020年11月
  • 2020年10月
  • 2020年9月
  • 2020年8月
  • 2020年7月
  • 2020年6月
  • 2020年5月
  • 2020年4月
  • 2020年3月
  • 2020年2月
  • 2020年1月
  • 2019年7月
  • 2019年6月
  • 2019年5月
  • 2019年4月
  • 2019年3月
  • 2019年2月
  • 2019年1月
  • 2018年12月
  • 2018年7月
  • 2018年6月

分类目录

  • Android (73)
  • bug清单 (79)
  • C++ (34)
  • Fuchsia (15)
  • php (4)
  • python (43)
  • sklearn (1)
  • 云计算 (20)
  • 人工智能 (61)
    • chatgpt (21)
      • 提示词 (6)
    • Keras (1)
    • Tensorflow (3)
    • 大模型 (1)
    • 智能体 (4)
    • 深度学习 (14)
  • 储能 (44)
  • 前端 (4)
  • 大数据开发 (488)
    • CDH (6)
    • datax (4)
    • doris (30)
    • Elasticsearch (15)
    • Flink (78)
    • flume (7)
    • Hadoop (19)
    • Hbase (23)
    • Hive (40)
    • Impala (2)
    • Java (71)
    • Kafka (10)
    • neo4j (5)
    • shardingsphere (6)
    • solr (5)
    • Spark (99)
    • spring (11)
    • 数据仓库 (9)
    • 数据挖掘 (7)
    • 海豚调度器 (10)
    • 运维 (34)
      • Docker (3)
  • 小游戏代码 (1)
  • 小程序代码 (139)
    • O2O (16)
    • UI控件 (5)
    • 互联网类 (23)
    • 企业类 (6)
    • 地图定位 (9)
    • 多媒体 (6)
    • 工具类 (25)
    • 电商类 (22)
    • 社交 (7)
    • 行业软件 (7)
    • 资讯读书 (11)
  • 嵌入式 (70)
    • autosar (63)
    • RTOS (1)
    • 总线 (1)
  • 开发博客 (16)
    • Harmony (9)
  • 技术架构 (6)
  • 数据库 (32)
    • mongodb (1)
    • mysql (13)
    • pgsql (2)
    • redis (1)
    • tdengine (4)
  • 未分类 (6)
  • 程序员网赚 (20)
    • 广告联盟 (3)
    • 私域流量 (5)
    • 自媒体 (5)
  • 量化投资 (4)
  • 面试 (14)

功能

  • 登录
  • 文章RSS
  • 评论RSS
  • WordPress.org

All Rights Reserved by Gitweixin.本站收集网友上传代码, 如有侵犯版权,请发邮件联系yiyuyos@gmail.com删除.