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

Android自定义富文本控件

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

  • 首页   /  
  • 作者: east
  • ( 页面70 )
Android 12月 11,2020

Android自定义富文本控件

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Parcel;
import android.support.annotation.IntDef;
import android.support.annotation.IntRange;
import android.support.annotation.Nullable;
import android.text.Html;
import android.text.Layout;
import android.text.Selection;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.method.Touch;
import android.text.style.ClickableSpan;
import android.text.style.ImageSpan;
import android.text.style.URLSpan;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;

import com.bumptech.glide.load.resource.drawable.GlideDrawable;
import com.bumptech.glide.request.Request;
import com.bumptech.glide.request.animation.GlideAnimation;
import com.bumptech.glide.request.target.SimpleTarget;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import pl.droidsonroids.gif.GifDrawable;

public class RichTextView extends GifSpanTextView implements Drawable.Callback, View.OnAttachStateChangeListener {

private static Pattern IMAGE_TAG_PATTERN = Pattern.compile("\\<img(.*?)\\>");
private static Pattern IMAGE_WIDTH_PATTERN = Pattern.compile("width=\"(.*?)\"");
private static Pattern IMAGE_HEIGHT_PATTERN = Pattern.compile("height=\"(.*?)\"");
private static Pattern IMAGE_SRC_PATTERN = Pattern.compile("src=\"(.*?)\"");
private static Pattern IMAGE_BIG_SRC_PATTERN = Pattern.compile("bigsrc=\"(.*?)\"");
private static Pattern IMAGE_HREF_PATTERN = Pattern.compile("href=\"(.*?)\"");

private Drawable placeHolder, errorImage;//占位图,错误图
private OnImageClickListener onImageClickListener;//图片点击回调
private OnURLClickListener onURLClickListener;//超链接点击回调
//    private HashSet<Target> targets;

// private HashSet targets;
private HashMap mImages;
private HashMap mBigImages;
private ImageFixListener mImageFixListener;
private int d_w = 200;
private int d_h = 100;
private boolean isScroll = true;

private String imageHost;
private boolean dontConsumeNonUrlClicks = true;
private boolean linkHit;
private Context mContext;
private GlideImageGeter glideImageGeter;

public RichTextView(Context context) {
    this(context, null);
}

public RichTextView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

public RichTextView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);

    init(context, attrs);
}

private void init(Context context, AttributeSet attrs) {
    mContext = context;
 //   targets = new HashSet<>();
    TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RichTextView);
    placeHolder = typedArray.getDrawable(R.styleable.RichTextView_placeHolder);
    errorImage = typedArray.getDrawable(R.styleable.RichTextView_errorImage);

    d_w = typedArray.getDimensionPixelSize(R.styleable.RichTextView_defaultWidth, Utils.Dp2Px(context, d_w));
    d_h = typedArray.getDimensionPixelSize(R.styleable.RichTextView_defaultHeight, Utils.Dp2Px(context, d_h));

    isScroll = typedArray.getBoolean(R.styleable.RichTextView_isScroll, isScroll);

    if (placeHolder == null) {
        placeHolder = new ColorDrawable(Color.GRAY);
    }
    placeHolder.setBounds(0, 0, d_w, d_h);
    if (errorImage == null) {
        errorImage = new ColorDrawable(Color.GRAY);
    }
    errorImage.setBounds(0, 0, d_w, d_h);
    typedArray.recycle();
}


/**
 * 设置富文本
 *
 * @param text 富文本
 */
public void setRichText(String text) {
    try {
        glideImageGeter = new GlideImageGeter(getContext(), this);
     //   targets.clear();
        matchImages(text);

        if (text != null) {
            text = text.replaceAll(" ", "\t");  //替换空格
        }
        Spanned spanned = Html.fromHtml(text, glideImageGeter, null);
        SpannableStringBuilder spannableStringBuilder;
        if (spanned instanceof SpannableStringBuilder) {
            spannableStringBuilder = (SpannableStringBuilder) spanned;
        } else {
            spannableStringBuilder = new SpannableStringBuilder(spanned);
        }

        // 处理图片得点击事件
        ImageSpan[] imageSpans = spannableStringBuilder.getSpans(0, spannableStringBuilder.length(), ImageSpan.class);
        final List<String> imageUrls = new ArrayList<>();

        for (int i = 0, size = imageSpans.length; i < size; i++) {
            ImageSpan imageSpan = imageSpans[i];
            String imageUrl = imageSpan.getSource();
            String bigImageUrl = mBigImages.get(imageUrl);
            if (!TextUtils.isEmpty(bigImageUrl)) {
                imageUrl = bigImageUrl;
            }
            int start = spannableStringBuilder.getSpanStart(imageSpan);
            int end = spannableStringBuilder.getSpanEnd(imageSpan);
            imageUrl = imageUrl.startsWith("http") ? imageUrl : imageHost + imageUrl;
            if (imageUrl.endsWith(".gif") || imageUrl.endsWith(".bmp") || imageUrl.endsWith(".jpg") || imageUrl.endsWith(".jpeg") || imageUrl.endsWith(".JPG") || imageUrl.endsWith(".JPEG") || imageUrl.endsWith(".png") || imageUrl.endsWith(".PNG")) {
                imageUrls.add(imageUrl);
            }
            final int finalI = i;
            ClickableSpan clickableSpan = new ClickableSpan() {
                @Override
                public void onClick(View widget) {
                    if (onImageClickListener != null) {
                        onImageClickListener.imageClicked(imageUrls, finalI);
                    }
                }
            };
            ClickableSpan[] clickableSpans = spannableStringBuilder.getSpans(start, end, ClickableSpan.class);
            if (clickableSpans != null && clickableSpans.length != 0) {
                for (ClickableSpan cs : clickableSpans) {
                    spannableStringBuilder.removeSpan(cs);
                }
            }
            spannableStringBuilder.setSpan(clickableSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        }

        // 处理超链接点击事件
        URLSpan[] urlSpans = spannableStringBuilder.getSpans(0, spannableStringBuilder.length(), URLSpan.class);

        for (int i = 0, size = urlSpans == null ? 0 : urlSpans.length; i < size; i++) {
            URLSpan urlSpan = urlSpans[i];

            int start = spannableStringBuilder.getSpanStart(urlSpan);
            int end = spannableStringBuilder.getSpanEnd(urlSpan);

            spannableStringBuilder.removeSpan(urlSpan);
            spannableStringBuilder.setSpan(new CallableURLSpan(urlSpan.getURL(), onURLClickListener), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        }

        //表情转义
        final Matcher matcher = EmojiHelper.getInstance().getEmojiMatcher(spanned.toString());

        while (matcher.find()) {
            final String name = matcher.group(1);

            final GifDrawable drawable = EmojiHelper.getInstance().getGifDrawable(name);
            if (drawable != null) {
                final ImageSpan gifSpan = new GifImageSpan(drawable);
                spannableStringBuilder.setSpan(gifSpan, matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            }
        }

        super.setText(spanned);
        if (isScroll) {
            setMovementMethod(LocalLinkMovementMethod.getInstance());    //解决ListView里TextView设置LinkMovementMethod后导致其ItemClick失效的问题
        }
        setLongClickable(false);    //屏蔽长按事件,防止部分手机Spannable长按时崩溃
    } catch (Exception ex) {
        ex.printStackTrace();
    }
}

/*
private void addTarget(ImageTarget target) {
targets.add(target);
}
/ /*
* 从文本中拿到标签,并获取图片url和宽高
*/
private void matchImages(String text) {
mImages = new HashMap<>();
mBigImages = new HashMap<>();
ImageHolder holder;
Matcher imageMatcher, srcMatcher, bigSrcMatcher, widthMatcher, heightMatcher, hrefMatcher;
int position = 0;
hrefMatcher = IMAGE_HREF_PATTERN.matcher(text);
String hrefImageUrl = null;
if (hrefMatcher.find()) {
hrefImageUrl = getTextBetweenQuotation(hrefMatcher.group().trim().substring(4));
}
imageMatcher = IMAGE_TAG_PATTERN.matcher(text);
while (imageMatcher.find()) {
String image = imageMatcher.group().trim();
srcMatcher = IMAGE_SRC_PATTERN.matcher(image);
String src = null;
if (srcMatcher.find()) {
src = getTextBetweenQuotation(srcMatcher.group().trim().substring(4));
}
if (TextUtils.isEmpty(src)) {
continue;
}
bigSrcMatcher = IMAGE_BIG_SRC_PATTERN.matcher(image);
String bigSrc = null;
if (bigSrcMatcher.find()) {
bigSrc = getTextBetweenQuotation(bigSrcMatcher.group().trim().substring(4));
}
if (TextUtils.isEmpty(bigSrc)) {
if (!TextUtils.isEmpty(hrefImageUrl)) {
bigSrc = hrefImageUrl;
} else {
bigSrc = src;
}
}

        holder = new ImageHolder(src, bigSrc, position);
        widthMatcher = IMAGE_WIDTH_PATTERN.matcher(image);
        if (widthMatcher.find()) {
            holder.width = parseStringToInteger(getTextBetweenQuotation(widthMatcher.group().trim().substring(6)));
        }

        heightMatcher = IMAGE_HEIGHT_PATTERN.matcher(image);
        if (heightMatcher.find()) {
            holder.height = parseStringToInteger(getTextBetweenQuotation(heightMatcher.group().trim().substring(6)));
        }

        mImages.put(holder.src, holder);
        mBigImages.put(holder.src, holder.bigSrc);
        position++;
    }
}

private int parseStringToInteger(String integerStr) {
    int result = -1;
    if (!TextUtils.isEmpty(integerStr)) {
        try {
            result = Integer.parseInt(integerStr);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    return result;
}

/**
 * 从双引号之间取出字符串
 */
@Nullable
private static String getTextBetweenQuotation(String text) {
    Pattern pattern = Pattern.compile("\"(.*?)\"");
    Matcher matcher = pattern.matcher(text);
    if (matcher.find()) {
        return matcher.group(1);
    }
    return null;
}

  


@Override
public boolean onTouchEvent(MotionEvent event) {
linkHit = false;
boolean res = super.onTouchEvent(event);

    if (dontConsumeNonUrlClicks)
        return linkHit;
    return res;

}

public static class LocalLinkMovementMethod extends LinkMovementMethod {
    static LocalLinkMovementMethod sInstance;


    public static LocalLinkMovementMethod getInstance() {
        if (sInstance == null)
            sInstance = new LocalLinkMovementMethod();

        return sInstance;
    }


    @Override
    public boolean onTouchEvent(TextView widget,
                                Spannable buffer, MotionEvent event) {
        int action = event.getAction();

        if (action == MotionEvent.ACTION_UP ||
                action == MotionEvent.ACTION_DOWN) {
            int x = (int) event.getX();
            int y = (int) event.getY();

            x -= widget.getTotalPaddingLeft();
            y -= widget.getTotalPaddingTop();

            x += widget.getScrollX();
            y += widget.getScrollY();

            Layout layout = widget.getLayout();
            int line = layout.getLineForVertical(y);
            int off = layout.getOffsetForHorizontal(line, x);

            ClickableSpan[] link = buffer.getSpans(
                    off, off, ClickableSpan.class);

            if (link.length != 0) {
                if (action == MotionEvent.ACTION_UP) {
                    link[0].onClick(widget);
                } else if (action == MotionEvent.ACTION_DOWN) {
                    Selection.setSelection(buffer,
                            buffer.getSpanStart(link[0]),
                            buffer.getSpanEnd(link[0]));
                }

                if (widget instanceof RichTextView) {
                    ((RichTextView) widget).linkHit = true;
                }
                return true;

            } else {
                Selection.removeSelection(buffer);
                Touch.onTouchEvent(widget, buffer, event);
                return false;
            }
        }
        return Touch.onTouchEvent(widget, buffer, event);
    }
}


private static final class URLDrawable extends Drawable {
    private Drawable drawable;

    @Override
    public void setAlpha(@IntRange(from = 0, to = 255) int alpha) {

    }

    @Override
    public void setColorFilter(@Nullable ColorFilter colorFilter) {

    }

    @Override
    public int getOpacity() {
        return 0;
    }

    @Override
    public void draw(Canvas canvas) {
        if (drawable != null)
            drawable.draw(canvas);
    }

    public void setDrawable(Drawable drawable) {
        this.drawable = drawable;
    }
}

private static class CallableURLSpan extends URLSpan {

    private OnURLClickListener onURLClickListener;

    public CallableURLSpan(String url, OnURLClickListener onURLClickListener) {
        super(url);
        this.onURLClickListener = onURLClickListener;
    }

    @SuppressWarnings("unused")
    public CallableURLSpan(Parcel src, OnURLClickListener onURLClickListener) {
        super(src);
        this.onURLClickListener = onURLClickListener;
    }

    @Override
    public void onClick(View widget) {
        if (onURLClickListener != null && onURLClickListener.urlClicked(getURL())) {
            return;
        }
        super.onClick(widget);
    }
}

public static class ImageHolder {
    public static final int DEFAULT = 0;
    public static final int CENTER_CROP = 1;
    public static final int CENTER_INSIDE = 2;

    @IntDef({DEFAULT, CENTER_CROP, CENTER_INSIDE})
    public @interface ScaleType {
    }

    private final String src;
    private final String bigSrc;
    private final int position;
    private int width = -1, height = -1;
    private int scaleType = DEFAULT;

    public ImageHolder(String src, String bigSrc, int position) {
        this.src = src;
        this.bigSrc = bigSrc;
        this.position = position;
    }

    public String getSrcUrl() {
        return src;
    }

    public String getBigSrcUrl() {
        return bigSrc;
    }

    public int getHeight() {
        return height;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    @ScaleType
    public int getScaleType() {
        return scaleType;
    }

    public void setScaleType(@ScaleType int scaleType) {
        this.scaleType = scaleType;
    }
}

@SuppressWarnings("unused")
public ImageHolder getImageHolder(String url) {
    return mImages.get(url);
}

@SuppressWarnings("unused")
public void setPlaceHolder(Drawable placeHolder) {
    this.placeHolder = placeHolder;
    this.placeHolder.setBounds(0, 0, d_w, d_h);
}

@SuppressWarnings("unused")
public void setErrorImage(Drawable errorImage) {
    this.errorImage = errorImage;
    this.errorImage.setBounds(0, 0, d_w, d_h);
}

public void setOnImageClickListener(OnImageClickListener onImageClickListener) {
    this.onImageClickListener = onImageClickListener;
}

public void setImageFixListener(ImageFixListener mImageFixListener) {
    this.mImageFixListener = mImageFixListener;
}

/**
 * 设置超链接点击回调事件(需在setRichText方法之前调用)
 *
 * @param onURLClickListener 回调
 */
public void setOnURLClickListener(OnURLClickListener onURLClickListener) {
    this.onURLClickListener = onURLClickListener;
}

@Override
public void onViewAttachedToWindow(View v) {

}

@Override
public void onViewDetachedFromWindow(View v) {
    glideImageGeter.recycle();
}

public interface OnImageClickListener {
    /**
     * 图片被点击后的回调方法
     *
     * @param imageUrls 本篇富文本内容里的全部图片
     * @param position  点击处图片在imageUrls中的位置
     */
    void imageClicked(List<String> imageUrls, int position);
}

public interface OnURLClickListener {

    /**
     * 超链接点击得回调方法
     *
     * @param url 点击得url
     * @return true:已处理,false:未处理(会进行默认处理)
     */
    boolean urlClicked(String url);
}

public interface ImageFixListener {
    /**
     * 修复图片尺寸的方法
     *
     * @param holder ImageHolder对象
     */
    void onFix(ImageHolder holder);

}

public void configure(String imageHostPrefix) {
    imageHost = imageHostPrefix;
}

}

作者 east
Android 12月 11,2020

Android身份证合法性校验工具类



import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
* <p>
* 身份证合法性校验
* </p>
*
* <pre>
* --15位身份证号码:第7、8位为出生年份(两位数),第9、10位为出生月份,第11、12位代表出生日期,第15位代表性别,奇数为男,偶数为女。
* --18位身份证号码:第7、8、9、10位为出生年份(四位数),第11、第12位为出生月份,第13、14位代表出生日期,第17位代表性别,奇数为男,偶数为女。
* 最后一位为校验位
* </pre>
*
*
@author
*/

public class IdcardValidator {
/**
* <pre>
* 省、直辖市代码表:
* 11 : 北京 12 : 天津 13 : 河北 14 : 山西 15 : 内蒙古
* 21 : 辽宁 22 : 吉林 23 : 黑龙江 31 : 上海 32 : 江苏
* 33 : 浙江 34 : 安徽 35 : 福建 36 : 江西 37 : 山东
* 41 : 河南 42 : 湖北 43 : 湖南 44 : 广东 45 : 广西 46 : 海南
* 50 : 重庆 51 : 四川 52 : 贵州 53 : 云南 54 : 西藏
* 61 : 陕西 62 : 甘肃 63 : 青海 64 : 宁夏 65 : 新疆
* 71 : 台湾
* 81 : 香港 82 : 澳门
* 91 : 国外
* </pre>
*/
private static String cityCode[] = { "11", "12", "13", "14", "15", "21",
"22", "23", "31", "32", "33", "34", "35", "36", "37", "41", "42",
"43", "44", "45", "46", "50", "51", "52", "53", "54", "61", "62",
"63", "64", "65", "71", "81", "82", "91" };

/**
* 每位加权因子
*/
private static int power[] = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5,
8, 4, 2 };

/**
* 验证所有的身份证的合法性
*
*
@param idcard
* 身份证
*
@return 合法返回true,否则返回false
*/
public static boolean isValidatedAllIdcard(String idcard) {
if (idcard == null || "".equals(idcard)) {
return false;
}
if (idcard.length() == 15) {
return validate15IDCard(idcard);
}
return validate18Idcard(idcard);
}

/**
* <p>
* 判断18位身份证的合法性
* </p>
* 根据〖中华人民共和国国家标准GB11643-1999〗中有关公民身份号码的规定,公民身份号码是特征组合码,由十七位数字本体码和一位数字校验码组成。
* 排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。
* <p>
* 顺序码: 表示在同一地址码所标识的区域范围内,对同年、同月、同 日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配 给女性。
* </p>
* <p>
* 1.前1、2位数字表示:所在省份的代码; 2.第3、4位数字表示:所在城市的代码; 3.第5、6位数字表示:所在区县的代码;
* 4.第7~14位数字表示:出生年、月、日; 5.第15、16位数字表示:所在地的派出所的代码;
* 6.第17位数字表示性别:奇数表示男性,偶数表示女性;
* 7.第18位数字是校检码:也有的说是个人信息码,一般是随计算机的随机产生,用来检验身份证的正确性。校检码可以是0~9的数字,有时也用x表示。
* </p>
* <p>
* 第十八位数字(校验码)的计算方法为: 1.将前面的身份证号码17位数分别乘以不同的系数。从第一位到第十七位的系数分别为:7 9 10 5 8 4
* 2 1 6 3 7 9 10 5 8 4 2
* </p>
* <p>
* 2.将这17位数字和系数相乘的结果相加。
* </p>
* <p>
* 3.用加出来和除以11,看余数是多少
* </p>
* 4.余数只可能有0 1 2 3 4 5 6 7 8 9 10这11个数字。其分别对应的最后一位身份证的号码为1 0 X 9 8 7 6 5 4 3
* 2。
* <p>
* 5.通过上面得知如果余数是2,就会在身份证的第18位数字上出现罗马数字的Ⅹ。如果余数是10,身份证的最后一位号码就是2。
* </p>
*
*
@param idcard
* @return
*/
public static boolean validate18Idcard(String idcard) {
if (idcard == null) {
return false;
}

// 非18位为假
if (idcard.length() != 18) {
return false;
}
// 获取前17位
String idcard17 = idcard.substring(0, 17);

// 前17位全部为数字
if (!isDigital(idcard17)) {
return false;
}

String provinceid = idcard.substring(0, 2);
// 校验省份
if (!checkProvinceid(provinceid)) {
return false;
}

// 校验出生日期
String birthday = idcard.substring(6, 14);

SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");

try {
Date birthDate = sdf.parse(birthday);
String tmpDate = sdf.format(birthDate);
if (!tmpDate.equals(birthday)) {// 出生年月日不正确
return false;
}

} catch (ParseException e1) {

return false;
}

// 获取第18位
String idcard18Code = idcard.substring(17, 18);

char c[] = idcard17.toCharArray();

int bit[] = converCharToInt(c);

int sum17 = 0;

sum17 = getPowerSum(bit);

// 将和值与11取模得到余数进行校验码判断
String checkCode = getCheckCodeBySum(sum17);
if (null == checkCode) {
return false;
}
// 将身份证的第18位与算出来的校码进行匹配,不相等就为假
if (!idcard18Code.equalsIgnoreCase(checkCode)) {
return false;
}

return true;
}

/**
* 校验15位身份证
*
* <pre>
* 只校验省份和出生年月日
* </pre>
*
*
@param idcard
* @return
*/
public static boolean validate15IDCard(String idcard) {
if (idcard == null) {
return false;
}
// 非15位为假
if (idcard.length() != 15) {
return false;
}

// 15全部为数字
if (!isDigital(idcard)) {
return false;
}

String provinceid = idcard.substring(0, 2);
// 校验省份
if (!checkProvinceid(provinceid)) {
return false;
}

String birthday = idcard.substring(6, 12);

SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd");

try {
Date birthDate = sdf.parse(birthday);
String tmpDate = sdf.format(birthDate);
if (!tmpDate.equals(birthday)) {// 身份证日期错误
return false;
}

} catch (ParseException e1) {

return false;
}

return true;
}

/**
* 将15位的身份证转成18位身份证
*
*
@param idcard
* @return
*/
public static String convertIdcarBy15bit(String idcard) {
if (idcard == null) {
return null;
}

// 非15位身份证
if (idcard.length() != 15) {
return null;
}

// 15全部为数字
if (!isDigital(idcard)) {
return null;
}

String provinceid = idcard.substring(0, 2);
// 校验省份
if (!checkProvinceid(provinceid)) {
return null;
}

String birthday = idcard.substring(6, 12);

SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd");

Date birthdate = null;
try {
birthdate = sdf.parse(birthday);
String tmpDate = sdf.format(birthdate);
if (!tmpDate.equals(birthday)) {// 身份证日期错误
return null;
}

} catch (ParseException e1) {
return null;
}

Calendar cday = Calendar.getInstance();
cday.setTime(birthdate);
String year = String.valueOf(cday.get(Calendar.YEAR));

String idcard17 = idcard.substring(0, 6) + year + idcard.substring(8);

char c[] = idcard17.toCharArray();
String checkCode = "";

// 将字符数组转为整型数组
int bit[] = converCharToInt(c);

int sum17 = 0;
sum17 = getPowerSum(bit);

// 获取和值与11取模得到余数进行校验码
checkCode = getCheckCodeBySum(sum17);

// 获取不到校验位
if (null == checkCode) {
return null;
}
// 将前17位与第18位校验码拼接
idcard17 += checkCode;
return idcard17;
}

/**
* 校验省份
*
*
@param provinceid
* @return 合法返回TRUE,否则返回FALSE
*/
private static boolean checkProvinceid(String provinceid) {
for (String id : cityCode) {
if (id.equals(provinceid)) {
return true;
}
}
return false;
}

/**
* 数字验证
*
*
@param str
* @return
*/
private static boolean isDigital(String str) {
return str.matches("^[0-9]*$");
}

/**
* 将身份证的每位和对应位的加权因子相乘之后,再得到和值
*
*
@param bit
* @return
*/
private static int getPowerSum(int[] bit) {

int sum = 0;

if (power.length != bit.length) {
return sum;
}

for (int i = 0; i < bit.length; i++) {
for (int j = 0; j < power.length; j++) {
if (i == j) {
sum = sum + bit[i] * power[j];
}
}
}
return sum;
}

/**
* 将和值与11取模得到余数进行校验码判断
*
*
*
@param sum17
* @return 校验位
*/
private static String getCheckCodeBySum(int sum17) {
String checkCode = null;
switch (sum17 % 11) {
case 10:
checkCode = "2";
break;
case 9:
checkCode = "3";
break;
case 8:
checkCode = "4";
break;
case 7:
checkCode = "5";
break;
case 6:
checkCode = "6";
break;
case 5:
checkCode = "7";
break;
case 4:
checkCode = "8";
break;
case 3:
checkCode = "9";
break;
case 2:
checkCode = "x";
break;
case 1:
checkCode = "0";
break;
case 0:
checkCode = "1";
break;
}
return checkCode;
}

/**
* 将字符数组转为整型数组
*
*
@param c
* @return
* @throws NumberFormatException
*/
private static int[] converCharToInt(char[] c) throws NumberFormatException {
int[] a = new int[c.length];
int k = 0;
for (char temp : c) {
a[k++] = Integer.parseInt(String.valueOf(temp));
}
return a;
}

}
作者 east
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

上一 1 … 69 70 71 … 93 下一个

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

标签

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

官方QQ群

小程序开发群:74052405

大数据开发群: 952493060

近期文章

  • 如何在Chrome中设置启动时自动打开多个默认网页
  • spark内存溢出怎样区分是软件还是代码原因
  • MQTT完全解析和实践
  • 解决运行Selenium报错:self.driver = webdriver.Chrome(service=service) TypeError: __init__() got an unexpected keyword argument ‘service’
  • python 3.6使用mysql-connector-python报错:SyntaxError: future feature annotations is not defined
  • 详解Python当中的pip常用命令
  • AUTOSAR如何在多个供应商交付的配置中避免ARXML不兼容?
  • C++thread pool(线程池)设计应关注哪些扩展性问题?
  • 各类MCAL(Microcontroller Abstraction Layer)如何与AUTOSAR工具链解耦?
  • 如何设计AUTOSAR中的“域控制器”以支持未来扩展?

文章归档

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

功能

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

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