Просмотр исходного кода

yujie : 添加图表的moduel 抽离图表模块 晚点完成图表全部功能后打包到jitpack 就可以直接通过imp导入了

jieyu 4 лет назад
Родитель
Сommit
ad4f25c265
43 измененных файлов с 8484 добавлено и 6 удалено
  1. 3 0
      RMA/app/src/main/AndroidManifest.xml
  2. 58 4
      RMA/app/src/main/java/cn/muchinfo/rma/business/chart/ChartManager.kt
  3. 33 0
      RMA/app/src/main/java/cn/muchinfo/rma/global/data/chart/HistoryTikData.kt
  4. 5 0
      RMA/app/src/main/java/cn/muchinfo/rma/view/MyApplication.kt
  5. 25 2
      RMA/app/src/main/java/cn/muchinfo/rma/view/base/chart/ChartActivity.kt
  6. 30 0
      RMA/app/src/main/java/cn/muchinfo/rma/view/base/chart/ChartViewModel.kt
  7. 105 0
      RMA/app/src/main/res/layout/activity_chart.xml
  8. 1 0
      RMA/chart/.gitignore
  9. 38 0
      RMA/chart/build.gradle
  10. 0 0
      RMA/chart/consumer-rules.pro
  11. 21 0
      RMA/chart/proguard-rules.pro
  12. 5 0
      RMA/chart/src/main/AndroidManifest.xml
  13. 120 0
      RMA/chart/src/main/java/com/desfate/chart/BaseChart.java
  14. 1114 0
      RMA/chart/src/main/java/com/desfate/chart/CandleStickChart.java
  15. 2325 0
      RMA/chart/src/main/java/com/desfate/chart/GridChart.java
  16. 15 0
      RMA/chart/src/main/java/com/desfate/chart/IChart.java
  17. 807 0
      RMA/chart/src/main/java/com/desfate/chart/LineChart.java
  18. 235 0
      RMA/chart/src/main/java/com/desfate/chart/MACandleStickChart.java
  19. 563 0
      RMA/chart/src/main/java/com/desfate/chart/MinusStickChart.java
  20. 838 0
      RMA/chart/src/main/java/com/desfate/chart/StickChart.java
  21. 37 0
      RMA/chart/src/main/java/com/desfate/chart/data/BidChartTotalDatas.java
  22. 44 0
      RMA/chart/src/main/java/com/desfate/chart/data/ChartBIASDatas.java
  23. 68 0
      RMA/chart/src/main/java/com/desfate/chart/data/ChartBidFiveDatas.java
  24. 31 0
      RMA/chart/src/main/java/com/desfate/chart/data/ChartDMADatas.java
  25. 188 0
      RMA/chart/src/main/java/com/desfate/chart/data/ChartData.java
  26. 44 0
      RMA/chart/src/main/java/com/desfate/chart/data/ChartDataContainer.java
  27. 56 0
      RMA/chart/src/main/java/com/desfate/chart/data/ChartKDJDatas.java
  28. 48 0
      RMA/chart/src/main/java/com/desfate/chart/data/ChartLineColors.java
  29. 40 0
      RMA/chart/src/main/java/com/desfate/chart/data/ChartMADatas.java
  30. 56 0
      RMA/chart/src/main/java/com/desfate/chart/data/ChartMarketGoodsDatas.java
  31. 38 0
      RMA/chart/src/main/java/com/desfate/chart/data/ChartPSYDatas.java
  32. 38 0
      RMA/chart/src/main/java/com/desfate/chart/data/ChartSelectItemData.java
  33. 203 0
      RMA/chart/src/main/java/com/desfate/chart/data/ChartSelectPlans.java
  34. 53 0
      RMA/chart/src/main/java/com/desfate/chart/data/XChartDatas.java
  35. 171 0
      RMA/chart/src/main/java/com/desfate/chart/entity/LineEntity.java
  36. 244 0
      RMA/chart/src/main/java/com/desfate/chart/entity/OHLCEntity.java
  37. 168 0
      RMA/chart/src/main/java/com/desfate/chart/entity/StickEntity.java
  38. 20 0
      RMA/chart/src/main/java/com/desfate/chart/entity/TimeLineEntity.java
  39. 60 0
      RMA/chart/src/main/java/com/desfate/chart/event/ITouchEventNotify.java
  40. 25 0
      RMA/chart/src/main/java/com/desfate/chart/event/ITouchEventResponse.java
  41. 82 0
      RMA/chart/src/main/java/com/desfate/chart/util/LogUtils.java
  42. 428 0
      RMA/chart/src/main/java/com/desfate/chart/util/MathUtil.java
  43. 1 0
      RMA/settings.gradle

+ 3 - 0
RMA/app/src/main/AndroidManifest.xml

@@ -166,6 +166,9 @@
         <activity android:name=".view.base.home.commodity.HedgeSpeciesDetailsActivity"/>
 
         <activity android:name=".view.base.future.trade.GoodsTradeActivity"/>
+
+        <activity android:name=".view.base.chart.ChartActivity"
+            android:screenOrientation="portrait"/>
     </application>
 
 </manifest>

+ 58 - 4
RMA/app/src/main/java/cn/muchinfo/rma/business/chart/ChartManager.kt

@@ -1,7 +1,8 @@
 package cn.muchinfo.rma.business.chart;
 
-import cn.muchinfo.rma.global.data.FutureDetailsData
 import cn.muchinfo.rma.global.data.chart.ChartTSData
+import cn.muchinfo.rma.global.data.chart.HistoryDatas
+import cn.muchinfo.rma.global.data.chart.HistoryTikData
 import cn.muchinfo.rma.netManage.base.ResponseCallback
 import cn.muchinfo.rma.netManage.utils.MyOkHttpUtils
 import cn.muchinfo.rma.view.base.app.BaseResult
@@ -10,14 +11,15 @@ import com.blankj.utilcode.util.SPUtils
 import okhttp3.Call
 import java.lang.Exception
 
-public class ChartManager {
+
+class ChartManager {
 
     /**
      * 分时图数据查询
-     * @param params Map<String, String>
+     * @param params Map<String, String>  goodsCode 商品代码(必填)
      * @param responseBack Function3<[@kotlin.ParameterName] Boolean, [@kotlin.ParameterName] ChartTSData?, [@kotlin.ParameterName] Error?, Unit>
      */
-    fun QueryTsData(
+    fun queryTsData(
         params: Map<String, String>,
         responseBack: (isSuccess: Boolean, respData: ChartTSData?, error: Error?) -> Unit
     ) {
@@ -37,4 +39,56 @@ public class ChartManager {
             }
         )
     }
+
+    /**
+     * 查询行情Tik数据
+     * @param params Map<String, String> goodsCode 商品代码(必填) /startTime 开始时间,格式:yyyy-MM-dd HH:mm:ss /endTime 结束时间,格式:yyyy-MM-dd HH:mm:ss /count 条数 / isAsc 是否按时间顺序排序(默认为时间倒序排序)
+     * @param responseBack Function3<[@kotlin.ParameterName] Boolean, [@kotlin.ParameterName] HistoryTikData?, [@kotlin.ParameterName] Error?, Unit>
+     */
+    fun queryHistoryTikDatas(
+        params: Map<String, String>,
+        responseBack: (isSuccess: Boolean, respData: HistoryTikData?, error: Error?) -> Unit
+    ) {
+        MyOkHttpUtils().query(
+            URL = SPUtils.getInstance()
+                .getString(Constant.goCommonSearchUrl) + "/Quote/QueryHistoryTikDatas",
+            params = params,
+            type = "1",
+            callback = object : ResponseCallback<BaseResult<HistoryTikData>>() {
+                override fun onResponse(response: BaseResult<HistoryTikData>?, id: Int) {
+                    responseBack(true, response?.data, null)
+                }
+
+                override fun onError(call: Call?, e: Exception?, id: Int) {
+                    responseBack(false, null, Error(e?.message))
+                }
+            }
+        )
+    }
+
+    /**
+     * 查询行情历史数据
+     * @param params Map<String, String> goodsCode 商品代码(必填)/ cycleType 周期类型, 0-秒 1: 1分钟 2: 5分钟 3: 30分钟 4: 60分钟 120: 2小时 240: 4小时 11: 日线(必填) /startTime 开始时间,格式:yyyy-MM-dd HH:mm:ss /endTime 结束时间,格式:yyyy-MM-dd HH:mm:ss /count 条数 / isAsc 是否按时间顺序排序(默认为时间倒序排序)
+     * @param responseBack Function3<[@kotlin.ParameterName] Boolean, [@kotlin.ParameterName] HistoryDatas?, [@kotlin.ParameterName] Error?, Unit>
+     */
+    fun queryHistoryDatas(
+        params: Map<String, String>,
+        responseBack: (isSuccess: Boolean, respData: HistoryDatas?, error: Error?) -> Unit
+        ) {
+            MyOkHttpUtils().query(
+                URL = SPUtils.getInstance()
+                    .getString(Constant.goCommonSearchUrl) + "/Quote/QueryHistoryDatas",
+                params = params,
+                type = "1",
+                callback = object : ResponseCallback<BaseResult<HistoryDatas>>() {
+                    override fun onResponse(response: BaseResult<HistoryDatas>?, id: Int) {
+                        responseBack(true, response?.data, null)
+                    }
+
+                    override fun onError(call: Call?, e: Exception?, id: Int) {
+                        responseBack(false, null, Error(e?.message))
+                    }
+                }
+            )
+    }
 }

+ 33 - 0
RMA/app/src/main/java/cn/muchinfo/rma/global/data/chart/HistoryTikData.kt

@@ -0,0 +1,33 @@
+package cn.muchinfo.rma.global.data.chart
+
+/**
+ * 行情Tik数据 通过outGoodsCode请求
+ */
+data class HistoryTikData(
+    var AV : String? = "",    //    卖量
+    var Ask : String? = "",   //    卖价
+    var BV : String? = "",    //    买量
+    var Bid : String? = "",   //    买价
+    var HI : String? = "",    //    单笔持仓
+    var HV : String? = "",    //    持仓量
+    var PE : String? = "",    //    现价
+    var TDR : String? = "",   //    交易方向,0:买 1:卖
+    var TK : String? = "",    //    交易类型
+    var TS : String? = "",    //    行情时间文本
+    var TT : String? = "",    //    现金额
+    var Vol : String? = ""    //    现量
+){
+    fun getSalesVolume() : String = AV.toString()
+    fun getSalesPrice() : String = Ask.toString()
+    fun getBuyVolume() : String = BV.toString()
+    fun getBuyPrice() : String = Bid.toString()
+    fun getSinglePosition() : String = HI.toString()
+    fun getHolderVolume() : String = HV.toString()
+    fun getCurrentPrice() : String = PE.toString()
+    fun getTransactionDirection() : String = TDR.toString()
+    fun getTransactionType() : String = TK.toString()
+    fun getQuoteTime() : String = TS.toString()
+    fun getCashAmount() : String = TT.toString()
+    fun getCurrentAmount() : String = Vol.toString()
+
+}

+ 5 - 0
RMA/app/src/main/java/cn/muchinfo/rma/view/MyApplication.kt

@@ -7,6 +7,7 @@ import android.os.Bundle
 import android.os.Looper
 import cn.muchinfo.rma.BuildConfig
 import cn.muchinfo.rma.business.account.AccountManager
+import cn.muchinfo.rma.business.chart.ChartManager
 import cn.muchinfo.rma.business.commodity.CommodityManager
 import cn.muchinfo.rma.business.common.CommonManager
 import cn.muchinfo.rma.business.contract.ContractManager
@@ -100,6 +101,9 @@ class MyApplication : BaseApplication() {
     /** 期货相关管理类 **/
     var futureManager : FutureManager? = null
 
+    /** 图表相关管理类 **/
+    var chartManager : ChartManager? = null
+
     companion object {
         private var instance: MyApplication? = null
 
@@ -190,6 +194,7 @@ class MyApplication : BaseApplication() {
         tradingQueryManager = TradingQueryManager()
         reportManager = ReportManager()
         futureManager = FutureManager()
+        chartManager = ChartManager()
     }
 
     /**

+ 25 - 2
RMA/app/src/main/java/cn/muchinfo/rma/view/base/chart/ChartActivity.kt

@@ -3,8 +3,16 @@ package cn.muchinfo.rma.view.base.chart;
 import android.content.Context
 import android.content.Intent
 import android.os.Bundle
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.lifecycle.Observer
+import cn.muchinfo.rma.R
 import cn.muchinfo.rma.view.base.BaseActivity
+import org.jetbrains.anko.verticalLayout
 
+/**
+ * 图表相关
+ */
 class ChartActivity : BaseActivity<ChartViewModel>() {
 
     companion object {
@@ -20,11 +28,26 @@ class ChartActivity : BaseActivity<ChartViewModel>() {
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        buildView()
+        setContentView(R.layout.activity_chart)
+        initView()
+
+
+        viewModel.initData(intent)
+        viewModel.initRequest()
     }
 
-    private fun buildView(){
+    fun initView(){
+        findViewById<ImageView>(R.id.back_img).setOnClickListener {
+            finish()
+        }
 
+        viewModel.goodsInfo.observe(this, Observer {
+            findViewById<TextView>(R.id.goods_name_tv).text = it?.goodsname
+            findViewById<TextView>(R.id.goods_code_tv).text = it?.goodscode
+        })
     }
 
+
+
+
 }

+ 30 - 0
RMA/app/src/main/java/cn/muchinfo/rma/view/base/chart/ChartViewModel.kt

@@ -1,7 +1,37 @@
 package cn.muchinfo.rma.view.base.chart
 
+import android.content.Intent
+import androidx.lifecycle.MutableLiveData
+import cn.muchinfo.rma.global.GlobalDataCollection
+import cn.muchinfo.rma.global.data.account.loginQeruy.GoodsInfo
+import cn.muchinfo.rma.view.MyApplication
 import cn.muchinfo.rma.view.base.BaseViewModel
 
 class ChartViewModel : BaseViewModel(){
 
+    var outGoodsCode = ""
+    var goodsCode = ""
+    var goodsInfo : MutableLiveData<GoodsInfo> = MutableLiveData<GoodsInfo>()  // 当前选中的商品信息
+
+    fun initData(intent: Intent){
+        outGoodsCode = intent.getStringExtra("outGoodsCode").toString()
+        goodsCode = intent.getStringExtra("goodsCode").toString()
+        goodsInfo.postValue(GlobalDataCollection.instance?.goodsInfoList?.find { it.goodscode.equals(goodsCode) })
+    }
+
+    fun initRequest(){
+        queryTsData(goodsCode);
+    }
+
+
+    fun queryTsData(goodsCode: String) {
+        val params = mutableMapOf<String, String>().apply {
+            put("goodsCode", goodsCode)
+        }
+        MyApplication.getInstance()?.chartManager?.queryTsData(params = params) { isSuccess, respData, _ ->
+            if (isSuccess) {
+
+            }
+        }
+    }
 }

+ 105 - 0
RMA/app/src/main/res/layout/activity_chart.xml

@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <!-- 图表标题 -->
+    <RelativeLayout
+        android:id="@+id/title_lay"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintRight_toLeftOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        android:background="@color/main_title_bg_color"
+        >
+
+        <ImageView
+            android:id="@+id/back_img"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@mipmap/back"
+            android:layout_centerVertical="true"
+            android:layout_marginStart="15dp"
+            android:contentDescription="back_btn" />
+
+        <RelativeLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:padding="5dp"
+            android:layout_centerInParent="true">
+
+            <!-- goods name -->
+            <TextView
+                android:id="@+id/goods_name_tv"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text = "商品名称"
+                android:layout_centerHorizontal="true"
+                android:textColor="@color/white"
+                />
+
+            <!-- goods code -->
+            <TextView
+                android:id="@+id/goods_code_tv"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/goods_name_tv"
+                android:text="cu2013"
+                android:textColor="@color/white"
+                />
+
+            <!-- goods tag -->
+            <TextView
+                android:id="@+id/tag_type_tv"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/goods_name_tv"
+                android:layout_toEndOf="@+id/goods_code_tv"
+                android:text="主"
+                android:textColor="@color/white"
+                android:background="@color/rma_yellow_text_color"
+                android:layout_marginStart="3dp"
+                android:layout_marginRight="3dp"
+                />
+
+            <!-- market tag -->
+            <TextView
+                android:id="@+id/tag_market_tag"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/goods_name_tv"
+                android:layout_toEndOf="@+id/tag_type_tv"
+                android:text="上期所"
+                android:textColor="@color/white"
+                android:background="@color/teal_200"
+                android:layout_marginStart="3dp"
+                android:layout_marginRight="3dp"
+                />
+
+        </RelativeLayout>
+
+        <ImageView
+            android:id="@+id/search_img"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentEnd="true"
+            android:layout_centerVertical="true"
+            android:src="@mipmap/main_search"
+            android:layout_marginEnd="15dp"
+            />
+
+    </RelativeLayout>
+
+    <!-- 图表信息 -->
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/goodsInfo_rv"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintRight_toLeftOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/title_lay" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 1 - 0
RMA/chart/.gitignore

@@ -0,0 +1 @@
+/build

+ 38 - 0
RMA/chart/build.gradle

@@ -0,0 +1,38 @@
+plugins {
+    id 'com.android.library'
+}
+
+android {
+    compileSdkVersion 30
+    buildToolsVersion "30.0.3"
+
+    defaultConfig {
+        minSdkVersion 23
+        targetSdkVersion 30
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+        consumerProguardFiles "consumer-rules.pro"
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+}
+
+dependencies {
+
+    implementation 'androidx.appcompat:appcompat:1.1.0'
+    implementation 'com.google.android.material:material:1.1.0'
+    testImplementation 'junit:junit:4.+'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+}

+ 0 - 0
RMA/chart/consumer-rules.pro


+ 21 - 0
RMA/chart/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 5 - 0
RMA/chart/src/main/AndroidManifest.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.desfate.chart">
+
+</manifest>

+ 120 - 0
RMA/chart/src/main/java/com/desfate/chart/BaseChart.java

@@ -0,0 +1,120 @@
+package com.desfate.chart;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * <p>
+ * Base view of all charts
+ * </p>
+ * <p>
+ * 全部チャートのベースオブジェクト。
+ * </p>
+ * <p>
+ * 所有图表对象的基类
+ * </p>
+ */
+public class BaseChart extends View implements IChart {
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param context
+     *
+     * @see android.view.View#BaseChart(Context)
+     */
+    public BaseChart(Context context) {
+        super(context);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param context
+     *
+     * @param attrs
+     *
+     * @see android.view.View#BaseChart(Context, AttributeSet)
+     */
+    public BaseChart(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param context
+     *
+     * @param attrs
+     *
+     * @param defStyle
+     *
+     * @see android.view.View#BaseChart(Context, AttributeSet, int)
+     */
+    public BaseChart(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param widthMeasureSpec
+     *
+     * @param heightMeasureSpec
+     *
+     * @see android.view.View#onMeasure(int, int)
+     */
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        setMeasuredDimension(measureWidth(widthMeasureSpec),
+                measureHeight(heightMeasureSpec));
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param gainFocus
+     *
+     * @param direction
+     *
+     * @param previouslyFocusedRect
+     *
+     * @see android.view.View#onFocusChanged(boolean, int,
+     * android.graphics.Rect)
+     */
+    @Override
+    protected void onFocusChanged(boolean gainFocus, int direction,
+                                  Rect previouslyFocusedRect) {
+        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+    }
+
+    private int measureWidth(int measureSpec) {
+        int result = 0;
+        int specMode = MeasureSpec.getMode(measureSpec);
+        int specSize = MeasureSpec.getSize(measureSpec);
+
+        if (specMode == MeasureSpec.EXACTLY) {
+            result = specSize;
+        } else if (specMode == MeasureSpec.AT_MOST) {
+            result = Math.min(result, specSize);
+        }
+        return result;
+    }
+
+    private int measureHeight(int measureSpec) {
+        int result = 0;
+        int specMode = MeasureSpec.getMode(measureSpec);
+        int specSize = MeasureSpec.getSize(measureSpec);
+
+        if (specMode == MeasureSpec.EXACTLY) {
+            result = specSize;
+        } else if (specMode == MeasureSpec.AT_MOST) {
+            result = Math.min(result, specSize);
+        }
+        return result;
+    }
+}

+ 1114 - 0
RMA/chart/src/main/java/com/desfate/chart/CandleStickChart.java

@@ -0,0 +1,1114 @@
+package com.desfate.chart;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+
+import com.desfate.chart.entity.OHLCEntity;
+import com.desfate.chart.event.ITouchEventNotify;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <p>
+ * CandleStickChart is a kind of graph that draw the OHLCs on a GridChart if you
+ * want display some moving average lines on this graph, please use see
+ * MACandleStickChart for more information
+ * </p>
+ * <p>
+ * CandleStickChartはGridChartの表面でロウソクを書いたラインチャードです。移動平均線など
+ * 分析線がお使いしたい場合、MACandleStickChartにご参照ください。
+ * </p>
+ * <p>
+ * CandleStickChart是在GridChart上绘制K线(蜡烛线)的图表、如果需要支持显示均线,请参照 MACandleStickChart。
+ * </p>
+ *
+ * @see CandleStickChart
+ * @see MACandleStickChart
+ */
+public class CandleStickChart extends GridChart {
+
+    private static final String TAG = "CandleStickChart";
+
+    /**
+     * <p>
+     * Default price up stick's border color
+     * </p>
+     * <p>
+     * 値上がりローソクのボーダー色のデフォルト値
+     * </p>
+     * <p>
+     * 默认阳线的边框颜色
+     * </p>
+     */
+    public static final int DEFAULT_POSITIVE_STICK_BORDER_COLOR = Color.RED;
+
+    /**
+     * <p>
+     * Default price up stick's fill color
+     * </p>
+     * <p>
+     * 値上がりローソクの色のデフォルト値
+     * </p>
+     * <p>
+     * 默认阳线的填充颜色
+     * </p>
+     */
+    public static final int DEFAULT_POSITIVE_STICK_FILL_COLOR = Color.RED;
+
+    /**
+     * <p>
+     * Default price down stick's border color
+     * </p>
+     * <p>
+     * 値下りローソクのボーダー色のデフォルト値
+     * </p>
+     * <p>
+     * 默认阴线的边框颜色
+     * </p>
+     */
+    public static final int DEFAULT_NEGATIVE_STICK_BORDER_COLOR = Color.GREEN;
+
+    /**
+     * <p>
+     * Default price down stick's fill color
+     * </p>
+     * <p>
+     * 値下りローソクの色のデフォルト値
+     * </p>
+     * <p>
+     * 默认阴线的填充颜色
+     * </p>
+     */
+    public static final int DEFAULT_NEGATIVE_STICK_FILL_COLOR = Color.GREEN;
+
+    /**
+     * <p>
+     * Default price no change stick's color (cross-star,T-like etc.)
+     * </p>
+     * <p>
+     * クローススターの色のデフォルト値
+     * </p>
+     * <p>
+     * 默认十字线显示颜色
+     * </p>
+     */
+    public static final int DEFAULT_CROSS_STAR_COLOR = Color.LTGRAY;
+
+    /**
+     * <p>
+     * Price up stick's border color
+     * </p>
+     * <p>
+     * 値上がりローソクのボーダー色
+     * </p>
+     * <p>
+     * 阳线的边框颜色
+     * </p>
+     */
+    private int positiveStickBorderColor = DEFAULT_POSITIVE_STICK_BORDER_COLOR;
+
+    /**
+     * <p>
+     * Price up stick's fill color
+     * </p>
+     * <p>
+     * 値上がりローソクの色
+     * </p>
+     * <p>
+     * 阳线的填充颜色
+     * </p>
+     */
+    private int positiveStickFillColor = DEFAULT_POSITIVE_STICK_FILL_COLOR;
+
+    /**
+     * <p>
+     * Price down stick's border color
+     * </p>
+     * <p>
+     * 値下りローソクのボーダー色
+     * </p>
+     * <p>
+     * 阴线的边框颜色
+     * </p>
+     */
+
+    private int negativeStickBorderColor = DEFAULT_NEGATIVE_STICK_BORDER_COLOR;
+
+    /**
+     * <p>
+     * Price down stick's fill color
+     * </p>
+     * <p>
+     * 値下りローソクの色
+     * </p>
+     * <p>
+     * 阴线的填充颜色
+     * </p>
+     */
+    private int negativeStickFillColor = /*DEFAULT_NEGATIVE_STICK_FILL_COLOR*/0xFF009933;
+
+    /**
+     * <p>
+     * Price no change stick's color (cross-star,T-like etc.)
+     * </p>
+     * <p>
+     * クローススターの色(価格変動無し)
+     * </p>
+     * <p>
+     * 十字线显示颜色(十字星,一字平线,T形线的情况)
+     * </p>
+     */
+    private int crossStarColor = DEFAULT_CROSS_STAR_COLOR;
+
+    /**
+     * <p>
+     * data to draw sticks
+     * </p>
+     * <p>
+     * スティックを書く用データ
+     * </p>
+     * <p>
+     * 绘制柱条用的数据
+     * </p>
+     */
+    private List<OHLCEntity> OHLCData;
+    private List<OHLCEntity> newOHLCData;
+
+    /**
+     * <p>
+     * max number of sticks
+     * </p>
+     * <p>
+     * スティックの最大表示数
+     * </p>
+     * <p>
+     * 柱条的最大表示数
+     * </p>
+     */
+    private int maxSticksNum;
+
+    /**
+     * <p>
+     * max value of Y axis
+     * </p>
+     * <p>
+     * Y軸の最大値
+     * </p>
+     * <p>
+     * Y的最大表示值
+     * </p>
+     */
+    private double maxValue = 0;
+
+    /**
+     * <p>
+     * min value of Y axis
+     * </p>
+     * <p>
+     * Y軸の最小値
+     * </p>
+     * <p>
+     * Y的最小表示值
+     * </p>
+     */
+    private double minValue = 0;
+
+    private boolean isChangeOHLCData = false;
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param context
+     *
+     */
+    public CandleStickChart(Context context) {
+        super(context);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param context
+     *
+     * @param attrs
+     *
+     * @param defStyle
+     *
+     * AttributeSet, int)
+     */
+    public CandleStickChart(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param context
+     *
+     * @param attrs
+     *
+     * AttributeSet)
+     */
+    public CandleStickChart(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    //逻辑更新
+    protected void onUpdate() {
+        updateOHLCData();
+        checkMember();
+        initMaxAndMin();
+        initAxisY();
+        initAxisX();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * <p>Called when is going to draw this chart<p> <p>チャートを書く前、メソッドを呼ぶ<p>
+     * <p>绘制图表时调用<p>
+     *
+     * @param canvas
+     *
+     * @see android.view.View#onDraw(android.graphics.Canvas)
+     */
+    @Override
+    protected void onDraw(Canvas canvas) {
+        onUpdate();
+        super.onDraw(canvas);
+    }
+
+    @Override
+    protected void drawChart(Canvas canvas) {
+        super.drawChart(canvas);
+        drawCandleSticks(canvas);
+    }
+
+    protected void initMaxAndMin() {
+        if (null != getOHLCData()) {
+
+            int minIndex = getOHLCData().size() - maxSticksNum;
+            if (this.getStartIndex() > minIndex && minIndex > 0) {
+                this.setStartIndex(minIndex);
+            }
+
+            for (int i = this.getStartIndex(); i >= 0 && i < getOHLCData().size()
+                    && i < this.getStartIndex() + maxSticksNum; i++) {
+                OHLCEntity ohlc = getOHLCData().get(i);
+
+                if (i == this.getStartIndex()) {
+                    maxValue = ohlc.getHigh();
+                    minValue = ohlc.getLow();
+                } else {
+                    if (ohlc.getHigh() > maxValue) {
+                        maxValue = ohlc.getHigh();
+                    }
+                    if (ohlc.getLow() < minValue) {
+                        minValue = ohlc.getLow();
+                    }
+                }
+            }
+            double rate = 0.07;
+            maxValue = (maxValue - minValue) * rate + maxValue;
+            minValue = minValue - (maxValue - minValue) * rate;
+            minValue = minValue < 0 ? 0 : minValue;
+        }
+    }
+
+    @Override
+    public int getAxisXPos(Object value) {
+        float graduate = Float.valueOf(super.getAxisXGraduate(value));
+        int pos = (int) Math.floor(graduate * maxSticksNum);
+
+        if (pos >= maxSticksNum) {
+            pos = maxSticksNum - 1;
+        } else if (pos < 0) {
+            pos = 0;
+        }
+        pos += this.getStartIndex();
+
+        return pos;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param value
+     *
+     */
+    @Override
+    protected String getAxisXValue(Object value) {
+        int index = getAxisXPos(value);
+
+        String ret = "";
+        try {
+            if (index < getOHLCData().size()) {
+                ret = getOHLCData().get(index).getTime();
+            }
+        } catch (Exception e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+
+        return ret;
+    }
+
+    /**
+     * <p>
+     * get current selected data index
+     * </p>
+     * <p>
+     * 選択したスティックのインデックス
+     * </p>
+     * <p>
+     * 获取当前选中的柱条的index
+     * </p>
+     *
+     * @return int
+     * <p>
+     * index
+     * </p>
+     * <p>
+     * インデックス
+     * </p>
+     * <p>
+     * index
+     * </p>
+     */
+    public int getSelectedIndex() {
+        if (null == super.getTouchPoint()) {
+            return 0;
+        }
+        float graduate = Float.valueOf(super.getAxisXGraduate(super
+                .getTouchPoint().x));
+        int index = (int) Math.floor(graduate * maxSticksNum);
+
+        if (index >= maxSticksNum) {
+            index = maxSticksNum - 1;
+        } else if (index < 0) {
+            index = 0;
+        }
+
+        return index;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param value
+     *
+     */
+    @Override
+    public String getAxisYGraduate(Object value) {
+        float graduate = Float.valueOf(super.getAxisYGraduate(value));
+        double dYVal = graduate * (maxValue - minValue) + minValue;
+        String strYVal = getNumberFormat().format(dYVal);
+        return strYVal;
+    }
+
+    /**
+     * <p>
+     * initialize degrees on Y axis
+     * </p>
+     * <p>
+     * Y軸の目盛を初期化
+     * </p>
+     * <p>
+     * 初始化Y轴的坐标值
+     * </p>
+     */
+    protected void initAxisX() {
+        List<String> TitleX = new ArrayList<String>();
+        if (null != getOHLCData() && getOHLCData().size() > 0 && this.getStartIndex() >= 0) {
+            float average = maxSticksNum / this.getLongitudeNum();
+            for (int i = 0; i < this.getLongitudeNum(); i++) {
+                int index = (int) Math.floor(i * average);
+                if (index > maxSticksNum - 1) {
+                    index = maxSticksNum - 1;
+                }
+                if (this.getStartIndex() + index < getOHLCData().size())
+                    TitleX.add(getOHLCData().get(this.getStartIndex() + index).getTime());
+            }
+            if (this.getStartIndex() + maxSticksNum - 1 < getOHLCData().size())
+                TitleX.add(getOHLCData().get(this.getStartIndex() + maxSticksNum - 1).getTime());
+        }
+        super.setAxisXTitles(TitleX);
+    }
+
+    public void setDecimalNum(int decimal) {
+        super.setDecimalNum(decimal);
+    }
+
+    /**
+     * <p>
+     * initialize degrees on Y axis
+     * </p>
+     * <p>
+     * Y軸の目盛を初期化
+     * </p>
+     * <p>
+     * 初始化Y轴的坐标值
+     * </p>
+     */
+    protected void initAxisY() {
+        List<String> TitleY = new ArrayList<String>();
+        if (maxValue == minValue){//这一步处理是有时会出现maxvalue和minvalue相同,导致y轴出现问题
+            maxValue = maxValue + 0.1;
+            minValue = minValue - 0.01;
+        }
+        double average = (maxValue - minValue) * 10000 / this.getLatitudeNum() / 10000;
+
+        // calculate degrees on Y axis
+        for (int i = 0; i < this.getLatitudeNum(); i++) {
+            String value = getNumberFormat().format(minValue + i * average);
+            if (value.length() < super.getAxisYMaxTitleLength()) {
+                while (value.length() < super.getAxisYMaxTitleLength()) {
+                    value = new String(" ") + value;
+                }
+            }
+            TitleY.add(value);
+        }
+        // calculate last degrees by use max value
+        String value = getNumberFormat().format(maxValue);
+        if (value.length() < super.getAxisYMaxTitleLength()) {
+            while (value.length() < super.getAxisYMaxTitleLength()) {
+                value = new String(" ") + value;
+            }
+        }
+        TitleY.add(value);
+
+        super.setAxisYTitles(TitleY);
+        if (super.isDisplayAxisYTitle() && super.getAxisYTitles() != null) {
+            int counts = super.getAxisYTitles().size();
+            Paint mPaintFont = new Paint();
+            mPaintFont.setTextSize(super.getLatitudeFontSize());
+            float textWidth = 0;
+            float maxAxisMarginLeft = 0;
+            for (int i = 0; i < counts; i++) {
+                textWidth = mPaintFont.measureText(super.getAxisYTitles().get(i)) + 10;
+                if (textWidth > maxAxisMarginLeft) {
+                    maxAxisMarginLeft = textWidth;
+                }
+            }
+
+            if (maxAxisMarginLeft > super.getNotifyAxisMarginLeft()) {
+                super.setAxisMarginLeft(maxAxisMarginLeft);
+                notifyEventAll(ITouchEventNotify.EVENT_MOVE, this);
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * draw sticks
+     * </p>
+     * <p>
+     * スティックを書く
+     * </p>
+     * <p>
+     * 绘制柱条
+     * </p>
+     *
+     * @param canvas
+     */
+    protected void drawCandleSticks(Canvas canvas) {
+        float stickWidth = getItemWidth();
+        float stickShowWidth = getItemShowWidth();
+        float stickShowOffset = (stickWidth - stickShowWidth) / 2;
+        float stickX = /*super.getAxisMarginLeft() + super.getAxisMarginRight()*/2f;//左右对称缩进,故值相同
+
+        Paint mPaintPositive = new Paint();
+//		mPaintPositive.setColor(positiveStickFillColor);
+        mPaintPositive.setColor(positiveStickBorderColor);
+        mPaintPositive.setStrokeWidth(2.0f);
+        //mPaintPositive.setStyle(Paint.Style.STROKE);//FIXME : 去掉这行,蜡烛图的红色柱状显示为实心的
+
+        Paint mPaintNegative = new Paint();
+        mPaintNegative.setColor(negativeStickFillColor);
+        mPaintNegative.setStrokeWidth(2.0f);
+
+        Paint mPaintCross = new Paint();
+        // FIXME 需要跟主题颜色匹配着变化
+
+        mPaintCross.setColor(crossStarColor);
+        mPaintCross.setStrokeWidth(2.0f);
+
+        if (null != getOHLCData()) {
+
+            int minIndex = getOHLCData().size() - maxSticksNum;
+            if (this.getStartIndex() > minIndex && minIndex > 0) {
+                this.setStartIndex(minIndex);
+            }
+
+            for (int i = this.getStartIndex(); i >= 0 && i < getOHLCData().size() && i < this.getStartIndex() + maxSticksNum; i++) {
+                OHLCEntity ohlc = getOHLCData().get(i);
+                float openY = (float) ((1f - (ohlc.getOpen() - minValue) * 10000000
+                        / ((maxValue - minValue) * 10000) / 1000)
+                        * (super.getHeight() - super.getAxisMarginBottom() - 2 * super.getAxisMarginTop()) + super
+                        .getAxisMarginTop());
+                float highY = (float) ((1f - (ohlc.getHigh() - minValue) * 10000000
+                        / ((maxValue - minValue) * 10000) / 1000)
+                        * (super.getHeight() - super.getAxisMarginBottom() - 2 * super.getAxisMarginTop()) + super
+                        .getAxisMarginTop());
+                float lowY = (float) ((1f - (ohlc.getLow() - minValue) * 10000000
+                        / ((maxValue - minValue) * 10000) / 1000)
+                        * (super.getHeight() - super.getAxisMarginBottom() - 2 * super.getAxisMarginTop()) + super
+                        .getAxisMarginTop());
+                float closeY = (float) ((1f - (ohlc.getClose() - minValue) * 10000000
+                        / ((maxValue - minValue) * 10000) / 1000)
+                        * (super.getHeight() - super.getAxisMarginBottom() - 2 * super.getAxisMarginTop()) + super
+                        .getAxisMarginTop());
+
+                float left = stickX + stickShowOffset - 0.5f;
+                if (ohlc.getOpen() < ohlc.getClose()) {
+                    // stick or line
+
+                    /**
+                     *  标记最新一口价格时显示的横线
+                     */
+//					if (i == getOHLCData().size() - 1) {
+//						canvas.drawLine(1, openY, getWidth() - getAxisMarginRight() - getAxisMarginLeft(), openY, mPaintPositive);
+//					}
+                    if (stickShowWidth >= 2f) {
+                        canvas.drawRect(left, closeY, left + stickShowWidth,
+                                openY, mPaintPositive);
+                    }
+//					canvas.drawLine(stickX + stickWidth / 2f, highY, stickX
+//							+ stickWidth / 2f, lowY, mPaintPositive);
+                    canvas.drawLine(left + stickShowWidth / 2f, highY, left
+                            + stickShowWidth / 2f, closeY, mPaintPositive);
+                    canvas.drawLine(left + stickShowWidth / 2f, openY, left
+                            + stickShowWidth / 2f, lowY, mPaintPositive);
+//					canvas.drawRect(left, highY, left
+//							+ stickShowWidth / 2f, closeY, mPaintPositive);
+                } else if (ohlc.getOpen() > ohlc.getClose()) {
+                    /**
+                     *  标记最新一口价格时显示的横线
+                     */
+//					if (i == getOHLCData().size() - 1) {给对方的撒规划
+//						canvas.drawLine(1, closeY, getWidth() - getAxisMarginRight() - getAxisMarginLeft(), closeY, mPaintPositive);
+//					}
+                    // stick or line
+                    if (stickShowWidth >= 2f) {
+                        canvas.drawRect(left, openY, left + stickShowWidth,
+                                closeY, mPaintNegative);
+                    }
+                    canvas.drawLine(left + stickShowWidth / 2f, highY, left
+                            + stickShowWidth / 2f, lowY, mPaintNegative);
+
+                } else {
+                    /**
+                     *  标记最新一口价格时显示的横线
+                     */
+//					if (i == getOHLCData().size() - 1) {
+//						canvas.drawLine(1, highY, getWidth() - getAxisMarginRight() - getAxisMarginLeft(), highY, mPaintPositive);
+//					}
+                    // line or point
+                    if (stickShowWidth >= 2f) {
+                        canvas.drawLine(left, closeY, left + stickShowWidth,
+                                openY, mPaintCross);
+                    }
+                    canvas.drawLine(left + stickShowWidth / 2f, highY, left
+                            + stickShowWidth / 2f, lowY, mPaintCross);
+                }
+                // next x
+                stickX = stickX + stickWidth;
+            }
+        }
+    }
+
+    /**
+     * 监听滑动
+     */
+    @Override
+    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
+                            float distanceY) {
+        // TODO Auto-generated method stub
+
+        if (moveChart(distanceX)) {
+            this.setTouchDistanceX(distanceX);
+            notifyEventAll(ITouchEventNotify.EVENT_MOVE, this);
+            this.setTouchDistanceX(0);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean onScale(ScaleGestureDetector detector) {
+        // TODO Auto-generated method stub
+        if (zoomChart(detector)) {
+            this.invalidate();
+            notifyEventAll(ITouchEventNotify.EVENT_SCALE, this);
+        }
+        return false;
+    }
+
+    public boolean zoomChart(ScaleGestureDetector detector) {
+        boolean isZoom = false;
+        float ps = detector.getPreviousSpan();
+        float cs = detector.getCurrentSpan();
+        if (ps > cs + 5) {
+            isZoom = zoomOut();
+        } else if (ps < cs - 5) {
+            isZoom = zoomIn();
+        }
+        return isZoom;
+    }
+
+    @Override
+    public void notifyEvent(int eventId, GridChart chart) {
+        switch (eventId) {
+            case ITouchEventNotify.EVENT_MOVE: {
+                moveChart(chart.getTouchDistanceX());
+                changeMarginLeft(chart.getAxisMarginLeft());
+                setNotifyAxisMarginLeft(chart.getAxisMarginLeft());
+            }
+            break;
+            case ITouchEventNotify.EVENT_SCALE:
+                zoomChart(chart.getScaleGestureDetector());
+                super.invalidate();
+                break;
+            default:
+                break;
+        }
+        super.notifyEvent(eventId, chart);
+    }
+
+    private void changeMarginLeft(float marginLeft) {
+        if (super.isDisplayAxisYTitle()) {
+            int counts = super.getAxisYTitles().size();
+            Paint mPaintFont = new Paint();
+            mPaintFont.setTextSize(super.getLatitudeFontSize());
+            float textWidth = 0;
+            float maxAxisMarginLeft = 0;
+            for (int i = 0; i < counts; i++) {
+                textWidth = mPaintFont.measureText(super.getAxisYTitles().get(i)) + 10;
+                if (textWidth > maxAxisMarginLeft) {
+                    maxAxisMarginLeft = textWidth;
+                }
+            }
+
+            if (maxAxisMarginLeft < marginLeft && marginLeft != super.getAxisMarginLeft()) {
+                super.setAxisMarginLeft(marginLeft);
+                super.invalidate();
+            }
+        }
+    }
+
+    private boolean moveChart(float distance) {
+        float fMoveNum = distance / getItemWidth();
+        int nMoveNum = Math.round(fMoveNum);
+        int tempIdx = 0;
+        if (nMoveNum != 0) {
+            tempIdx = this.getStartIndex() + nMoveNum;
+            if (tempIdx <= getOHLCData().size() - this.getMaxSticksNum()) {
+                this.setStartIndex(tempIdx);
+                if (this.getStartIndex() < 0)
+                    this.setStartIndex(0);
+                super.invalidate();
+                return true;
+            }
+
+        }
+        return false;
+    }
+
+    @Override
+    protected float getClickPosXOffset() {
+        return getItemWidth() / 2;
+    }
+
+    @Override
+    protected float getItemWidth() {
+        return ((super.getWidth() - super.getAxisMarginLeft() - 2 * super.getAxisMarginRight()) / maxSticksNum);
+    }
+
+    /**
+     * <p>
+     * 实际绘制的柱宽
+     * </p>
+     */
+    @Override
+    protected float getItemShowWidth() {
+        return getItemWidth() - 2f;
+    }
+
+
+    /**
+     * <p>
+     * add a new stick data to sticks and refresh this chart
+     * </p>
+     * <p>
+     * 新しいスティックデータを追加する,フラフをレフレシューする
+     * </p>
+     * <p>
+     * 追加一条新数据并刷新当前图表
+     * </p>
+     *
+     * @param entity <p>
+     *               data
+     *               </p>
+     *               <p>
+     *               データ
+     *               </p>
+     *               <p>
+     *               新数据
+     *               </p>
+     */
+    public void pushData(OHLCEntity entity) {
+        if (null != entity) {
+            // 追�?��据到数据列表
+            addData(entity);
+            // 强制重�?
+            super.postInvalidate();
+        }
+    }
+
+    /**
+     * <p>
+     * add a new stick data to sticks
+     * </p>
+     * <p>
+     * 新しいスティックデータを追加する
+     * </p>
+     * <p>
+     * 追加一条新数据
+     * </p>
+     *
+     * @param entity <p>
+     *               data
+     *               </p>
+     *               <p>
+     *               データ
+     *               </p>
+     *               <p>
+     *               新数据
+     *               </p>
+     */
+    public void addData(OHLCEntity entity) {
+        if (null != entity) {
+            if (null == getOHLCData() || 0 == getOHLCData().size()) {
+                setOHLCData(new ArrayList<OHLCEntity>());
+//				this.minValue = ((int) entity.getLow()) / 10 * 10;
+//				this.maxValue = ((int) entity.getHigh()) / 10 * 10;
+            }
+
+            this.getOHLCData().add(entity);
+
+			/*if (this.minValue > entity.getLow()) {
+                this.minValue = ((int) entity.getLow()) / 10 * 10;
+			}
+
+			if (this.maxValue < entity.getHigh()) {
+				this.maxValue = 10 + ((int) entity.getHigh()) / 10 * 10;
+			}
+
+			if (OHLCData.size() > maxSticksNum) {
+				maxSticksNum = maxSticksNum + 1;
+			}*/
+        }
+    }
+
+    private final int NONE = 0;
+    private final int ZOOM = 1;
+    private final int DOWN = 2;
+
+    private float olddistance = 0f;
+    private float newdistance = 0f;
+
+    private int TOUCH_MODE;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * <p>Called when chart is touched<p> <p>チャートをタッチしたら、メソッドを呼ぶ<p>
+	 * <p>图表点击时调用<p>
+	 * 
+	 * @param event
+	 * 
+	 * @see android.view.View#onTouchEvent(MotionEvent)
+	 */
+	/*@Override
+	public boolean onTouchEvent(MotionEvent event) {
+
+		final float MIN_LENGTH = (super.getWidth() / 40) < 5 ? 5 : (super
+				.getWidth() / 50);
+
+		switch (event.getAction() & MotionEvent.ACTION_MASK) {
+		case MotionEvent.ACTION_DOWN:
+			TOUCH_MODE = DOWN;
+			break;
+		case MotionEvent.ACTION_UP:
+		case MotionEvent.ACTION_POINTER_UP:
+			TOUCH_MODE = NONE;
+			return super.onTouchEvent(event);
+		case MotionEvent.ACTION_POINTER_DOWN:
+			olddistance = calcDistance(event);
+			if (olddistance > MIN_LENGTH) {
+				TOUCH_MODE = ZOOM;
+			}
+			break;
+		case MotionEvent.ACTION_MOVE:
+			if (TOUCH_MODE == ZOOM) {
+				newdistance = calcDistance(event);
+				if (newdistance > MIN_LENGTH
+						&& Math.abs(newdistance - olddistance) > MIN_LENGTH) {
+
+					if (newdistance > olddistance) {
+						zoomIn();
+					} else {
+						zoomOut();
+					}
+					olddistance = newdistance;
+
+					super.postInvalidate();
+					super.notifyEventAll(this);
+				}
+			}
+			break;
+		}
+		return true;
+	}*/
+
+    /**
+     * <p>
+     * calculate the distance between two touch points
+     * </p>
+     * <p>
+     * 複数タッチしたポイントの距離
+     * </p>
+     * <p>
+     * 计算两点触控时两点之间的距离
+     * </p>
+     *
+     * @param event
+     * @return float
+     * <p>
+     * distance
+     * </p>
+     * <p>
+     * 距離
+     * </p>
+     * <p>
+     * 距离
+     * </p>
+     */
+    private float calcDistance(MotionEvent event) {
+        float x = event.getX(0) - event.getX(1);
+        float y = event.getY(0) - event.getY(1);
+        return (float) Math.sqrt(x * x + y * y);
+    }
+
+    /**
+     * <p>
+     * Zoom in the graph
+     * </p>
+     * <p>
+     * 拡大表示する。
+     * </p>
+     * <p>
+     * 放大表示
+     * </p>
+     */
+    protected boolean zoomIn() {
+        if (maxSticksNum > 10) {
+            maxSticksNum = maxSticksNum - 3;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * <p>
+     * Zoom out the grid
+     * </p>
+     * <p>
+     * 縮小表示する。
+     * </p>
+     * <p>
+     * 缩小
+     * </p>
+     */
+    protected boolean zoomOut() {
+        if (maxSticksNum < getOHLCData().size() - 3) {
+            int temp = maxSticksNum;
+            maxSticksNum = maxSticksNum + 3;
+            if (this.getItemWidth() < 1) {
+                maxSticksNum = temp;
+                return false;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * @return the positiveStickBorderColor
+     */
+    public int getPositiveStickBorderColor() {
+        return positiveStickBorderColor;
+    }
+
+    /**
+     * @param positiveStickBorderColor the positiveStickBorderColor to set
+     */
+    public void setPositiveStickBorderColor(int positiveStickBorderColor) {
+        this.positiveStickBorderColor = positiveStickBorderColor;
+    }
+
+    /**
+     * @return the positiveStickFillColor
+     */
+    public int getPositiveStickFillColor() {
+        return positiveStickFillColor;
+    }
+
+    /**
+     * @param positiveStickFillColor the positiveStickFillColor to set
+     */
+    public void setPositiveStickFillColor(int positiveStickFillColor) {
+        this.positiveStickFillColor = positiveStickFillColor;
+    }
+
+    /**
+     * @return the negativeStickBorderColor
+     */
+    public int getNegativeStickBorderColor() {
+        return negativeStickBorderColor;
+    }
+
+    /**
+     * @param negativeStickBorderColor the negativeStickBorderColor to set
+     */
+    public void setNegativeStickBorderColor(int negativeStickBorderColor) {
+        this.negativeStickBorderColor = negativeStickBorderColor;
+    }
+
+    /**
+     * @return the negativeStickFillColor
+     */
+    public int getNegativeStickFillColor() {
+        return negativeStickFillColor;
+    }
+
+    /**
+     * @param negativeStickFillColor the negativeStickFillColor to set
+     */
+    public void setNegativeStickFillColor(int negativeStickFillColor) {
+        this.negativeStickFillColor = negativeStickFillColor;
+    }
+
+    /**
+     * @return the crossStarColor
+     */
+    public int getCrossStarColor() {
+        return crossStarColor;
+    }
+
+    /**
+     * @param crossStarColor the crossStarColor to set
+     */
+    public void setCrossStarColor(int crossStarColor) {
+        this.crossStarColor = crossStarColor;
+    }
+
+    private void updateOHLCData() {
+        if (isChangeOHLCData) {
+            isChangeOHLCData = false;
+            if (newOHLCData != null) {
+
+                if (OHLCData == null) {
+                    OHLCData = new ArrayList<OHLCEntity>();
+                }
+				/*for (OHLCEntity e : newOHLCData) {
+					addData(e);
+				}*/
+                OHLCData.clear();
+                OHLCData.addAll(newOHLCData);
+
+            }
+        }
+    }
+
+    /**
+     * @return the oHLCData
+     */
+    public List<OHLCEntity> getOHLCData() {
+        if (OHLCData == null) {
+            OHLCData = new ArrayList<OHLCEntity>();
+        }
+        return OHLCData;
+    }
+
+    /**
+     * @param oHLCData the oHLCData to set
+     */
+    public void setOHLCData(List<OHLCEntity> oHLCData) {
+        if (newOHLCData == null) {
+            newOHLCData = new ArrayList<OHLCEntity>();
+        }
+        newOHLCData.clear();
+        newOHLCData.addAll(oHLCData);
+        isChangeOHLCData = true;
+    }
+
+    /**
+     * @return the maxSticksNum
+     */
+    public int getMaxSticksNum() {
+        return maxSticksNum;
+    }
+
+    /**
+     * @param maxSticksNum the maxSticksNum to set
+     */
+    public void setMaxSticksNum(int maxSticksNum) {
+        this.maxSticksNum = maxSticksNum;
+    }
+
+    /**
+     * @return the maxValue
+     */
+    public double getMaxValue() {
+        return maxValue;
+    }
+
+    /**
+     * @param maxValue the maxValue to set
+     */
+    public void setMaxValue(double maxValue) {
+        this.maxValue = maxValue;
+    }
+
+    /**
+     * @return the minValue
+     */
+    public double getMinValue() {
+        return minValue;
+    }
+
+    /**
+     * @param minValue the minValue to set
+     */
+    public void setMinValue(double minValue) {
+        this.minValue = minValue;
+    }
+
+    private void checkMember() {
+        if (this.getStartIndex() < 0) {
+            int minIndex = getOHLCData().size() - maxSticksNum;
+            if (minIndex > 0) {
+                setStartIndex(minIndex);
+            } else if (getOHLCData().size() > 0) {
+                setStartIndex(0);
+                //setMaxSticksNum(getOHLCData().size());
+            }
+        }
+    }
+}

+ 2325 - 0
RMA/chart/src/main/java/com/desfate/chart/GridChart.java

@@ -0,0 +1,2325 @@
+package com.desfate.chart;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.DashPathEffect;
+import android.graphics.Paint;
+import android.graphics.PathEffect;
+import android.graphics.PointF;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.GestureDetector;
+import android.view.GestureDetector.OnGestureListener;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import android.view.ScaleGestureDetector.OnScaleGestureListener;
+
+import com.desfate.chart.data.ChartLineColors;
+import com.desfate.chart.event.ITouchEventNotify;
+import com.desfate.chart.event.ITouchEventResponse;
+import com.desfate.chart.util.LogUtils;
+
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+
+
+/**
+ * <p>
+ * GridChart is base type of all the charts that use a grid to display like
+ * line-chart stick-chart etc. GridChart implemented a simple grid with basic
+ * functions what can be used in it's inherited charts.
+ * </p>
+ * <p>
+ * GridChartは全部グリドチャートのベスクラスです、一部処理は共通化け実現した。
+ * </p>
+ * <p>
+ * GridChart是所有网格图表的基础类对象,它实现了基本的网格图表功能,这些功能将被它的继承类使用
+ * </p>
+ */
+public class GridChart extends BaseChart implements OnGestureListener, OnScaleGestureListener, ITouchEventNotify,
+        ITouchEventResponse {
+    private int linesData = 0;
+    /**
+     * <p>
+     * default background color
+     * </p>
+     * <p>
+     * 背景の色のデフォルト値
+     * </p>
+     * <p>
+     * 默认背景色
+     * </p>
+     */
+    public static final int DEFAULT_BACKGROUND_COLOR = Color.BLACK;
+
+    /**
+     * <p>
+     * default color of X axis
+     * </p>
+     * <p>
+     * X軸の色のデフォルト値
+     * </p>
+     * <p>
+     * 默认坐标轴X的显示颜色
+     * </p>
+     */
+    public static final int DEFAULT_AXIS_X_COLOR = Color.RED;
+
+    /**
+     * <p>
+     * default color of Y axis
+     * </p>
+     * <p>
+     * Y軸の色のデフォルト値
+     * </p>
+     * <p>
+     * 默认坐标轴Y的显示颜色
+     * </p>
+     */
+    public static final int DEFAULT_AXIS_Y_COLOR = Color.RED;
+
+    /**
+     * <p>
+     * default color of grid‘s longitude line
+     * </p>
+     * <p>
+     * 経線の色のデフォルト値
+     * </p>
+     * <p>
+     * 默认网格经线的显示颜色
+     * </p>
+     */
+    public static final int DEFAULT_LONGITUDE_COLOR = Color.RED;
+
+    /**
+     * <p>
+     * default color of grid‘s latitude line
+     * </p>
+     * <p>
+     * 緯線の色のデフォルト値
+     * </p>
+     * <p>
+     * 默认网格纬线的显示颜色
+     * </p>
+     */
+    public static final int DEFAULT_LAITUDE_COLOR = Color.RED;
+
+    /**
+     * <p>
+     * default margin of the axis to the left border
+     * </p>
+     * <p>
+     * 轴線より左枠線の距離のデフォルト値
+     * </p>
+     * <p>
+     * 默认轴线左边距
+     * </p>
+     */
+    public static final float DEFAULT_AXIS_MARGIN_LEFT = 42f;
+
+    /**
+     * <p>
+     * default margin of the axis to the bottom border
+     * </p>
+     * <p>
+     * 轴線より下枠線の距離のデフォルト値
+     * </p>
+     * <p>
+     * 默认轴线下边距
+     * </p>
+     */
+    public static final float DEFAULT_AXIS_MARGIN_BOTTOM = 16f;
+
+    /**
+     * <p>
+     * default margin of the axis to the top border
+     * </p>
+     * <p>
+     * 轴線より上枠線の距離のデフォルト値
+     * </p>
+     * <p>
+     * 默认轴线上边距
+     * </p>
+     */
+    public static final float DEFAULT_AXIS_MARGIN_TOP = 5f;
+
+    /**
+     * <p>
+     * default margin of the axis to the right border
+     * </p>
+     * <p>
+     * 轴線より右枠線の距離のデフォルト値
+     * </p>
+     * <p>
+     * 轴线右边距
+     * </p>
+     */
+    public static final float DEFAULT_AXIS_MARGIN_RIGHT = 5f;
+
+    /**
+     * <p>
+     * default numbers of grid‘s latitude line
+     * </p>
+     * <p>
+     * 緯線の数量のデフォルト値
+     * </p>
+     * <p>
+     * 网格纬线的数量
+     * </p>
+     */
+    public static final int DEFAULT_LATITUDE_NUM = 4;
+
+    /**
+     * <p>
+     * default numbers of grid‘s longitude line
+     * </p>
+     * <p>
+     * 経線の数量のデフォルト値
+     * </p>
+     * <p>
+     * 网格经线的数量
+     * </p>
+     */
+    public static final int DEFAULT_LONGITUDE_NUM = 3;
+
+    /**
+     * <p>
+     * Should display longitude line?
+     * </p>
+     * <p>
+     * 経線を表示するか?
+     * </p>
+     * <p>
+     * 默认经线是否显示
+     * </p>
+     */
+    public static final boolean DEFAULT_DISPLAY_LONGITUDE = Boolean.TRUE;
+
+    /**
+     * <p>
+     * Should display longitude as dashed line?
+     * </p>
+     * <p>
+     * 経線を点線にするか?
+     * </p>
+     * <p>
+     * 默认经线是否显示为虚线
+     * </p>
+     */
+    public static final boolean DEFAULT_DASH_LONGITUDE = Boolean.TRUE;
+
+    /**
+     * <p>
+     * Should display longitude line?
+     * </p>
+     * <p>
+     * 緯線を表示するか?
+     * </p>
+     * <p>
+     * 纬线是否显示
+     * </p>
+     */
+    public static final boolean DEFAULT_DISPLAY_LATITUDE = Boolean.TRUE;
+
+    /**
+     * <p>
+     * Should display latitude as dashed line?
+     * </p>
+     * <p>
+     * 緯線を点線にするか?
+     * </p>
+     * <p>
+     * 纬线是否显示为虚线
+     * </p>
+     */
+    public static final boolean DEFAULT_DASH_LATITUDE = Boolean.TRUE;
+
+    /**
+     * <p>
+     * Should display the degrees in X axis?
+     * </p>
+     * <p>
+     * X軸のタイトルを表示するか?
+     * </p>
+     * <p>
+     * X轴上的标题是否显示
+     * </p>
+     */
+    public static final boolean DEFAULT_DISPLAY_AXIS_X_TITLE = Boolean.TRUE;
+
+    /**
+     * <p>
+     * Should display the degrees in Y axis?
+     * </p>
+     * <p>
+     * Y軸のタイトルを表示するか?
+     * </p>
+     * <p>
+     * 默认Y轴上的标题是否显示
+     * </p>
+     */
+    public static final boolean DEFAULT_DISPLAY_AXIS_Y_TITLE = Boolean.TRUE;
+
+    /**
+     * <p>
+     * Should display the border?
+     * </p>
+     * <p>
+     * 枠を表示するか?
+     * </p>
+     * <p>
+     * 默认控件是否显示边框
+     * </p>
+     */
+    public static final boolean DEFAULT_DISPLAY_BORDER = Boolean.TRUE;
+
+    /**
+     * <p>
+     * default color of text for the longitude degrees display
+     * </p>
+     * <p>
+     * 経度のタイトルの色のデフォルト値
+     * </p>
+     * <p>
+     * 默认经线刻度字体颜色
+     * </p>
+     */
+    public static final int DEFAULT_BORDER_COLOR = Color.RED;
+
+    /**
+     * <p>
+     * default color of text for the longitude degrees display
+     * </p>
+     * <p>
+     * 経度のタイトルの色のデフォルト値
+     * </p>
+     * <p>
+     * 经线刻度字体颜色
+     * </p>
+     */
+    public static final int DEFAULT_LONGITUDE_FONT_COLOR = Color.WHITE;
+
+    /**
+     * <p>
+     * default font size of text for the longitude degrees display
+     * </p>
+     * <p>
+     * 経度のタイトルのフォントサイズのデフォルト値
+     * </p>
+     * <p>
+     * 经线刻度字体大小
+     * </p>
+     */
+    public static final int DEFAULT_LONGITUDE_FONT_SIZE = 12;
+
+    /**
+     * <p>
+     * default color of text for the latitude degrees display
+     * </p>
+     * <p>
+     * 緯度のタイトルの色のデフォルト値
+     * </p>
+     * <p>
+     * 纬线刻度字体颜色
+     * </p>
+     */
+    public static final int DEFAULT_LATITUDE_FONT_COLOR = Color.RED;
+
+    /**
+     * <p>
+     * default font size of text for the latitude degrees display
+     * </p>
+     * <p>
+     * 緯度のタイトルのフォントサイズのデフォルト値
+     * </p>
+     * <p>
+     * 默认纬线刻度字体大小
+     * </p>
+     */
+    public static final int DEFAULT_LATITUDE_FONT_SIZE = 12;
+
+    /**
+     * <p>
+     * default titles' max length for display of Y axis
+     * </p>
+     * <p>
+     * Y軸の表示用タイトルの最大文字長さのデフォルト値
+     * </p>
+     * <p>
+     * 默认Y轴标题最大文字长度
+     * </p>
+     */
+    public static final int DEFAULT_AXIS_Y_MAX_TITLE_LENGTH = 5;
+
+    /**
+     * <p>
+     * default dashed line type
+     * </p>
+     * <p>
+     * 点線タイプのデフォルト値
+     * </p>
+     * <p>
+     * 默认虚线效果
+     * </p>
+     */
+    public static final PathEffect DEFAULT_DASH_EFFECT = new DashPathEffect(
+            new float[]{3, 3, 3, 3}, 1);
+
+    /**
+     * <p>
+     * Should display the Y cross line if grid is touched?
+     * </p>
+     * <p>
+     * 默认在控件被点击时,显示十字线
+     * </p>
+     */
+    public static final boolean ENABLE_CROSS_ON_TOUCH = Boolean.TRUE;
+    /**
+     * <p>
+     * Should display the Y cross line if grid is touched?
+     * </p>
+     * <p>
+     * タッチしたポイントがある場合、十字線の垂直線を表示するか?
+     * </p>
+     * <p>
+     * 默认在控件被点击时,不显示十字竖线线
+     * </p>
+     */
+    public static final boolean DEFAULT_DISPLAY_CROSS_X_ON_TOUCH = Boolean.FALSE;
+
+    /**
+     * <p>
+     * Should display the Y cross line if grid is touched?
+     * </p>
+     * <p>
+     * タッチしたポイントがある場合、十字線の水平線を表示するか?
+     * </p>
+     * <p>
+     * 默认在控件被点击时,不显示十字横线线
+     * </p>
+     */
+    public static final boolean DEFAULT_DISPLAY_CROSS_Y_ON_TOUCH = Boolean.FALSE;
+
+    /**
+     * <p>
+     * background color
+     * </p>
+     * <p>
+     * 背景の色
+     * </p>
+     * <p>
+     * 背景色
+     * </p>
+     */
+    private int backgroundColor = DEFAULT_BACKGROUND_COLOR;
+
+    /**
+     * <p>
+     * Color of X axis
+     * </p>
+     * <p>
+     * X軸の色
+     * </p>
+     * <p>
+     * 坐标轴X的显示颜色
+     * </p>
+     */
+    private int axisXColor = DEFAULT_AXIS_X_COLOR;
+
+    /**
+     * <p>
+     * Color of Y axis
+     * </p>
+     * <p>
+     * Y軸の色
+     * </p>
+     * <p>
+     * 坐标轴Y的显示颜色
+     * </p>
+     */
+    private int axisYColor = DEFAULT_AXIS_Y_COLOR;
+
+    /**
+     * <p>
+     * Color of grid‘s longitude line
+     * </p>
+     * <p>
+     * 経線の色
+     * </p>
+     * <p>
+     * 网格经线的显示颜色
+     * </p>
+     */
+    private int longitudeColor = DEFAULT_LONGITUDE_COLOR;
+
+    /**
+     * <p>
+     * Color of grid‘s latitude line
+     * </p>
+     * <p>
+     * 緯線の色
+     * </p>
+     * <p>
+     * 网格纬线的显示颜色
+     * </p>
+     */
+    private int latitudeColor = DEFAULT_LAITUDE_COLOR;
+
+    /**
+     * <p>
+     * 相关联图表的轴线左边距
+     * </p>
+     */
+    private float notifyAxisMarginLeft = DEFAULT_AXIS_MARGIN_LEFT;
+
+    /**
+     * <p>
+     * Margin of the axis to the left border
+     * </p>
+     * <p>
+     * 轴線より左枠線の距離
+     * </p>
+     * <p>
+     * 轴线左边距
+     * </p>
+     */
+    private float axisMarginLeft = DEFAULT_AXIS_MARGIN_LEFT;
+
+    /**
+     * <p>
+     * Margin of the axis to the bottom border
+     * </p>
+     * <p>
+     * 轴線より下枠線の距離
+     * </p>
+     * <p>
+     * 轴线下边距
+     * </p>
+     */
+    private float axisMarginBottom = DEFAULT_AXIS_MARGIN_BOTTOM;
+
+    /**
+     * <p>
+     * Margin of the axis to the top border
+     * </p>
+     * <p>
+     * 轴線より上枠線の距離
+     * </p>
+     * <p>
+     * 轴线上边距
+     * </p>
+     */
+    private float axisMarginTop = DEFAULT_AXIS_MARGIN_TOP;
+
+    /**
+     * <p>
+     * Margin of the axis to the right border
+     * </p>
+     * <p>
+     * 轴線より右枠線の距離
+     * </p>
+     * <p>
+     * 轴线右边距
+     * </p>
+     */
+    private float axisMarginRight = DEFAULT_AXIS_MARGIN_RIGHT;
+
+    /**
+     * <p>
+     * Should display the degrees in X axis?
+     * </p>
+     * <p>
+     * X軸のタイトルを表示するか?
+     * </p>
+     * <p>
+     * X轴上的标题是否显示
+     * </p>
+     */
+    private boolean displayAxisXTitle = DEFAULT_DISPLAY_AXIS_X_TITLE;
+
+    /**
+     * <p>
+     * Should display the degrees in Y axis?
+     * </p>
+     * <p>
+     * Y軸のタイトルを表示するか?
+     * </p>
+     * <p>
+     * Y轴上的标题是否显示
+     * </p>
+     */
+    private boolean displayAxisYTitle = DEFAULT_DISPLAY_AXIS_Y_TITLE;
+
+    /**
+     * <p>
+     * Numbers of grid‘s latitude line
+     * </p>
+     * <p>
+     * 緯線の数量
+     * </p>
+     * <p>
+     * 网格纬线的数量
+     * </p>
+     */
+    private int latitudeNum = DEFAULT_LATITUDE_NUM;
+
+    /**
+     * <p>
+     * Numbers of grid‘s longitude line
+     * </p>
+     * <p>
+     * 経線の数量
+     * </p>
+     * <p>
+     * 网格经线的数量
+     * </p>
+     */
+    private int longitudeNum = DEFAULT_LONGITUDE_NUM;
+    /**
+     * <p>
+     * 小数位
+     * </p>
+     */
+    private int decimalNum = 2;//!< @brief 小数位
+    /**
+     * <p>
+     * Should display longitude line?
+     * </p>
+     * <p>
+     * 経線を表示するか?
+     * </p>
+     * <p>
+     * 经线是否显示
+     * </p>
+     */
+    private boolean displayLongitude = DEFAULT_DISPLAY_LONGITUDE;
+
+    /**
+     * <p>
+     * Should display longitude as dashed line?
+     * </p>
+     * <p>
+     * 経線を点線にするか?
+     * </p>
+     * <p>
+     * 经线是否显示为虚线
+     * </p>
+     */
+    private boolean dashLongitude = DEFAULT_DASH_LONGITUDE;
+
+    /**
+     * <p>
+     * Should display longitude line?
+     * </p>
+     * <p>
+     * 緯線を表示するか?
+     * </p>
+     * <p>
+     * 纬线是否显示
+     * </p>
+     */
+    private boolean displayLatitude = DEFAULT_DISPLAY_LATITUDE;
+
+    /**
+     * <p>
+     * Should display latitude as dashed line?
+     * </p>
+     * <p>
+     * 緯線を点線にするか?
+     * </p>
+     * <p>
+     * 纬线是否显示为虚线
+     * </p>
+     */
+    private boolean dashLatitude = DEFAULT_DASH_LATITUDE;
+
+    /**
+     * <p>
+     * dashed line type
+     * </p>
+     * <p>
+     * 点線タイプ?
+     * </p>
+     * <p>
+     * 虚线效果
+     * </p>
+     */
+    private PathEffect dashEffect = DEFAULT_DASH_EFFECT;
+
+    /**
+     * <p>
+     * Should display the border?
+     * </p>
+     * <p>
+     * 枠を表示するか?
+     * </p>
+     * <p>
+     * 控件是否显示边框
+     * </p>
+     */
+    private boolean displayBorder = DEFAULT_DISPLAY_BORDER;
+
+    /**
+     * <p>
+     * Color of grid‘s border line
+     * </p>
+     * <p>
+     * 枠線の色
+     * </p>
+     * <p>
+     * 图边框的颜色
+     * </p>
+     */
+    private int borderColor = DEFAULT_BORDER_COLOR;
+
+    /**
+     * <p>
+     * Color of text for the longitude degrees display
+     * </p>
+     * <p>
+     * 経度のタイトルの色
+     * </p>
+     * <p>
+     * 经线刻度字体颜色
+     * </p>
+     */
+    private int longitudeFontColor = DEFAULT_LONGITUDE_FONT_COLOR;
+
+    /**
+     * <p>
+     * Font size of text for the longitude degrees display
+     * </p>
+     * <p>
+     * 経度のタイトルのフォントサイズ
+     * </p>
+     * <p>
+     * 经线刻度字体大小
+     * </p>
+     */
+    private int longitudeFontSize = DEFAULT_LONGITUDE_FONT_SIZE;
+
+    /**
+     * <p>
+     * Color of text for the latitude degrees display
+     * </p>
+     * <p>
+     * 緯度のタイトルの色
+     * </p>
+     * <p>
+     * 纬线刻度字体颜色
+     * </p>
+     */
+    private int latitudeFontColor = DEFAULT_LATITUDE_FONT_COLOR;
+
+    /**
+     * <p>
+     * Font size of text for the latitude degrees display
+     * </p>
+     * <p>
+     * 緯度のタイトルのフォントサイズ
+     * </p>
+     * <p>
+     * 纬线刻度字体大小
+     * </p>
+     */
+    private int latitudeFontSize = DEFAULT_LATITUDE_FONT_SIZE;
+
+    /**
+     * <p>
+     * Titles Array for display of X axis
+     * </p>
+     * <p>
+     * X軸の表示用タイトル配列
+     * </p>
+     * <p>
+     * X轴标题数组
+     * </p>
+     */
+    private List<String> axisXTitles;
+
+    /**
+     * <p>
+     * Titles for display of Y axis
+     * </p>
+     * <p>
+     * Y軸の表示用タイトル配列
+     * </p>
+     * <p>
+     * Y轴标题数组
+     * </p>
+     */
+    private List<String> axisYTitles;
+
+    /**
+     * <p>
+     * Titles' max length for display of Y axis
+     * </p>
+     * <p>
+     * Y軸の表示用タイトルの最大文字長さ
+     * </p>
+     * <p>
+     * Y轴标题最大文字长度
+     * </p>
+     */
+    private int axisYMaxTitleLength = DEFAULT_AXIS_Y_MAX_TITLE_LENGTH;
+
+    private boolean enableCrossOnTouch = ENABLE_CROSS_ON_TOUCH;
+    /**
+     * <p>
+     * Should display the Y cross line if grid is touched?
+     * </p>
+     * <p>
+     * タッチしたポイントがある場合、十字線の垂直線を表示するか?
+     * </p>
+     * <p>
+     * 在控件被点击时,显示十字竖线线
+     * </p>
+     */
+    private boolean displayCrossXOnTouch = DEFAULT_DISPLAY_CROSS_X_ON_TOUCH;
+
+    /**
+     * <p>
+     * Should display the Y cross line if grid is touched?
+     * </p>
+     * <p>
+     * タッチしたポイントがある場合、十字線の水平線を表示するか?
+     * </p>
+     * <p>
+     * 在控件被点击时,显示十字横线线
+     * </p>
+     */
+    private boolean displayCrossYOnTouch = DEFAULT_DISPLAY_CROSS_Y_ON_TOUCH;
+
+    /**
+     * <p>
+     * Touched point inside of grid
+     * </p>
+     * <p>
+     * タッチしたポイント
+     * </p>
+     * <p>
+     * 单点触控的选中点
+     * </p>
+     */
+    private PointF touchPoint;
+
+    /**
+     * <p>
+     * Touched point inside of grid
+     * </p>
+     * <p>
+     * タッチしたポイント
+     * </p>
+     * <p>
+     * 触控的横向滑动距离
+     * </p>
+     */
+    private float touchDistanceX = 0;
+
+    /**
+     * <p>
+     * Touched point’s X value inside of grid
+     * </p>
+     * <p>
+     * タッチしたポイントのX
+     * </p>
+     * <p>
+     * 单点触控的选中点的X
+     * </p>
+     */
+    private float clickPostX = 0f;
+
+    /**
+     * <p>
+     * Touched point’s Y value inside of grid
+     * </p>
+     * <p>
+     * タッチしたポイントのY
+     * </p>
+     * <p>
+     * 单点触控的选中点的Y
+     * </p>
+     */
+    private float clickPostY = 0f;
+
+    /**
+     * <p>
+     * Event will notify objects' list
+     * </p>
+     * <p>
+     * イベント通知対象リスト
+     * </p>
+     * <p>
+     * 事件通知对象列表
+     * </p>
+     */
+    private List<ITouchEventResponse> notifyList;
+    /**
+     * <p>
+     * 起始位置
+     * </p>
+     */
+    private int startIndex = 0;
+
+    private boolean can_move = false;//针对分时图表做的标志位
+
+
+    private GestureDetector mDetector;
+    private ScaleGestureDetector mScaleDetector;
+    /**
+     * 当前行情的最新价
+     */
+    private String tasLastPrice = "";
+//	private boolean isOnScale = false;
+
+    private String currentYear = "";//当前年
+
+    private int newPriceColor = 0;
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param context
+     *
+     */
+    public GridChart(Context context) {
+        super(context);
+        mDetector = new GestureDetector(this);
+        mScaleDetector = new ScaleGestureDetector(context, this);
+        initDatas();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param context
+     *
+     * @param attrs
+     *
+     * @param defStyle
+     *
+     * AttributeSet, int)
+     */
+    public GridChart(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        mDetector = new GestureDetector(this);
+        mScaleDetector = new ScaleGestureDetector(context, this);
+        initDatas();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param context
+     *
+     * @param attrs
+     *
+     * AttributeSet)
+     */
+    public GridChart(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mDetector = new GestureDetector(this);
+        mScaleDetector = new ScaleGestureDetector(context, this);
+        initDatas();
+    }
+
+    private void initDatas() {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTimeInMillis(System.currentTimeMillis());
+        currentYear = String.valueOf(calendar.get(Calendar.YEAR)) + "-";
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * <p>Called when is going to draw this chart<p> <p>チャートを書く前、メソッドを呼ぶ<p>
+     * <p>绘制图表时调用<p>
+     *
+     * @param canvas
+     *
+     * @see android.view.View#onDraw(android.graphics.Canvas)
+     */
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+        super.setBackgroundColor(backgroundColor);
+
+        drawXAxis(canvas);
+//		drawYAxis(canvas);
+
+        if (this.displayBorder) {
+            drawBorder(canvas);
+        }
+
+        if (displayLongitude || displayAxisXTitle) {
+            drawAxisGridX(canvas);
+        }
+        if (displayLatitude || displayAxisYTitle) {
+            drawAxisGridY(canvas);
+        }
+
+        drawChart(canvas);
+
+        if (displayCrossXOnTouch || displayCrossYOnTouch) {
+            drawWithFingerClick(canvas);
+        }
+    }
+
+    protected void drawChart(Canvas canvas) {
+
+    }
+
+    private boolean checkClickPos(MotionEvent event) {
+        return event.getX() <= this.getWidth() - this.getAxisMarginRight()
+                && event.getY() <= this.getHeight() - this.getAxisMarginBottom();
+    }
+
+    private boolean _move = true;
+
+    /*
+     * (non-Javadoc)
+     *
+     * <p>Called when chart is touched<p> <p>チャートをタッチしたら、メソッドを呼ぶ<p>
+     * <p>图表点击时调用<p>
+     *
+     * @param event
+     *
+     * @see android.view.View#onTouchEvent(MotionEvent)
+     */
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        int action = event.getAction();
+        if (isEnableCrossOnTouch()) {
+            switch (action) {
+                case MotionEvent.ACTION_UP: {
+                    displayCrossXOnTouch = displayCrossYOnTouch = true;
+                    if (this.getClickPostX() > 0 || this.getClickPostY() > 0) {
+                        this.setClickPostX(0);
+                        this.setClickPostY(0);
+                        this.invalidate();
+                        notifyEventAll(ITouchEventNotify.EVENT_UP, this);
+                    }
+                }
+                break;
+                case MotionEvent.ACTION_MOVE: {
+                    float postX = getClickPostX();
+                    float hl = getAxisXPos(postX);
+                    if (isCan_move()) {
+                        if (linesData > 0 && hl > linesData && _move) {
+                            if (postX > event.getX()) {
+                                this.setClickPostX(maxDatasLength);
+                                _move = false;
+                                break;
+                            }
+                            this.setClickPostX(maxDatasLength);
+                            this.setClickPostY(event.getY());
+                            this.invalidate();
+                            notifyEventAll(ITouchEventNotify.EVENT_CROSSONTOUCH, this);
+                            break;
+                        } else if ((this.getClickPostX() > 0 || this.getClickPostY() > 0) && checkClickPos(event) && maxDatasLength >= postX) {
+                            _move = true;
+                            this.setClickPostX(event.getX() > maxDatasLength ? maxDatasLength : event.getX());
+                            this.setClickPostY(event.getY());
+                            this.invalidate();
+                            notifyEventAll(ITouchEventNotify.EVENT_CROSSONTOUCH, this);
+                        }
+                    } else {
+                        if ((this.getClickPostX() > 0 || this.getClickPostY() > 0) && checkClickPos(event)) {
+                            this.setClickPostX(event.getX());
+                            this.setClickPostY(event.getY());
+                            this.invalidate();
+                            notifyEventAll(ITouchEventNotify.EVENT_CROSSONTOUCH, this);
+                        }
+                    }
+
+                }
+                break;
+                default:
+                    break;
+            }
+        }
+        boolean ret = this.mScaleDetector.onTouchEvent(event);
+        if (!mScaleDetector.isInProgress()) {
+            ret = this.mDetector.onTouchEvent(event);
+        }
+        return ret;
+    }
+
+    @Override
+    public boolean onDown(MotionEvent e) {
+        // TODO Auto-generated method stub
+        return true;
+    }
+
+    /**
+     * 监听滑动
+     */
+    @Override
+    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+                           float velocityY) {
+        int startIndex = getStartIndex();
+        if (startIndex == 0 && (e2.getX() - e1.getX()) > 0) {
+            // 判断是否滑到最左边
+            notifyEventAll(ITouchEventNotify.EVENT_LEFTEND, this);
+        }
+        return true;
+    }
+
+    protected float getClickPosXOffset() {
+        return 0.0f;
+    }
+
+    protected float getItemWidth() {
+        return 1.0f;
+    }
+
+    protected float getItemShowWidth() {
+        return 1.0f;
+    }
+
+    @Override
+    public void onLongPress(MotionEvent e) {
+        // TODO Auto-generated method stub
+        if (isEnableCrossOnTouch() && checkClickPos(e)) {
+            this.setClickPostX(e.getX());
+            this.setClickPostY(e.getY());
+            this.invalidate();
+            notifyEventAll(ITouchEventNotify.EVENT_CROSSONTOUCH, this);
+        }
+    }
+
+    @Override
+    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
+                            float distanceY) {
+        // TODO Auto-generated method stub
+        return true;
+    }
+
+    @Override
+    public void onShowPress(MotionEvent e) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public boolean onSingleTapUp(MotionEvent event) {
+        // TODO Auto-generated method stub
+        return true;
+    }
+
+    /**
+     * <p>
+     * draw some text with border
+     * </p>
+     * <p>
+     * 文字を書く、枠あり
+     * </p>
+     * <p>
+     * 绘制一段文本,并增加外框
+     * </p>
+     *
+     * @param ptStart  <p>
+     *                 start point
+     *                 </p>
+     *                 <p>
+     *                 開始ポイント
+     *                 </p>
+     *                 <p>
+     *                 开始点
+     *                 </p>
+     * @param ptEnd    <p>
+     *                 end point
+     *                 </p>
+     *                 <p>
+     *                 結束ポイント
+     *                 </p>
+     *                 <p>
+     *                 结束点
+     *                 </p>
+     * @param content  <p>
+     *                 text content
+     *                 </p>
+     *                 <p>
+     *                 文字内容
+     *                 </p>
+     *                 <p>
+     *                 文字内容
+     *                 </p>
+     * @param fontSize <p>
+     *                 font size
+     *                 </p>
+     *                 <p>
+     *                 文字フォントサイズ
+     *                 </p>
+     *                 <p>
+     *                 字体大小
+     *                 </p>
+     * @param canvas
+     */
+    private void drawAlphaTextBox(PointF ptStart, PointF ptEnd, String content,
+                                  int fontSize, Canvas canvas) {
+
+        Paint mPaintBox = new Paint();
+        mPaintBox.setColor(Color.LTGRAY);
+//		mPaintBox.setAlpha(80);
+
+        Paint mPaintBoxLine = new Paint();
+        mPaintBoxLine.setColor(Color.BLACK);
+        mPaintBoxLine.setAntiAlias(true);
+
+        // draw a rectangle
+//		canvas.drawRoundRect(new RectF(ptStart.x, ptStart.y, ptEnd.x, ptEnd.y),20.0f, 20.0f, mPaintBox);
+        canvas.drawRect(new RectF(ptStart.x, ptStart.y, ptEnd.x, ptEnd.y), mPaintBox);
+
+        // draw a rectangle' border
+//		canvas.drawLine(ptStart.x, ptStart.y, ptStart.x, ptEnd.y, mPaintBoxLine);
+//		canvas.drawLine(ptStart.x, ptEnd.y, ptEnd.x, ptEnd.y, mPaintBoxLine);
+//		canvas.drawLine(ptEnd.x, ptEnd.y, ptEnd.x, ptStart.y, mPaintBoxLine);
+//		canvas.drawLine(ptEnd.x, ptStart.y, ptStart.x, ptStart.y, mPaintBoxLine);
+
+        // draw text
+        canvas.drawText(content, ptStart.x, ptEnd.y, mPaintBoxLine);
+    }
+
+    private void drawTextBox(PointF ptStart, PointF ptEnd, String content,
+                             int fontSize, Canvas canvas, Paint textPaint) {
+
+        Paint mPaintBox = new Paint();
+        mPaintBox.setColor(Color.LTGRAY);
+
+        // draw a rectangle
+        canvas.drawRect(new RectF(ptStart.x, ptStart.y, ptEnd.x, ptEnd.y), mPaintBox);
+
+        // draw text
+        canvas.drawText(content, ptStart.x + 1f, ptEnd.y - 2f, textPaint);
+    }
+
+    /**
+     * <p>
+     * calculate degree title on X axis
+     * </p>
+     * <p>
+     * X軸の目盛を計算する
+     * </p>
+     * <p>
+     * 计算X轴上显示的坐标值
+     * </p>
+     *
+     * @param value <p>
+     *              value for calculate
+     *              </p>
+     *              <p>
+     *              計算有用データ
+     *              </p>
+     *              <p>
+     *              计算用数据
+     *              </p>
+     * @return String
+     * <p>
+     * degree
+     * </p>
+     * <p>
+     * 目盛
+     * </p>
+     * <p>
+     * 坐标值
+     * </p>
+     */
+    protected float getAxisXGraduate(Object value) {
+
+        float length = super.getWidth() - axisMarginLeft - axisMarginRight - 4f;
+//		float valueLength = ((Float) value).floatValue() - axisMarginLeft;
+//				- axisMarginRight;
+        float valueLength = ((Float) value).floatValue() - 2f;
+        return valueLength / length;
+    }
+
+    protected String getAxisXValue(Object value) {
+        return String.valueOf(getAxisXGraduate(value));
+    }
+
+    public int getAxisXPos(Object value) {
+        return 0;
+    }
+
+    /**
+     * <p>
+     * calculate degree title on Y axis
+     * </p>
+     * <p>
+     * Y軸の目盛を計算する
+     * </p>
+     * <p>
+     * 计算Y轴上显示的坐标值
+     * </p>
+     *
+     * @param value <p>
+     *              value for calculate
+     *              </p>
+     *              <p>
+     *              計算有用データ
+     *              </p>
+     *              <p>
+     *              计算用数据
+     *              </p>
+     * @return String
+     * <p>
+     * degree
+     * </p>
+     * <p>
+     * 目盛
+     * </p>
+     * <p>
+     * 坐标值
+     * </p>
+     */
+    public String getAxisYGraduate(Object value) {
+
+        float length = super.getHeight() - axisMarginBottom - 2 * axisMarginTop;
+        float valueLength = length
+                - (((Float) value).floatValue() - axisMarginTop);
+
+        return String.valueOf(valueLength / length);
+    }
+
+    /**
+     * <p>
+     * draw cross line ,called when graph is touched
+     * </p>
+     * <p>
+     * 十字線を書く、グラプをタッチたら、メソードを呼び
+     * </p>
+     * <p>
+     * 在图表被点击后绘制十字线
+     * </p>
+     *
+     * @param canvas
+     */
+    protected void drawWithFingerClick(Canvas canvas) {
+        Paint mPaint = new Paint();
+        mPaint.setColor(Color.LTGRAY);
+
+        float lineHLength = getWidth() - 2f;
+        float lineVLength = getHeight() - 2f;
+
+        if (clickPostX > maxDatasLength && maxDatasLength > 0) {
+            clickPostX = maxDatasLength;
+        }
+        // draw text
+        if (isDisplayAxisXTitle()) {
+            lineVLength = lineVLength - axisMarginBottom;
+
+            if (clickPostX > 0 && clickPostY > 0) {
+                if (displayCrossXOnTouch) {
+                    // draw text
+                    Paint textPaint = new Paint();
+                    textPaint.setColor(Color.BLACK/*ChartLineColors.BORDERCOLOR*/);
+                    textPaint.setTextSize(longitudeFontSize);
+                    textPaint.setAntiAlias(true);
+
+                    // TODO calculate points to draw
+                    String content = getAxisXValue(clickPostX);
+                    LogUtils.e("chart", content);
+                    content = content.replace(currentYear, "");
+                    float textWidth = textPaint.measureText(content) + 2f;
+                    float PosSX = clickPostX - textWidth / 2f;
+                    float PosEX = clickPostX + textWidth / 2f;
+                    float PosXMax = this.getWidth() - this.getAxisMarginRight();
+                    if (PosEX > PosXMax) {
+                        PosEX = PosXMax;
+                        PosSX = PosXMax - textWidth;
+                    }
+                    PointF BoxVS = new PointF(PosSX, lineVLength + 3f);//new PointF(PosSX, lineVLength + 3f);
+                    PointF BoxVE = new PointF(PosEX, lineVLength + axisMarginBottom);
+
+                    drawTextBox(BoxVS, BoxVE, content, longitudeFontSize, canvas, textPaint);
+                }
+            }
+        }
+
+        if (isDisplayAxisYTitle()) {
+            lineHLength = lineHLength - getAxisMarginLeft();
+
+            if (clickPostX > 0 && clickPostY > 0) {
+                if (displayCrossYOnTouch) {
+                    Paint textPaint = new Paint();
+                    textPaint.setColor(Color.BLACK);
+                    textPaint.setTextSize(latitudeFontSize);
+                    textPaint.setAntiAlias(true);
+
+                    // TODO calculate points to draw
+                    String content = getAxisYGraduate(clickPostY);
+                    float textWidth = textPaint.measureText(content) + 2f;
+                    PointF BoxHS = new PointF(this.getWidth() - /*textWidth*/axisMarginLeft, clickPostY - latitudeFontSize / 2f);
+                    PointF BoxHE = new PointF(this.getWidth() - (axisMarginLeft - textWidth), clickPostY + latitudeFontSize / 2f);
+
+                    // draw text
+                    drawTextBox(BoxHS, BoxHE, content, latitudeFontSize, canvas, textPaint);
+                }
+            }
+        }
+
+        // draw line
+        if (clickPostX > 0 && clickPostY > 0) {
+            if (displayCrossXOnTouch) {
+                float x = (getAxisXPos(clickPostX) - this.getStartIndex()) * this.getItemWidth() + getClickPosXOffset();
+                x += this.getAxisMarginRight();
+                canvas.drawLine(x, 1f, x, lineVLength, mPaint);
+            }
+
+            if (displayCrossYOnTouch) {
+                canvas.drawLine(/*axisMarginLeft*/1f, clickPostY, /*axisMarginLeft*/0f
+                        + lineHLength - 1, clickPostY, mPaint);
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * draw border
+     * </p>
+     * <p>
+     * グラプのボーダーを書く
+     * </p>
+     * <p>
+     * 绘制边框
+     * </p>
+     *
+     * @param canvas
+     */
+    protected void drawBorder(Canvas canvas) {
+        float width = super.getWidth() - 2;
+        float height = super.getHeight() - 2;
+
+        Paint mPaint = new Paint();
+        mPaint.setColor(ChartLineColors.BORDERCOLOR);
+        mPaint.setStrokeWidth(1);
+        LogUtils.e("chart", "str" + mPaint.getStrokeWidth());
+        // draw a rectangle
+        canvas.drawLine(1f, 1f, 1f + width, 1f, mPaint);
+        canvas.drawLine(1f + width, 1f, 1f + width, 1f + height, mPaint);
+        canvas.drawLine(1f + width, 1f + height, 1f, 1f + height, mPaint);
+        canvas.drawLine(1f, 1f + height, 1f, 1f, mPaint);
+    }
+
+    /**
+     * <p>
+     * draw X Axis
+     * </p>
+     * <p>
+     * X軸を書く
+     * </p>
+     * <p>
+     * 绘制X轴
+     * </p>
+     *
+     * @param canvas
+     */
+    protected void drawXAxis(Canvas canvas) {
+
+        float length = super.getWidth();
+        float postY = super.getHeight() - axisMarginBottom - 1;
+
+        Paint mPaint = new Paint();
+        mPaint.setColor(axisXColor);
+
+        canvas.drawLine(0f, postY, length, postY, mPaint);
+
+    }
+
+    /**
+     * <p>
+     * draw Y Axis
+     * </p>
+     * <p>
+     * Y軸を書く
+     * </p>
+     * <p>
+     * 绘制Y轴
+     * </p>
+     *
+     * @param canvas
+     */
+    protected void drawYAxis(Canvas canvas) {
+
+        float length = super.getHeight() - axisMarginBottom;
+        float postX = axisMarginLeft + 1;
+
+        Paint mPaint = new Paint();
+        mPaint.setColor(axisXColor);
+
+        canvas.drawLine(postX, 0f, postX, length, mPaint);
+    }
+
+    /**
+     * <p>
+     * draw longitude lines
+     * </p>
+     * <p>
+     * 経線を書く
+     * </p>
+     * <p>
+     * 绘制经线
+     * </p>
+     *
+     * @param canvas
+     */
+    protected void drawAxisGridX(Canvas canvas) {
+
+        if (null != axisXTitles) {
+            int counts = axisXTitles.size();
+            float length = super.getHeight() - axisMarginBottom;
+
+            Paint mPaintLine = new Paint();
+            mPaintLine.setColor(longitudeColor);
+            mPaintLine.setAntiAlias(true);
+            if (dashLongitude) {
+                mPaintLine.setPathEffect(dashEffect);
+            }
+
+            Paint mPaintFont = new Paint();
+            mPaintFont.setColor(longitudeFontColor);
+            mPaintFont.setAntiAlias(true);
+            mPaintFont.setTextSize(longitudeFontSize);
+            if (counts > 1) {
+                float postOffset = (super.getWidth() - axisMarginLeft - 2 * axisMarginRight)
+                        / (counts - 1);
+                float offset = axisMarginLeft + axisMarginRight;
+                for (int i = 0; i < counts; i++) {
+                    // draw line
+//					if (displayLongitude) {
+//						float width_x = (this.getWidth() - offset + i * postOffset) / 8;//每条线的宽度
+//						for (int j = 0; j < 8 && i == 0; j++) {//画出7条线,使得图表显示区域8等分
+//							//FIXME : 
+//							canvas.drawLine(0 + j * width_x, 1f, 0 + j * width_x, length, mPaintLine);
+//						}
+                    canvas.drawLine(this.getWidth() - offset + i * postOffset, 1f, this.getWidth() - offset + i * postOffset, length, mPaintLine);
+//					}
+                    // draw title
+                    if (displayAxisXTitle) {
+                        if (i == counts - 1) {
+                            canvas.drawText(axisXTitles.get(i).replace(currentYear, ""), offset + i
+                                            * postOffset - 1
+                                            - mPaintFont.measureText(axisXTitles.get(i).replace(currentYear, "")) - axisMarginLeft, super.getHeight()
+                                            - axisMarginBottom + longitudeFontSize,
+                                    mPaintFont);
+                        } else if (0 == i) {
+                            canvas.drawText(axisXTitles.get(i).replace(currentYear, ""),
+                                    /*this.axisMarginLeft + */2f, super.getHeight()
+                                            - axisMarginBottom
+                                            + longitudeFontSize, mPaintFont);
+                        } else {
+                            canvas.drawText(axisXTitles.get(i).replace(currentYear, ""), offset + i
+                                            * postOffset
+                                            - mPaintFont.measureText(axisXTitles.get(i).replace(currentYear, "")) / 2f, super.getHeight()
+                                            - axisMarginBottom + longitudeFontSize,
+                                    mPaintFont);
+                        }
+                    }
+                }
+            } else {
+                canvas.drawLine(super.getWidth() - axisMarginLeft - axisMarginRight, 1f, super.getWidth() - axisMarginRight - axisMarginLeft, length, mPaintLine);
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * draw latitude lines
+     * </p>
+     * <p>
+     * 緯線を書く
+     * </p>
+     * <p>
+     * 绘制纬线
+     * </p>
+     *
+     * @param canvas
+     */
+    protected void drawAxisGridY(Canvas canvas) {
+        if (null != axisYTitles) {
+            int counts = axisYTitles.size();
+            float length = super.getWidth() - axisMarginLeft;
+
+            Paint mPaintLine = new Paint();
+            mPaintLine.setColor(latitudeColor);
+            mPaintLine.setAntiAlias(true);
+            if (dashLatitude) {
+                mPaintLine.setPathEffect(dashEffect);
+            }
+
+            Paint mPaintFont = new Paint();
+            mPaintFont.setColor(latitudeFontColor);
+            mPaintFont.setAntiAlias(true);
+            mPaintFont.setTextSize(latitudeFontSize);
+            if (!"".equals(getTasLastPrice())) {// 最新价格在界面上的显示
+                mPaintFont.setColor(getNewPriceColor());
+                String height = axisYTitles.get(axisYTitles.size() - 1);
+                String low = axisYTitles.get(0);
+                double sum = Double.parseDouble(height) - Double.parseDouble(low);// 最高价和最低价的差值
+                double sum_lastPrice = Double.parseDouble(getTasLastPrice()) - Double.parseDouble(low);// 最新价和最底价的差值
+                // 针对每个像素高度占用值
+                double cell = ((this.getHeight() - axisMarginBottom - axisMarginTop) / sum);
+                float _height = (float) (sum_lastPrice * cell);
+                /**
+                 * 画出当前的最新价
+                 */
+                canvas.drawText(getTasLastPrice(), super.getWidth() - axisMarginLeft,
+                        this.getHeight() - _height - axisMarginBottom + latitudeFontSize / 2f, mPaintFont);
+
+
+                mPaintFont.setColor(latitudeFontColor);// 还原颜色
+
+                /**
+                 * 画出实时行情的标线
+                 */
+                canvas.drawLine(1, this.getHeight() - _height - axisMarginBottom - 1,
+                        super.getWidth() - axisMarginLeft - axisMarginRight, this.getHeight() - _height - axisMarginBottom - 1, mPaintFont);
+
+            }
+
+            if (counts > 1) {
+                float postOffset = (super.getHeight() - axisMarginBottom - 2 * axisMarginTop)
+                        / (counts - 1);
+                float offset = super.getHeight() - axisMarginBottom - axisMarginTop;
+                float posX = 0;
+                for (int i = 0; i < counts; i++) {
+                    // draw line
+                    if (displayLatitude) {
+                        if (i != 0 && i != counts - 1) {
+                            canvas.drawLine(0,
+                                    offset - i * postOffset, super.getWidth() - axisMarginLeft, offset - i * postOffset,
+                                    mPaintLine);
+                        }
+                    }
+                    // draw title
+                    posX = axisMarginLeft - mPaintFont.measureText(axisYTitles.get(i)) - 1;
+                    if (displayAxisYTitle) {
+                        if (i == counts - 1) {
+                            canvas.drawText(axisYTitles.get(i), super.getWidth() - axisMarginLeft, offset - i
+                                            * postOffset + latitudeFontSize / 6f + latitudeFontSize / 2f,
+                                    mPaintFont);
+                        } else if (0 == i) {
+                            canvas.drawText(axisYTitles.get(i), super.getWidth() - axisMarginLeft,
+                                    super.getHeight() - this.axisMarginBottom
+                                            - 2f, mPaintFont);
+                        } else {
+                            canvas.drawText(axisYTitles.get(i), super.getWidth() - axisMarginLeft, offset - i
+                                            * postOffset + latitudeFontSize / 2f,
+                                    mPaintFont);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Zoom in the graph
+     * </p>
+     * <p>
+     * 拡大表示する。
+     * </p>
+     * <p>
+     * 放大表示
+     * </p>
+     */
+    protected boolean zoomIn() {
+        return false;
+    }
+
+    /**
+     * <p>
+     * Zoom out the grid
+     * </p>
+     * <p>
+     * 縮小表示する。
+     * </p>
+     * <p>
+     * 缩小
+     * </p>
+     */
+    protected boolean zoomOut() {
+        return false;
+    }
+
+    public NumberFormat getNumberFormat() {
+        NumberFormat nf = NumberFormat.getInstance();
+        nf.setGroupingUsed(false);
+        nf.setMaximumFractionDigits(decimalNum);
+        nf.setMinimumFractionDigits(decimalNum);
+
+        return nf;
+    }
+
+    public NumberFormat getNumberFormat(boolean b) {
+        NumberFormat nf = NumberFormat.getInstance();
+        nf.setGroupingUsed(false);
+        if (b == true) {
+            decimalNum = 0;
+        } else {
+            decimalNum = 2;
+        }
+        nf.setMaximumFractionDigits(decimalNum);
+        nf.setMinimumFractionDigits(decimalNum);
+
+        return nf;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param event
+     *
+     * @see
+     * cn.limc.androidcharts.event.ITouchEventResponse#notifyEvent(GridChart)
+     */
+    public void notifyEvent(int eventId, GridChart chart) {
+        PointF point = chart.getTouchPoint();
+        if (null != point) {
+            clickPostX = point.x;
+            clickPostY = point.y;
+        }
+        touchPoint = new PointF(clickPostX, clickPostY);
+        super.invalidate();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param event
+     *
+     * @see
+     * cn.limc.androidcharts.event.ITouchEventNotify#addNotify(ITouchEventResponse
+     * )
+     */
+    public void addNotify(ITouchEventResponse notify) {
+        if (null == notifyList) {
+            notifyList = new ArrayList<ITouchEventResponse>();
+        }
+        notifyList.add(notify);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param event
+     *
+     * @see cn.limc.androidcharts.event.ITouchEventNotify#removeNotify(int)
+     */
+    public void removeNotify(int i) {
+        if (null != notifyList && notifyList.size() > i) {
+            notifyList.remove(i);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param event
+     *
+     * @see cn.limc.androidcharts.event.ITouchEventNotify#removeNotify()
+     */
+    public void removeAllNotify() {
+        if (null != notifyList) {
+            notifyList.clear();
+        }
+    }
+
+    public void notifyEventAll(int eventId, GridChart chart) {
+        if (null != notifyList) {
+            for (int i = 0; i < notifyList.size(); i++) {
+                ITouchEventResponse ichart = notifyList.get(i);
+                ichart.notifyEvent(eventId, chart);
+            }
+        }
+    }
+
+    /**
+     * @return the axisXColor
+     */
+    public int getAxisXColor() {
+        return axisXColor;
+    }
+
+    /**
+     * @param axisXColor the axisXColor to set
+     */
+    public void setAxisXColor(int axisXColor) {
+        this.axisXColor = axisXColor;
+    }
+
+    /**
+     * @return the axisYColor
+     */
+    public int getAxisYColor() {
+        return axisYColor;
+    }
+
+    /**
+     * @param axisYColor the axisYColor to set
+     */
+    public void setAxisYColor(int axisYColor) {
+        this.axisYColor = axisYColor;
+    }
+
+    /**
+     * @return the longitudeColor
+     */
+    public int getLongitudeColor() {
+        return longitudeColor;
+    }
+
+    /**
+     * @param longitudeColor the longitudeColor to set
+     */
+    public void setLongitudeColor(int longitudeColor) {
+        this.longitudeColor = longitudeColor;
+    }
+
+    /**
+     * @return the latitudeColor
+     */
+    public int getLatitudeColor() {
+        return latitudeColor;
+    }
+
+    /**
+     * @param latitudeColor the latitudeColor to set
+     */
+    public void setLatitudeColor(int latitudeColor) {
+        this.latitudeColor = latitudeColor;
+    }
+
+    /**
+     * @return the notifyAxisMarginLeft
+     */
+    public float getNotifyAxisMarginLeft() {
+        return notifyAxisMarginLeft;
+    }
+
+    /**
+     * @param marginLeft the notify AxisMarginLeft to set
+     */
+    public void setNotifyAxisMarginLeft(float marginLeft) {
+        this.notifyAxisMarginLeft = marginLeft;
+    }
+
+    /**
+     * @return the axisMarginLeft
+     */
+    public float getAxisMarginLeft() {
+        return axisMarginLeft;
+    }
+
+    /**
+     * @param axisMarginLeft the axisMarginLeft to set
+     */
+    public void setAxisMarginLeft(float axisMarginLeft) {
+        this.axisMarginLeft = axisMarginLeft;
+    }
+
+    /**
+     * @return the axisMarginBottom
+     */
+    public float getAxisMarginBottom() {
+        return axisMarginBottom;
+    }
+
+    /**
+     * @param axisMarginBottom the axisMarginBottom to set
+     */
+    public void setAxisMarginBottom(float axisMarginBottom) {
+        this.axisMarginBottom = axisMarginBottom;
+    }
+
+    /**
+     * @return the axisMarginTop
+     */
+    public float getAxisMarginTop() {
+        return axisMarginTop;
+    }
+
+    /**
+     * @param axisMarginTop the axisMarginTop to set
+     */
+    public void setAxisMarginTop(float axisMarginTop) {
+        this.axisMarginTop = axisMarginTop;
+    }
+
+    /**
+     * @return the axisMarginRight
+     */
+    public float getAxisMarginRight() {
+        return axisMarginRight;
+    }
+
+    /**
+     * @param axisMarginRight the axisMarginRight to set
+     */
+    public void setAxisMarginRight(float axisMarginRight) {
+        this.axisMarginRight = axisMarginRight;
+    }
+
+    /**
+     * @return the displayAxisXTitle
+     */
+    public boolean isDisplayAxisXTitle() {
+        return displayAxisXTitle;
+    }
+
+    /**
+     * @param displayAxisXTitle the displayAxisXTitle to set
+     */
+    public void setDisplayAxisXTitle(boolean displayAxisXTitle) {
+        this.displayAxisXTitle = displayAxisXTitle;
+    }
+
+    /**
+     * @return the displayAxisYTitle
+     */
+    public boolean isDisplayAxisYTitle() {
+        return displayAxisYTitle;
+    }
+
+    /**
+     * @param displayAxisYTitle the displayAxisYTitle to set
+     */
+    public void setDisplayAxisYTitle(boolean displayAxisYTitle) {
+        this.displayAxisYTitle = displayAxisYTitle;
+    }
+
+    /**
+     * @return the latitudeNum
+     */
+    public int getLatitudeNum() {
+        return latitudeNum;
+    }
+
+    /**
+     * @param latitudeNum the latitudeNum to set
+     */
+    public void setLatitudeNum(int latitudeNum) {
+        this.latitudeNum = latitudeNum;
+    }
+
+    /**
+     * @return the longitudeNum
+     */
+    public int getLongitudeNum() {
+        return longitudeNum;
+    }
+
+    /**
+     * @param longitudeNum the longitudeNum to set
+     */
+    public void setLongitudeNum(int longitudeNum) {
+        this.longitudeNum = longitudeNum;
+    }
+
+    /**
+     * @return the displayLongitude
+     */
+    public boolean isDisplayLongitude() {
+        return displayLongitude;
+    }
+
+    /**
+     * @param displayLongitude the displayLongitude to set
+     */
+    public void setDisplayLongitude(boolean displayLongitude) {
+        this.displayLongitude = displayLongitude;
+    }
+
+    /**
+     * @return the dashLongitude
+     */
+    public boolean isDashLongitude() {
+        return dashLongitude;
+    }
+
+    /**
+     * @param dashLongitude the dashLongitude to set
+     */
+    public void setDashLongitude(boolean dashLongitude) {
+        this.dashLongitude = dashLongitude;
+    }
+
+    /**
+     * @return the displayLatitude
+     */
+    public boolean isDisplayLatitude() {
+        return displayLatitude;
+    }
+
+    /**
+     * @param displayLatitude the displayLatitude to set
+     */
+    public void setDisplayLatitude(boolean displayLatitude) {
+        this.displayLatitude = displayLatitude;
+    }
+
+    /**
+     * @return the dashLatitude
+     */
+    public boolean isDashLatitude() {
+        return dashLatitude;
+    }
+
+    /**
+     * @param dashLatitude the dashLatitude to set
+     */
+    public void setDashLatitude(boolean dashLatitude) {
+        this.dashLatitude = dashLatitude;
+    }
+
+    /**
+     * @return the dashEffect
+     */
+    public PathEffect getDashEffect() {
+        return dashEffect;
+    }
+
+    /**
+     * @param dashEffect the dashEffect to set
+     */
+    public void setDashEffect(PathEffect dashEffect) {
+        this.dashEffect = dashEffect;
+    }
+
+    /**
+     * @return the displayBorder
+     */
+    public boolean isDisplayBorder() {
+        return displayBorder;
+    }
+
+    /**
+     * @param displayBorder the displayBorder to set
+     */
+    public void setDisplayBorder(boolean displayBorder) {
+        this.displayBorder = displayBorder;
+    }
+
+    /**
+     * @return the borderColor
+     */
+    public int getBorderColor() {
+        return borderColor;
+    }
+
+    /**
+     * @param borderColor the borderColor to set
+     */
+    public void setBorderColor(int borderColor) {
+        this.borderColor = borderColor;
+    }
+
+    /**
+     * @return the longitudeFontColor
+     */
+    public int getLongitudeFontColor() {
+        return longitudeFontColor;
+    }
+
+    /**
+     * @param longitudeFontColor the longitudeFontColor to set
+     */
+    public void setLongitudeFontColor(int longitudeFontColor) {
+        this.longitudeFontColor = longitudeFontColor;
+    }
+
+    /**
+     * @return the longitudeFontSize
+     */
+    public int getLongitudeFontSize() {
+        return longitudeFontSize;
+    }
+
+    /**
+     * @param longitudeFontSize the longitudeFontSize to set
+     */
+    public void setLongitudeFontSize(int longitudeFontSize) {
+        this.longitudeFontSize = longitudeFontSize;
+        if (this.longitudeFontSize > this.axisMarginBottom) {
+            setAxisMarginBottom(longitudeFontSize + longitudeFontSize / 4);
+        }
+    }
+
+    /**
+     * @return the latitudeFontColor
+     */
+    public int getLatitudeFontColor() {
+        return latitudeFontColor;
+    }
+
+    /**
+     * @param latitudeFontColor the latitudeFontColor to set
+     */
+    public void setLatitudeFontColor(int latitudeFontColor) {
+        this.latitudeFontColor = latitudeFontColor;
+    }
+
+    /**
+     * @return the latitudeFontSize
+     */
+    public int getLatitudeFontSize() {
+        return latitudeFontSize;
+    }
+
+    /**
+     * @param latitudeFontSize the latitudeFontSize to set
+     */
+    public void setLatitudeFontSize(int latitudeFontSize) {
+        this.latitudeFontSize = latitudeFontSize;
+    }
+
+    /**
+     * @return the axisXTitles
+     */
+    public List<String> getAxisXTitles() {
+        return axisXTitles;
+    }
+
+    /**
+     * @param axisXTitles the axisXTitles to set
+     */
+    public void setAxisXTitles(List<String> axisXTitles) {
+        this.axisXTitles = axisXTitles;
+    }
+
+    /**
+     * @return the axisYTitles
+     */
+    public List<String> getAxisYTitles() {
+        return axisYTitles;
+    }
+
+    /**
+     * @param axisYTitles the axisYTitles to set
+     */
+    public void setAxisYTitles(List<String> axisYTitles) {
+        this.axisYTitles = axisYTitles;
+    }
+
+    /**
+     * @return the axisYMaxTitleLength
+     */
+    public int getAxisYMaxTitleLength() {
+        return axisYMaxTitleLength;
+    }
+
+    /**
+     * @param axisYMaxTitleLength the axisYMaxTitleLength to set
+     */
+    public void setAxisYMaxTitleLength(int axisYMaxTitleLength) {
+        this.axisYMaxTitleLength = axisYMaxTitleLength;
+    }
+
+    /**
+     * @return the displayCrossXOnTouch
+     */
+    public boolean isDisplayCrossXOnTouch() {
+        return displayCrossXOnTouch;
+    }
+
+    /**
+     * @param displayCrossXOnTouch the displayCrossXOnTouch to set
+     */
+    public void setDisplayCrossXOnTouch(boolean displayCrossXOnTouch) {
+        this.displayCrossXOnTouch = displayCrossXOnTouch;
+    }
+
+    /**
+     * @return the displayCrossYOnTouch
+     */
+    public boolean isDisplayCrossYOnTouch() {
+        return displayCrossYOnTouch;
+    }
+
+    /**
+     * @param displayCrossYOnTouch the displayCrossYOnTouch to set
+     */
+    public void setDisplayCrossYOnTouch(boolean displayCrossYOnTouch) {
+        this.displayCrossYOnTouch = displayCrossYOnTouch;
+    }
+
+    /**
+     * for new: + chart.getAxisMarginLeft()   //margin right
+     *
+     * @return the clickPostX
+     */
+    public float getClickPostX() {
+        return clickPostX;
+    }
+
+    /**
+     * @param clickPostX the clickPostX to set
+     */
+    public void setClickPostX(float clickPostX) {
+        this.clickPostX = clickPostX;
+    }
+
+    /**
+     * @return the clickPostY
+     */
+    public float getClickPostY() {
+        return clickPostY;
+    }
+
+    /**
+     * @param clickPostY the clickPostY to set
+     */
+    public void setClickPostY(float clickPostY) {
+        this.clickPostY = clickPostY;
+    }
+
+    /**
+     * @return the notifyList
+     */
+    public List<ITouchEventResponse> getNotifyList() {
+        return notifyList;
+    }
+
+    /**
+     * @param notifyList the notifyList to set
+     */
+    public void setNotifyList(List<ITouchEventResponse> notifyList) {
+        this.notifyList = notifyList;
+    }
+
+    /**
+     * @return the touchPoint
+     */
+    public PointF getTouchPoint() {
+        return touchPoint;
+    }
+
+    /**
+     * @param touchPoint the touchPoint to set
+     */
+    public void setTouchPoint(PointF touchPoint) {
+        this.touchPoint = touchPoint;
+    }
+
+    /**
+     * @return the touchDistanceX
+     */
+    public float getTouchDistanceX() {
+        return touchDistanceX;
+    }
+
+    /**
+     * @param touchDistanceX touch Point2 the touchPoint2 to set
+     */
+    public void setTouchDistanceX(float touchDistanceX) {
+        this.touchDistanceX = touchDistanceX;
+    }
+
+    /**
+     * @return the backgroundColor
+     */
+    public int getBackgroundColor() {
+        return backgroundColor;
+    }
+
+    /**
+     * @param backgroundColor the backgroundColor to set
+     */
+    public void setBackgroundColor(int backgroundColor) {
+        this.backgroundColor = backgroundColor;
+    }
+
+    public int getDecimalNum() {
+        return decimalNum;
+    }
+
+    public void setDecimalNum(int decimalNum) {
+        this.decimalNum = decimalNum;
+    }
+
+    public ScaleGestureDetector getScaleGestureDetector() {
+        return this.mScaleDetector;
+    }
+
+    @Override
+    public boolean onScale(ScaleGestureDetector detector) {
+        // TODO Auto-generated method stub
+
+        return false;
+    }
+
+    @Override
+    public boolean onScaleBegin(ScaleGestureDetector detector) {
+        // TODO Auto-generated method stub
+//		isOnScale = true;
+        return true;
+    }
+
+    @Override
+    public void onScaleEnd(ScaleGestureDetector detector) {
+        // TODO Auto-generated method stub
+//		isOnScale = false;
+    }
+
+    public boolean isEnableCrossOnTouch() {
+        return enableCrossOnTouch;
+    }
+
+    public void setEnableCrossOnTouch(boolean enableCrossOnTouch) {
+        this.enableCrossOnTouch = enableCrossOnTouch;
+    }
+
+    public int getStartIndex() {
+        return startIndex;
+    }
+
+    public void setStartIndex(int startIndex) {
+        this.startIndex = startIndex;
+    }
+
+    public String getTasLastPrice() {
+        return tasLastPrice;
+    }
+
+    public void setTasLastPrice(String tasLastPrice) {
+        this.tasLastPrice = tasLastPrice;
+    }
+
+
+    public void setLinesData(int linesData) {
+        this.linesData = linesData;
+    }
+
+    private float maxDatasLength = 0;
+
+    public void setMaxDatasLength(float maxDatasLength) {
+        this.maxDatasLength = maxDatasLength;
+    }
+
+    public boolean isCan_move() {
+        return can_move;
+    }
+
+    /**
+     * 分时图必须设置这个值
+     *
+     * @param can_move
+     */
+    public void setCan_move(boolean can_move) {
+        this.can_move = can_move;
+    }
+
+    public int getNewPriceColor() {
+        return newPriceColor;
+    }
+
+    public void setNewPriceColor(int newPriceColor) {
+        this.newPriceColor = newPriceColor;
+    }
+
+}

+ 15 - 0
RMA/chart/src/main/java/com/desfate/chart/IChart.java

@@ -0,0 +1,15 @@
+package com.desfate.chart;
+
+/**
+ * <p>
+ * Interface for all charts
+ * </p>
+ * <p>
+ * 全部チャートのベースインタフェース。
+ * </p>
+ * <p>
+ * 所有图表对象的接口
+ * </p>
+ */
+public interface IChart {
+}

+ 807 - 0
RMA/chart/src/main/java/com/desfate/chart/LineChart.java

@@ -0,0 +1,807 @@
+package com.desfate.chart;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PointF;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+
+import com.desfate.chart.entity.LineEntity;
+import com.desfate.chart.event.ITouchEventNotify;
+import com.desfate.chart.util.MathUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <p>
+ * LineChart is a kind of graph that draw some lines on a GridChart
+ * </p>
+ * <p>
+ * LineChartはGridChartの表面でラインを書いたラインチャードです。
+ * </p>
+ * <p>
+ * LineChart是在GridChart上绘制一条或多条线条的图
+ * </p>
+ *
+ * @see GridChart
+ */
+public class LineChart extends GridChart {
+
+    private static final String TAG = "LineChart";
+
+    /**
+     * <p>
+     * data to draw lines
+     * </p>
+     * <p>
+     * ラインを書く用データ
+     * </p>
+     * <p>
+     * 绘制线条用的数据
+     * </p>
+     */
+    private List<LineEntity> lineData;
+    private List<LineEntity> newLineData;
+
+    /**
+     * <p>
+     * max points of a single line
+     * </p>
+     * <p>
+     * ラインの最大ポイント数
+     * </p>
+     * <p>
+     * 线条的最大表示点数
+     * </p>
+     */
+    private int maxPointNum;
+    private String lastPrice;
+    /**
+     * <p>
+     * min value of Y axis
+     * </p>
+     * <p>
+     * Y軸の最小値
+     * </p>
+     * <p>
+     * Y的最小表示值
+     * </p>
+     */
+    private double minValue = Double.MAX_VALUE;
+
+    /**
+     * <p>
+     * max value of Y axis
+     * </p>
+     * <p>
+     * Y軸の最大値
+     * </p>
+     * <p>
+     * Y的最大表示值
+     * </p>
+     */
+    private double maxValue;
+    private int datasNum;
+
+    // FIXME:
+    public void setDatasNum(int datasNum) {
+        this.datasNum = datasNum;
+    }
+
+    public int getDatasNum() {
+        return datasNum;
+    }
+
+    private int startIndex = 0;// !< @brief 起始位置
+    private boolean isChangeLineData = false;
+
+    /**
+     * 当前行情的最新价
+     */
+    private String tasLastPrice = "";
+
+    public String getTasLastPrice() {
+        return tasLastPrice;
+    }
+
+    public void setTasLastPrice(String tasLastPrice) {
+        this.tasLastPrice = tasLastPrice;
+        super.setTasLastPrice(tasLastPrice);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param context
+     *
+     * (Context)
+     */
+    public LineChart(Context context) {
+        super(context);
+        initDatas();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param context
+     *
+     * @param attrs
+     *
+     * @param defStyle
+     *
+     * (Context, AttributeSet, int)
+     */
+    public LineChart(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        initDatas();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param context
+     *
+     * @param attrs
+     *
+     *
+     *
+     * (Context, AttributeSet)
+     */
+    public LineChart(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initDatas();
+    }
+
+    /**
+     * 设置默认初始值
+     */
+    private void initDatas() {
+        setCan_move(true);//分时图必须设置的值
+    }
+
+    protected void onUpdate() {
+        updateLineData();
+        checkMember();
+        initMaxAndMin();
+//		initAxisY();
+        initAxisX();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * <p>Called when is going to draw this chart<p> <p>チャートを書く前、メソッドを呼ぶ<p>
+     * <p>绘制图表时调用<p>
+     *
+     * @param canvas
+     *
+     * @see android.view.View#onDraw(android.graphics.Canvas)
+     */
+    @Override
+    protected void onDraw(Canvas canvas) {
+        onUpdate();
+        super.onDraw(canvas);
+    }
+
+    @Override
+    protected void drawChart(Canvas canvas) {
+        super.drawChart(canvas);
+        // draw lines
+        if (null != this.lineData) {
+            drawLines(canvas);
+        }
+    }
+
+    protected void initMaxAndMin() {
+        // calculate max value and min value
+        if (null != this.lineData) {
+
+            for (int i = 0; i < lineData.size(); i++) {
+                LineEntity line = lineData.get(i);
+                if (line.isDisplay()) {
+                    List<Double> lineData = line.getLineData();
+
+                    if (lineData != null) {
+
+                        int minIndex = lineData.size() - this.getMaxPointNum();
+                        if (startIndex > minIndex && minIndex > 0) {
+                            startIndex = minIndex;
+                        }
+
+                        for (int j = startIndex; j >= 0 && j < lineData.size()
+                                && j < startIndex + this.getMaxPointNum(); j++) {
+                            float value = lineData.get(j).floatValue();
+                            // if (i == 0 && j == startIndex) {
+                            //
+                            // maxValue = value;
+                            // minValue = value;
+                            // } else {
+                            if (value > maxValue) {
+                                maxValue = value;
+                            } else if (value < minValue && value > 0) {
+                                minValue = value;
+                            }
+                            // }
+                        }
+
+                        if (maxValue == minValue) {
+                            maxValue = maxValue * (1.05);
+                            minValue = minValue * 0.95;
+                        } else {
+
+                        }
+                    }
+                }
+            }
+
+        }
+    }
+
+    /**
+     * <p>
+     * initialize degrees on Y axis
+     * </p>
+     * <p>
+     * X軸の目盛を初期化
+     * </p>
+     * <p>
+     * 初始化X轴的坐标值
+     * </p>
+     */
+    protected void initAxisX() {
+        List<String> TitleX = new ArrayList<String>();
+        if (lineData != null && lineData.size() > 0) {
+            List<String> listTime = lineData.get(0).getLineTime();
+            if (null != listTime) {
+                float average = this.getMaxPointNum() / this.getLongitudeNum();
+                for (int i = 0; i < this.getLongitudeNum(); i++) {
+                    int index = (int) Math.floor(i * average);
+                    if (index > this.getMaxPointNum() - 1) {
+                        index = this.getMaxPointNum() - 1;
+                    }
+                    if (startIndex + index < listTime.size() && startIndex + index >= 0)
+                        TitleX.add(listTime.get(startIndex + index));
+                }
+
+                int end = startIndex + this.getMaxPointNum() - 1;
+                if (end < listTime.size() && end >= 0)
+                    TitleX.add(listTime.get(end));
+            }
+        }
+        super.setAxisXTitles(TitleX);
+    }
+
+    /**
+     * <p>
+     * initialize degrees on Y axis
+     * </p>
+     * <p>
+     * Y軸の目盛を初期化
+     * </p>
+     * <p>
+     * 初始化Y轴的坐标值
+     * </p>
+     */
+    protected void initAxisY() {
+
+        List<String> TitleY = new ArrayList<String>();
+        double average = (maxValue - minValue) * 10000 / this.getLatitudeNum() / 10000;
+        double ave = Math.pow(10, -this.getDecimalNum());
+        if (average < ave) {
+            average = ave;
+        }
+        // calculate degrees on Y axis
+        for (int i = 0; i < this.getLatitudeNum(); i++) {
+            String value = getNumberFormat().format(minValue + i * average);
+            if (value.length() < super.getAxisYMaxTitleLength()) {
+                while (value.length() < super.getAxisYMaxTitleLength()) {
+                    value = new String(" ") + value;
+                }
+            }
+            TitleY.add(value);
+        }
+        // calculate last degrees by use max value
+        double dLast = minValue + this.getLatitudeNum() * average;
+        if (dLast > maxValue) {
+            maxValue = dLast;
+        }
+        String value = getNumberFormat().format(maxValue);
+        if (value.length() < super.getAxisYMaxTitleLength()) {
+            while (value.length() < super.getAxisYMaxTitleLength()) {
+                value = new String(" ") + value;
+            }
+        }
+        TitleY.add(value);
+
+        super.setAxisYTitles(TitleY);
+        if (super.isDisplayAxisYTitle() && super.getAxisYTitles() != null) {
+            int counts = super.getAxisYTitles().size();
+            Paint mPaintFont = new Paint();
+            mPaintFont.setTextSize(super.getLatitudeFontSize());
+            float textWidth = 0;
+            float maxAxisMarginLeft = 0;
+            for (int i = 0; i < counts; i++) {
+                textWidth = mPaintFont.measureText(super.getAxisYTitles().get(i)) + 10;
+                if (textWidth > maxAxisMarginLeft) {
+                    maxAxisMarginLeft = textWidth;
+                }
+            }
+
+            if (maxAxisMarginLeft > super.getNotifyAxisMarginLeft()) {
+                super.setAxisMarginLeft(maxAxisMarginLeft);
+                notifyEventAll(ITouchEventNotify.EVENT_MOVE, this);
+            }
+        }
+    }
+
+    /**
+     * @param tasPreprice 昨收
+     * @param lastPrice   最新价
+     */
+    public void initAxisY(String tasPreprice, String lastPrice, String hqExchFigures) {
+        double average = Double.parseDouble(tasPreprice);
+        super.setTasLastPrice(MathUtil.roundNum(lastPrice, Integer.parseInt(hqExchFigures)));
+        List<String> TitleY = new ArrayList<String>();
+        if (getMaxValue() > 0) {
+            if (Math.abs(average - this.getMaxValue()) >= Math.abs(average - this.getMinValue())) {
+
+                this.setMinValue(Math.abs(average - (this.getMaxValue() - average)));
+                average = (this.getMaxValue() - average) / (this.getLatitudeNum() / 2);
+
+            } else {
+                if (average <= getMaxValue()) {
+                    this.setMaxValue(Math.abs(average + (this.getMinValue()) - average));
+                    average = Math.abs((this.getMinValue() - average)) / (this.getLatitudeNum() / 2);
+                } else {
+                    this.setMaxValue(Math.abs(average + (average - this.getMinValue())));
+                    average = Math.abs((this.getMinValue() - average)) / (this.getLatitudeNum() / 2);
+                }
+            }
+        } else {
+            return;
+        }
+        for (int i = 0; i < this.getLatitudeNum(); i++) {
+            String value = getNumberFormat().format(this.getMinValue() + i * average);
+            if (value.length() < super.getAxisYMaxTitleLength()) {
+                while (value.length() < super.getAxisYMaxTitleLength()) {
+                    value = new String(" ") + value;
+                }
+            }
+            TitleY.add(MathUtil.roundNum(value, Integer.parseInt(hqExchFigures)));
+        }
+        // calculate last degrees by use max value
+        double dLast = minValue + this.getLatitudeNum() * average;
+        if (dLast > maxValue) {
+            maxValue = dLast;
+        }
+        String value = getNumberFormat().format(maxValue);
+        if (value.length() < super.getAxisYMaxTitleLength()) {
+            while (value.length() < super.getAxisYMaxTitleLength()) {
+                value = new String(" ") + value;
+            }
+        }
+        TitleY.add(MathUtil.roundNum(value, Integer.parseInt(hqExchFigures)));
+        super.setAxisYTitles(TitleY);
+        if (super.isDisplayAxisYTitle() && super.getAxisYTitles() != null) {
+            int counts = super.getAxisYTitles().size();
+            Paint mPaintFont = new Paint();
+            mPaintFont.setTextSize(super.getLatitudeFontSize());
+            float textWidth = 0;
+            float maxAxisMarginLeft = 0;
+            for (int i = 0; i < counts; i++) {
+                textWidth = mPaintFont.measureText(super.getAxisYTitles().get(i)) + 10;
+                if (textWidth > maxAxisMarginLeft) {
+                    maxAxisMarginLeft = textWidth;
+                }
+            }
+
+            if (maxAxisMarginLeft > super.getNotifyAxisMarginLeft()) {
+                super.setAxisMarginLeft(maxAxisMarginLeft);
+                notifyEventAll(ITouchEventNotify.EVENT_MOVE, this);
+            }
+        }
+    }
+
+    @Override
+    public int getAxisXPos(Object value) {
+        float graduate = Float.valueOf(super.getAxisXGraduate(value));
+        int pos = (int) Math.floor(graduate * (maxPointNum - 1));
+        if (pos >= maxPointNum) {
+            pos = maxPointNum - 1;
+        } else if (pos < 0) {
+            pos = 0;
+        }
+        pos += this.getStartIndex();
+        return pos;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param value
+     *
+     * @see
+     * (Object)
+     */
+    @Override
+    public String getAxisXValue(Object value) {
+        int index = getAxisXPos(value);
+        String ret = "";
+        if (lineData.size() <= 0) {
+            return "0";
+        } else {
+            List<String> listTime = lineData.get(0).getLineTime();
+            if (index < listTime.size()) {
+                ret = listTime.get(index);
+            }
+        }
+
+        return ret;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param value
+     *
+     * @see
+     * (Object)
+     */
+    @Override
+    public String getAxisYGraduate(Object value) {
+        float graduate = Float.valueOf(super.getAxisYGraduate(value));
+        double dYVal = graduate * (maxValue - minValue) + minValue;
+        String strYVal = getNumberFormat().format(dYVal);
+        return strYVal;
+    }
+
+    /**
+     * <p>
+     * draw lines
+     * </p>
+     * <p>
+     * ラインを書く
+     * </p>
+     * <p>
+     * 绘制线条
+     * </p>
+     *
+     * @param canvas
+     */
+    protected void drawLines(Canvas canvas) {
+        // distance between two points
+        float lineLength = getItemWidth();
+        // start point‘s X
+        float startX;
+
+        // draw lines
+        for (int i = 0; i < lineData.size(); i++) {
+            LineEntity line = lineData.get(i);
+            if (line.isDisplay()) {
+                Paint mPaint = new Paint();
+                mPaint.setColor(line.getLineColor());
+                mPaint.setAntiAlias(true);
+                mPaint.setStrokeWidth(2.0f);
+                List<Double> lineData = line.getLineData();
+                // set start point’s X
+                startX = /*super.getAxisMarginLeft() + super.getAxisMarginRight()*/ 2f;// 绘图区域左右对称缩进
+                // start point
+                PointF ptFirst = null;
+                if (lineData != null) {
+
+                    int minIndex = lineData.size() - this.getMaxPointNum();
+                    if (startIndex > minIndex && minIndex > 0) {
+                        startIndex = minIndex;
+                    }
+
+                    for (int j = startIndex; j >= 0 && j < lineData.size() && j < startIndex + this.getMaxPointNum(); j++) {
+
+                        float value = lineData.get(j).floatValue();
+                        // calculate Y
+                        float valueY = super.getAxisMarginTop()
+                                + (float) ((1f - (value - this.getMinValue()) * 10000000
+                                / ((this.getMaxValue() - this.getMinValue()) * 10000) / 1000) * (super
+                                .getHeight() - super.getAxisMarginBottom() - 2 * super.getAxisMarginTop()));
+                        // if is not last point connect to previous point
+                        if (j > startIndex) {
+                            canvas.drawLine(ptFirst.x, ptFirst.y, startX, valueY, mPaint);
+                        }
+                        // reset
+                        ptFirst = new PointF(startX, valueY);
+                        startX = startX + lineLength;
+                        if (j >= datasNum - 2) {
+                            super.setMaxDatasLength((int) (startX));
+                            break;
+                        }
+                        //super.setMaxDatasLength(startX);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Zoom in the graph
+     * </p>
+     * <p>
+     * 拡大表示する。
+     * </p>
+     * <p>
+     * 放大表示
+     * </p>
+     */
+    protected boolean zoomIn() {
+        if (maxPointNum > 10) {
+            maxPointNum = maxPointNum - 3;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * <p>
+     * Zoom out the grid
+     * </p>
+     * <p>
+     * 縮小表示する。
+     * </p>
+     * <p>
+     * 缩小
+     * </p>
+     */
+    protected boolean zoomOut() {
+        LineEntity line = lineData.get(0);
+        if (line != null && maxPointNum < line.getLineData().size() - 3) {
+            int temp = maxPointNum;
+            maxPointNum = maxPointNum + 3;
+            if (this.getItemWidth() < 2) {
+                maxPointNum = temp;
+                return false;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * 监听滑动
+     */
+    @Override
+    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
+        // TODO Auto-generated method stub
+
+//		if (moveChart(distanceX)) {
+//			this.setTouchDistanceX(distanceX);
+//			notifyEventAll(ITouchEventNotify.EVENT_MOVE, this);
+//			this.setTouchDistanceX(0);
+//		}
+
+        return true;
+    }
+
+    @Override
+    public boolean onScale(ScaleGestureDetector detector) {
+        // TODO Auto-generated method stub
+        try {
+            if (!"".equals(tasLastPrice)) {// 暂时这么处理,分时图不让放大
+                return false;
+            }
+            if (zoomChart(detector)) {
+                this.invalidate();
+                notifyEventAll(ITouchEventNotify.EVENT_SCALE, this);
+            }
+            return false;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    public boolean zoomChart(ScaleGestureDetector detector) {
+        boolean isZoom = false;
+        float ps = detector.getPreviousSpan();
+        float cs = detector.getCurrentSpan();
+        if (ps > cs + 5) {
+            isZoom = zoomOut();
+        } else if (ps < cs - 5) {
+            isZoom = zoomIn();
+        }
+        return isZoom;
+    }
+
+    @Override
+    public void notifyEvent(int eventId, GridChart chart) {
+
+        try {
+            switch (eventId) {
+                case ITouchEventNotify.EVENT_MOVE: {
+                    moveChart(chart.getTouchDistanceX());
+                    changeMarginLeft(chart.getAxisMarginLeft());
+                    setNotifyAxisMarginLeft(chart.getAxisMarginLeft());
+                }
+                break;
+                case ITouchEventNotify.EVENT_SCALE:
+                    zoomChart(chart.getScaleGestureDetector());
+                    break;
+                default:
+                    break;
+            }
+        } catch (Exception e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+    }
+
+    private void changeMarginLeft(float marginLeft) {
+        if (super.isDisplayAxisYTitle()) {
+            int counts = super.getAxisYTitles().size();
+            Paint mPaintFont = new Paint();
+            mPaintFont.setTextSize(super.getLatitudeFontSize());
+            float textWidth = 0;
+            float maxAxisMarginLeft = 0;
+            for (int i = 0; i < counts; i++) {
+                textWidth = mPaintFont.measureText(super.getAxisYTitles().get(i)) + 10;
+                if (textWidth > maxAxisMarginLeft) {
+                    maxAxisMarginLeft = textWidth;
+                }
+            }
+
+            if (maxAxisMarginLeft < marginLeft && marginLeft != super.getAxisMarginLeft()) {
+                super.setAxisMarginLeft(marginLeft);
+                super.invalidate();
+            }
+        }
+    }
+
+    private boolean moveChart(float distance) {
+        float fMoveNum = distance / getItemWidth();
+        int nMoveNum = Math.round(fMoveNum);
+        int tempIdx = 0;
+        int size = this.getSize();
+        if (nMoveNum != 0) {
+            tempIdx = getStartIndex() + nMoveNum;
+            if (tempIdx <= size - this.getMaxPointNum()) {
+                this.setStartIndex(tempIdx);
+            }
+            if (this.getStartIndex() < 0)
+                setStartIndex(0);
+
+            super.invalidate();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected float getItemWidth() {
+        return (super.getWidth() - super.getAxisMarginLeft() - super.getAxisMarginRight() - 4f)
+                / (this.getMaxPointNum() - 1);
+    }
+
+    public void updateLineData() {
+        if (this.lineData == null) {
+            lineData = new ArrayList<LineEntity>();
+        }
+        if (isChangeLineData) {
+            isChangeLineData = false;
+            if (this.newLineData != null) {
+                this.lineData.clear();
+                this.lineData.addAll(newLineData);
+            }
+        }
+    }
+
+    /**
+     * @return the lineData
+     */
+    public List<LineEntity> getLineData() {
+        if (newLineData == null) {
+            newLineData = new ArrayList<LineEntity>();
+        }
+        return newLineData;
+    }
+
+    /**
+     * @param lineData the lineData to set
+     */
+    public void setLineData(List<LineEntity> lineData) {
+        if (this.newLineData == null) {
+            this.newLineData = new ArrayList<LineEntity>();
+        }
+        this.newLineData.clear();
+        this.newLineData.addAll(lineData);
+        isChangeLineData = true;
+    }
+
+    /**
+     * @return the maxPointNum
+     */
+    public int getMaxPointNum() {
+        return maxPointNum;
+    }
+
+    /**
+     * @param maxPointNum the maxPointNum to set
+     */
+    public void setMaxPointNum(int maxPointNum) {
+        this.maxPointNum = maxPointNum;
+    }
+
+    /**
+     * @return the minValue
+     */
+    public double getMinValue() {
+        return minValue;
+    }
+
+    /**
+     * @param minValue the minValue to set
+     */
+    public void setMinValue(double minValue) {
+        this.minValue = minValue;
+    }
+
+    /**
+     * @return the maxValue
+     */
+    public double getMaxValue() {
+        return maxValue;
+    }
+
+    /**
+     * @param maxValue the maxValue to set
+     */
+    public void setMaxValue(double maxValue) {
+        this.maxValue = maxValue;
+    }
+
+    public int getStartIndex() {
+        return startIndex;
+    }
+
+    public void setStartIndex(int startIndex) {
+        this.startIndex = startIndex;
+    }
+
+    private int getSize() {
+        int size = 0;
+        if (lineData != null && lineData.size() > 0) {
+            LineEntity line = lineData.get(0);
+            if (line != null && line.isDisplay()) {
+                List<Double> singleLineData = line.getLineData();
+                if (singleLineData != null) {
+                    size = singleLineData.size();
+                }
+            }
+        }
+        return size;
+    }
+
+    private void checkMember() {
+        int size = getSize();
+        if (this.startIndex < 0 && size > 0) {
+            int minIndex = size - this.getMaxPointNum();
+            if (minIndex > 0) {
+                setStartIndex(minIndex);
+            } else {
+                setStartIndex(0);
+                setMaxPointNum(size);
+            }
+        }
+    }
+
+
+    public String getLastPrice() {
+        return lastPrice;
+    }
+
+    public void setLastPrice(String lastPrice) {
+        this.lastPrice = lastPrice;
+        super.setTasLastPrice(lastPrice);
+    }
+}

+ 235 - 0
RMA/chart/src/main/java/com/desfate/chart/MACandleStickChart.java

@@ -0,0 +1,235 @@
+package com.desfate.chart;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PointF;
+import android.util.AttributeSet;
+
+import com.desfate.chart.entity.LineEntity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * <p>
+ * MACandleStickChart is inherits from CandleStickChart which can display moving
+ * average lines on this graph.
+ * </p>
+ * <p>
+ * MACandleStickChartはグラフの一種です、移動平均線など分析線がこのグラフで表示は可能です。
+ * </p>
+ * <p>
+ * MACandleStickChart继承于CandleStickChart的,可以在CandleStickChart基础上
+ * 显示移动平均等各种分析指标数据。
+ * </p>
+ *
+ * @see CandleStickChart
+ * @see StickChart
+ */
+public class MACandleStickChart extends CandleStickChart {
+
+    /**
+     * <p>
+     * data to draw lines
+     * </p>
+     * <p>
+     * ラインを書く用データ
+     * </p>
+     * <p>
+     * 绘制线条用的数据
+     * </p>
+     */
+    private List<LineEntity> lineData;
+    private List<LineEntity> newLineData;
+
+    private boolean isChangeLineData = false;
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param context
+     *
+     */
+    public MACandleStickChart(Context context) {
+        super(context);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param context
+     *
+     * @param attrs
+     *
+     * @param defStyle
+     *
+     * AttributeSet, int)
+     */
+    public MACandleStickChart(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param context
+     *
+     * @param attrs
+     *
+     *
+     *
+     * AttributeSet)
+     */
+    public MACandleStickChart(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onUpdate() {
+        updateLineData();
+        super.onUpdate();
+    }
+
+    /**
+     * (non-Javadoc)
+     * <p>
+     * <p>Called when is going to draw this chart<p> <p>チャートを書く前、メソッドを呼ぶ<p>
+     * <p>绘制图表时调用<p>
+     *
+     * @param canvas
+     * @see android.view.View#onDraw(Canvas)
+     */
+    @Override
+    protected void drawChart(Canvas canvas) {
+        super.drawChart(canvas);
+        // draw lines
+        if (null != this.lineData) {
+            if (0 != this.lineData.size()) {
+                drawLines(canvas);
+            }
+        }
+    }
+
+    @Override
+    protected void initMaxAndMin() {
+        super.initMaxAndMin();
+        // calculate max value and min value
+        if (null != this.lineData) {
+            for (int i = 0; i < lineData.size(); i++) {
+                LineEntity line = lineData.get(i);
+                if (line.isDisplay()) {
+                    List<Double> lineData = line.getLineData();
+
+                    if (lineData != null) {
+                        for (int j = this.getStartIndex(); j >= 0 && j < lineData.size() && j < this.getStartIndex() + this.getMaxSticksNum(); j++) {
+                            float value = lineData.get(j).floatValue();
+                            if (value > this.getMaxValue()) {
+                                this.setMaxValue(value);
+                            } else if (value < this.getMinValue()) {
+                                this.setMinValue(value);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public void setDeicmal(int decimal) {
+        setDecimalNum(decimal);
+    }
+
+    /**
+     * <p>
+     * draw lines
+     * </p>
+     * <p>
+     * ラインを書く
+     * </p>
+     * <p>
+     * 绘制线条
+     * </p>
+     *
+     * @param canvas
+     */
+    protected void drawLines(Canvas canvas) {
+        // distance between two points
+        float lineLength = getItemWidth();
+        // start point‘s X
+        float startX;
+
+        // draw MA lines
+        for (int i = 0; i < lineData.size(); i++) {
+            LineEntity line = lineData.get(i);
+            if (line.isDisplay()) {
+                Paint mPaint = new Paint();
+                mPaint.setColor(line.getLineColor());
+                mPaint.setAntiAlias(true);
+                mPaint.setStrokeWidth(2.0f);
+                List<Double> lineData = line.getLineData();
+                // set start point’s X
+                // 设置画质图表的起点
+//				startX = /*super.getAxisMarginLeft() + super.getAxisMarginRight()*/2f;
+                startX = lineLength / 2;// 线条起始位置
+                // start point
+                PointF ptFirst = null;
+                if (lineData != null) {
+
+                    for (int j = this.getStartIndex(); j >= 0 && j < lineData.size() && j < this.getStartIndex() + this.getMaxSticksNum(); j++) {
+                        float value = lineData.get(j).floatValue();
+                        // calculate Y
+                        float valueY = super.getAxisMarginTop() + (float) ((1f -
+                                (value - super.getMinValue()) * 10000000 / ((super.getMaxValue() - super.getMinValue()) * 10000) / 1000)
+                                * (super.getHeight() - super.getAxisMarginBottom() - 2 * super.getAxisMarginTop()));
+
+                        // if is not last point connect to previous point
+                        if (j > this.getStartIndex()) {
+                            canvas.drawLine(ptFirst.x, ptFirst.y, startX,
+                                    valueY, mPaint);
+                        }
+                        // reset
+                        ptFirst = new PointF(startX, valueY);
+                        startX = startX + lineLength;
+                    }
+                }
+            }
+        }
+    }
+
+    public void updateLineData() {
+        if (this.lineData == null) {
+            lineData = new ArrayList<LineEntity>();
+        }
+        if (isChangeLineData) {
+            isChangeLineData = false;
+            if (this.newLineData != null) {
+                this.lineData.clear();
+                this.lineData.addAll(newLineData);
+            }
+        }
+    }
+
+    /**
+     * @return the lineData
+     */
+    public List<LineEntity> getLineData() {
+        if (newLineData == null) {
+            newLineData = new ArrayList<LineEntity>();
+        }
+        return newLineData;
+    }
+
+    /**
+     * @param lineData the lineData to set
+     */
+    public void setLineData(List<LineEntity> lineData) {
+        if (this.newLineData == null) {
+            this.newLineData = new ArrayList<LineEntity>();
+        }
+        this.newLineData.clear();
+        this.newLineData.addAll(lineData);
+        isChangeLineData = true;
+    }
+}

+ 563 - 0
RMA/chart/src/main/java/com/desfate/chart/MinusStickChart.java

@@ -0,0 +1,563 @@
+package com.desfate.chart;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.PointF;
+import android.util.AttributeSet;
+
+import com.desfate.chart.data.ChartLineColors;
+import com.desfate.chart.entity.LineEntity;
+import com.desfate.chart.entity.StickEntity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * <p>
+ * MinusStickChart is inherits from StickChart which its data can be minus value
+ * </p>
+ * <p>
+ * MinusStickChartはグラフの一種です、マイナーデータをチャートで表示は可能です
+ * </p>
+ * <p>
+ * MinusStickChart继承于StickChart的,可以在StickChart基础上绘制包含负数的柱状条。
+ * </p>
+ */
+public class MinusStickChart extends StickChart {
+
+    /**
+     * <p>
+     * Default price up stick's border color
+     * </p>
+     * <p>
+     * 默认上方柱形的边框颜色
+     * </p>
+     */
+    public static final int DEFAULT_POSITIVE_STICK_BORDER_COLOR = Color.RED;
+    /**
+     * <p>
+     * Default price up stick's fill color
+     * </p>
+     * <p>
+     * 默认上方柱形的填充颜色
+     * </p>
+     */
+    public static final int DEFAULT_POSITIVE_STICK_FILL_COLOR = Color.RED;
+    /**
+     * <p>
+     * Default price down stick's border color
+     * </p>
+     * <p>
+     * 値下りローソクのボーダー色のデフォルト値
+     * </p>
+     * <p>
+     * 默认下方柱形的边框颜色
+     * </p>
+     */
+    public static final int DEFAULT_NEGATIVE_STICK_BORDER_COLOR = Color.GREEN;
+
+    /**
+     * 当价格持平时候柱子颜色
+     */
+    public static final int DEFAULT_CROSS_STAR_COLOR = Color.LTGRAY;
+    /**
+     * <p>
+     * Default price down stick's fill color
+     * </p>
+     * <p>
+     * 値下りローソクの色のデフォルト値
+     * </p>
+     * <p>
+     * 默认下方柱形的填充颜色
+     * </p>
+     */
+    public static final int DEFAULT_NEGATIVE_STICK_FILL_COLOR = Color.GREEN;
+    /**
+     * <p>
+     * Price up stick's border color
+     * </p>
+     * <p>
+     * 値上がりローソクのボーダー色
+     * </p>
+     * <p>
+     * 上方柱形的边框颜色
+     * </p>
+     */
+    private int positiveStickBorderColor = DEFAULT_POSITIVE_STICK_BORDER_COLOR;
+    /**
+     * <p>
+     * Price up stick's fill color
+     * </p>
+     * <p>
+     * 上方柱形的填充颜色
+     * </p>
+     */
+    private int positiveStickFillColor = DEFAULT_POSITIVE_STICK_FILL_COLOR;
+    /**
+     * <p>
+     * Price down stick's border color
+     * </p>
+     * <p>
+     * 下方柱形的边框颜色
+     * </p>
+     */
+
+    private int negativeStickBorderColor = /*DEFAULT_NEGATIVE_STICK_BORDER_COLOR*/0xFF009933;
+    /**
+     * <p>
+     * Price down stick's fill color
+     * </p>
+     * <p>
+     * 下方柱形的填充颜色
+     * </p>
+     */
+    private int negativeStickFillColor = /*DEFAULT_NEGATIVE_STICK_FILL_COLOR*/0xFF009933;
+
+    /**
+     * <p>
+     * Price no change stick's color (cross-star,T-like etc.)
+     * </p>
+     * <p>
+     * クローススターの色(価格変動無し)
+     * </p>
+     * <p>
+     * 十字线显示颜色(十字星,一字平线,T形线的情况)
+     * </p>
+     */
+    private int crossStarColor = DEFAULT_CROSS_STAR_COLOR;
+    /**
+     * <p>
+     * data to draw lines
+     * </p>
+     * <p>
+     * ラインを書く用データ
+     * </p>
+     * <p>
+     * 绘制线条用的数据
+     * </p>
+     */
+    private List<LineEntity> lineData = null;
+    private List<LineEntity> newLineData = null;
+
+    /**
+     * 是否显示stick图形
+     */
+    private boolean showStick = true;
+
+    private boolean isChangeLineData = false;
+
+    private int tickStartIndex = 0;
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param context
+     *
+     */
+    public MinusStickChart(Context context) {
+        super(context);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param context
+     *
+     * @param attrs
+     *
+     * @param defStyle
+     *
+     * AttributeSet, int)
+     */
+    public MinusStickChart(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param context
+     *
+     * @param attrs
+     *
+     * AttributeSet)
+     */
+    public MinusStickChart(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onUpdate() {
+        updateLineData();
+        super.onUpdate();
+    }
+
+    /**
+     * @param crossStarColor the crossStarColor to set
+     */
+    public void setCrossStarColor(int crossStarColor) {
+        this.crossStarColor = crossStarColor;
+    }
+
+    public int getCrossStarColor() {
+        return crossStarColor;
+    }
+
+    @Override
+    protected void drawChart(Canvas canvas) {
+        super.drawChart(canvas);
+        // draw lines
+        if (null != this.getLineData()) {
+            if (0 != this.getLineData().size()) {
+                drawLines(canvas);
+            }
+        }
+    }
+
+    @Override
+    protected void initMaxAndMin() {
+        super.initMaxAndMin();
+        // calculate max value and min value
+        if (null != this.getLineData()) {
+            for (int i = 0; i < getLineData().size(); i++) {
+                LineEntity line = getLineData().get(i);
+                if (line.isDisplay()) {
+                    List<Double> lineData = line.getLineData();
+
+                    if (lineData != null) {
+
+                        for (int j = this.getStartIndex(); j >= 0 && j < lineData.size() && j < this.getStartIndex() + this.getMaxSticksNum(); j++) {
+                            float value = lineData.get(j).floatValue();
+                            if (value > maxValue) {
+                                maxValue = value;
+                            } else if (value < minValue) {
+                                minValue = value;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * 实际绘制的柱宽
+     * </p>
+     */
+    @Override
+    protected float getItemShowWidth() {
+        return super.getItemWidth() - 6f;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param canvas
+     *
+     *
+     *
+     */
+    @Override
+    protected void drawSticks(Canvas canvas) {
+        // stick width
+        float stickWidth = getItemWidth();
+        float stickShowWidth = getItemShowWidth();
+        float stickShowOffset = (stickWidth - stickShowWidth) / 2;
+        // start point's X
+        float stickX = /*super.getAxisMarginLeft() + super.getAxisMarginRight()*/2f;
+
+        Paint mPaintPositiveFill = new Paint();
+        mPaintPositiveFill.setStyle(Style.FILL);
+        mPaintPositiveFill.setStrokeWidth(2.0f);
+        mPaintPositiveFill.setColor(positiveStickFillColor);
+
+        Paint mPaintPositiveBorder = new Paint();
+        mPaintPositiveBorder.setStyle(Style.STROKE);
+        mPaintPositiveBorder.setStrokeWidth(2.0f);
+        mPaintPositiveBorder.setColor(positiveStickBorderColor);
+
+        Paint mPaintNegativeFill = new Paint();
+        mPaintNegativeFill.setStyle(Style.FILL);
+        mPaintNegativeFill.setColor(negativeStickFillColor);
+
+        Paint mPaintNegativeBorder = new Paint();
+        mPaintNegativeBorder.setStyle(Style.STROKE);
+        mPaintNegativeBorder.setStrokeWidth(2.0f);
+        mPaintNegativeBorder.setColor(negativeStickBorderColor);
+
+        Paint sameFill = new Paint();
+        sameFill.setStyle(Style.FILL);
+        sameFill.setColor(crossStarColor);
+
+        Paint sameBorder = new Paint();
+        sameBorder.setStyle(Style.STROKE);
+        sameBorder.setStrokeWidth(2.0f);
+        sameBorder.setColor(crossStarColor);
+
+        List<StickEntity> data = super.getStickData();
+
+        if (null != data) {
+
+            int minIndex = data.size() - this.getMaxSticksNum();
+            if (this.getStartIndex() > minIndex && minIndex > 0) {
+                setStartIndex(minIndex);
+            }
+            if (!isShowStick()) {
+                return;
+            }
+            Paint mPaint = new Paint();
+            mPaint.setColor(ChartLineColors.BORDERCOLOR);
+            float startY = (float) ((1f - (0 - super.minValue) * 10000000
+                    / ((maxValue - minValue) * 10000) / 1000)
+                    * (super.getHeight() - super.getAxisMarginBottom() - 2 * super.getAxisMarginTop()) + super.getAxisMarginTop());
+//			float maxY = (float) ((1f - (maxValue - super.minValue)*10000000
+//					/ ((maxValue - minValue)*10000)/1000)
+//					* (super.getHeight() - super.getAxisMarginBottom() - 2*super.getAxisMarginTop()) + super.getAxisMarginTop());
+//			float minY = (float) ((1f - (minValue - super.minValue)*10000000
+//					/ ((maxValue - minValue)*10000)/1000)
+//					* (super.getHeight() - super.getAxisMarginBottom() - 2*super.getAxisMarginTop()) + super.getAxisMarginTop());
+            canvas.drawLine(2f, startY, super.getWidth() - super.getAxisMarginLeft(), startY, mPaint);
+//			canvas.drawLine(getClickPostX(), maxY, getClickPostX(), minY, mPaint);
+            // display as stick or line
+            for (int i = this.getStartIndex(); i >= 0 && i < data.size() && i < this.getStartIndex() + this.getMaxSticksNum(); i++) {
+
+                StickEntity e = data.get(i);
+
+                float highY = (float) ((1f - (e.getHigh() - super.minValue) * 10000000
+                        / ((maxValue - minValue) * 10000) / 1000)
+                        * (super.getHeight() - super.getAxisMarginBottom() - 2 * super.getAxisMarginTop()) + super.getAxisMarginTop());
+                float lowY = (float) ((1f - (e.getLow() - minValue) * 10000000
+                        / ((maxValue - minValue) * 10000) / 1000)
+                        * (super.getHeight() - super.getAxisMarginBottom() - 2 * super.getAxisMarginTop()) + super.getAxisMarginTop());
+
+                // draw stick
+                float left = stickX + stickShowOffset;
+                if (lineData.size() > 1) {
+                    if (e.getColorFlag() < -1) {
+                        if (e.getLow() >= 0) {
+                            canvas.drawRect(left, highY, left + stickShowWidth, lowY, mPaintPositiveFill);
+                            canvas.drawRect(left, highY, left + stickShowWidth, lowY, mPaintPositiveBorder);
+                        } else {
+                            canvas.drawRect(left, highY, left + stickShowWidth, lowY, mPaintNegativeFill);
+                            canvas.drawRect(left, highY, left + stickShowWidth, lowY, mPaintNegativeBorder);
+                        }
+                    }
+                    if (e.getColorFlag() > 0) {
+                        canvas.drawRect(left, highY, left + stickShowWidth, lowY,
+                                mPaintPositiveFill);
+                        canvas.drawRect(left, highY, left + stickShowWidth, lowY,
+                                mPaintPositiveBorder);
+                    } else if (e.getColorFlag() == 0) {
+                        canvas.drawRect(left, highY, left + stickShowWidth, lowY,
+                                sameFill);
+                        canvas.drawRect(left, highY, left + stickShowWidth, lowY,
+                                sameBorder);
+                    } else if (e.getColorFlag() == -1) {
+                        canvas.drawRect(left, highY, left + stickShowWidth, lowY,
+                                mPaintNegativeFill);
+                        canvas.drawRect(left, highY, left + stickShowWidth, lowY,
+                                mPaintNegativeBorder);
+                    }
+                }
+                // next x
+                stickX = stickX + stickWidth;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * draw lines
+     * </p>
+     * <p>
+     * ラインを書く
+     * </p>
+     * <p>
+     * 绘制线条
+     * </p>
+     *
+     * @param canvas
+     */
+    protected void drawLines(Canvas canvas) {
+        // distance between two points
+        float lineLength = getItemWidth();
+        // start point‘s X
+        float startX;
+
+        // draw MA lines
+        for (int i = 0; i < getLineData().size(); i++) {
+            LineEntity line = getLineData().get(i);
+            if (line.isDisplay()) {
+                Paint mPaint = new Paint();
+                mPaint.setColor(line.getLineColor());
+                mPaint.setAntiAlias(true);
+                mPaint.setStrokeWidth(2.0f);
+                List<Double> lineData = line.getLineData();
+                // set start point’s X
+//				startX = /*super.getAxisMarginLeft() + super.getAxisMarginRight()*/2f;
+                startX = lineLength / 2f;
+                // start point
+                PointF ptFirst = null;
+                if (lineData != null) {
+
+                    for (int j = this.getStartIndex(); j >= 0 && j < lineData.size() && j < this.getStartIndex() + this.getMaxSticksNum(); j++) {
+
+                        float value = lineData.get(j).floatValue();
+                        // calculate Y
+                        float valueY = super.getAxisMarginTop() + (float) ((1f -
+                                (value - super.getMinValue()) * 10000000 / ((super.getMaxValue() - super.getMinValue()) * 10000) / 1000) *
+                                (super.getHeight() - super.getAxisMarginBottom() - 2 * super.getAxisMarginTop()));
+
+//						if (lineEntityData.getLineData().size() == 1) {//FIXME : old(现在只支持rsi14,前面14个周期不画线)
+//							if (j > 13) {
+//								if (j > this.getStartIndex()) {
+//									canvas.drawLine(ptFirst.x, ptFirst.y, startX,
+//											valueY, mPaint);
+//								}
+//							}
+//						} else {
+//							if (j > this.getStartIndex()) {
+//								canvas.drawLine(ptFirst.x, ptFirst.y, startX,
+//										valueY, mPaint);
+//							}
+//						}
+
+                        if (j >= line.getStartIndex()) {//FIXME : 设置了画线位置
+                            if (j > this.getStartIndex()) {
+                                canvas.drawLine(ptFirst.x, ptFirst.y, startX,
+                                        valueY, mPaint);
+                            }
+                        }
+
+                        // reset
+                        ptFirst = new PointF(startX, valueY);
+                        startX = startX + lineLength;
+                    }
+                }
+            }
+        }
+    }
+
+    public void updateLineData() {
+        if (isChangeLineData) {
+            isChangeLineData = false;
+            if (this.newLineData != null) {
+
+                if (this.lineData == null) {
+                    lineData = new ArrayList<LineEntity>();
+                }
+                this.lineData.clear();
+                this.lineData.addAll(newLineData);
+            }
+        }
+    }
+
+    /**
+     * @return the lineData
+     */
+    public List<LineEntity> getLineData() {
+
+        if (lineData == null) {
+            lineData = new ArrayList<LineEntity>();
+        }
+        return lineData;
+    }
+
+    public List<LineEntity> getNewLineData() {
+
+        if (newLineData == null) {
+            newLineData = new ArrayList<LineEntity>();
+        }
+        return newLineData;
+    }
+
+    /**
+     * @param lineData the lineData to set
+     */
+    public void setLineData(List<LineEntity> lineData) {
+        if (this.newLineData == null) {
+            this.newLineData = new ArrayList<LineEntity>();
+        }
+        this.newLineData.clear();
+        this.newLineData.addAll(lineData);
+        isChangeLineData = true;
+    }
+
+    /**
+     * @return the positiveStickBorderColor
+     */
+    public int getPositiveStickBorderColor() {
+        return positiveStickBorderColor;
+    }
+
+    /**
+     * @param positiveStickBorderColor the positiveStickBorderColor to set
+     */
+    public void setPositiveStickBorderColor(int positiveStickBorderColor) {
+        this.positiveStickBorderColor = positiveStickBorderColor;
+    }
+
+    /**
+     * @return the positiveStickFillColor
+     */
+    public int getPositiveStickFillColor() {
+        return positiveStickFillColor;
+    }
+
+    /**
+     * @param positiveStickFillColor the positiveStickFillColor to set
+     */
+    public void setPositiveStickFillColor(int positiveStickFillColor) {
+        this.positiveStickFillColor = positiveStickFillColor;
+    }
+
+    /**
+     * @return the negativeStickBorderColor
+     */
+    public int getNegativeStickBorderColor() {
+        return negativeStickBorderColor;
+    }
+
+    /**
+     * @param negativeStickBorderColor the negativeStickBorderColor to set
+     */
+    public void setNegativeStickBorderColor(int negativeStickBorderColor) {
+        this.negativeStickBorderColor = negativeStickBorderColor;
+    }
+
+    /**
+     * @return the negativeStickFillColor
+     */
+    public int getNegativeStickFillColor() {
+        return negativeStickFillColor;
+    }
+
+    /**
+     * @param negativeStickFillColor the negativeStickFillColor to set
+     */
+    public void setNegativeStickFillColor(int negativeStickFillColor) {
+        this.negativeStickFillColor = negativeStickFillColor;
+    }
+
+    public int getTickStartIndex() {
+        return tickStartIndex;
+    }
+
+    public void setTickStartIndex(int tickStartIndex) {
+        this.tickStartIndex = tickStartIndex;
+    }
+
+    public boolean isShowStick() {
+        return showStick;
+    }
+
+    public void setShowStick(boolean showStick) {
+        this.showStick = showStick;
+    }
+
+}

+ 838 - 0
RMA/chart/src/main/java/com/desfate/chart/StickChart.java

@@ -0,0 +1,838 @@
+package com.desfate.chart;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+
+import com.desfate.chart.entity.StickEntity;
+import com.desfate.chart.event.ITouchEventNotify;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <p>
+ * StickChart is a kind of graph that draw the sticks on a GridChart if you want
+ * display some moving average lines on this graph, please use see MAStickChart
+ * for more information.
+ * </p>
+ * <p>
+ * StickChartはGridChartの表面でスティックを書いたラインチャードです。移動平均線など
+ * 分析線がお使いしたい場合、MAStickChartにご参照ください。
+ * </p>
+ * <p>
+ * StickChart是在GridChart上绘制柱状数据的图表、不支持显示均线。
+ * </p>
+ */
+public class StickChart extends GridChart {
+
+    private static final String TAG = "StickChart";
+
+    /**
+     * <p>
+     * default color for display stick border
+     * </p>
+     * <p>
+     * 表示スティックのボーダーの色のデフォルト値
+     * </p>
+     * <p>
+     * 默认表示柱条的边框颜色
+     * </p>
+     */
+    public static final int DEFAULT_STICK_BORDER_COLOR = Color.RED;
+
+    /**
+     * <p>
+     * default color for display stick
+     * </p>
+     * <p>
+     * 表示スティックの色のデフォルト値
+     * </p>
+     * <p>
+     * 默认表示柱条的填充颜色
+     * </p>
+     */
+    public static final int DEFAULT_STICK_FILL_COLOR = Color.RED;
+
+    /**
+     * <p>
+     * Color for display stick border
+     * </p>
+     * <p>
+     * 表示スティックのボーダーの色
+     * </p>
+     * <p>
+     * 表示柱条的边框颜色
+     * </p>
+     */
+    private int stickBorderColor = DEFAULT_STICK_BORDER_COLOR;
+
+    /**
+     * <p>
+     * Color for display stick
+     * </p>
+     * <p>
+     * 表示スティックの色
+     * </p>
+     * <p>
+     * 表示柱条的填充颜色
+     * </p>
+     */
+    private int stickFillColor = DEFAULT_STICK_FILL_COLOR;
+
+    /**
+     * <p>
+     * data to draw sticks
+     * </p>
+     * <p>
+     * スティックを書く用データ
+     * </p>
+     * <p>
+     * 绘制柱条用的数据
+     * </p>
+     */
+    private List<StickEntity> StickData;
+    private List<StickEntity> newStickData;
+
+    /**
+     * <p>
+     * max number of sticks
+     * </p>
+     * <p>
+     * スティックの最大表示数
+     * </p>
+     * <p>
+     * 柱条的最大表示数
+     * </p>
+     */
+    private int maxSticksNum;
+
+    /**
+     * <p>
+     * max value of Y axis
+     * </p>
+     * <p>
+     * Y軸の最大値
+     * </p>
+     * <p>
+     * Y的最大表示值
+     * </p>
+     */
+    protected double maxValue;
+
+    /**
+     * <p>
+     * min value of Y axis
+     * </p>
+     * <p>
+     * Y軸の最小値
+     * </p>
+     * <p>
+     * Y的最小表示值
+     * </p>
+     */
+    protected double minValue;
+
+    private boolean isChangeStickData = false;
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param context
+     *
+     */
+    public StickChart(Context context) {
+        super(context);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param context
+     *
+     * @param attrs
+     *
+     * @param defStyle
+     *
+     * AttributeSet, int)
+     */
+    public StickChart(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param context
+     *
+     * @param attrs
+     *
+     * AttributeSet)
+     */
+    public StickChart(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    //逻辑更新
+    protected void onUpdate() {
+        updateStickData();
+        checkMember();
+        initMaxAndMin();
+        initAxisY();
+        initAxisX();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * <p>Called when is going to draw this chart<p> <p>チャートを書く前、メソッドを呼ぶ<p>
+     * <p>绘制图表时调用<p>
+     *
+     * @param canvas
+     *
+     * @see android.view.View#onDraw(android.graphics.Canvas)
+     */
+    @Override
+    protected void onDraw(Canvas canvas) {
+        onUpdate();
+        super.onDraw(canvas);
+    }
+
+    @Override
+    protected void drawChart(Canvas canvas) {
+        super.drawChart(canvas);
+        drawSticks(canvas);
+    }
+
+    protected void initMaxAndMin() {
+        if (null != getStickData()) {
+
+            int minIndex = getStickData().size() - maxSticksNum;
+            if (this.getStartIndex() > minIndex && minIndex > 0) {
+                this.setStartIndex(minIndex);
+            }
+
+            for (int i = this.getStartIndex(); i >= 0 && i < getStickData().size() && i < this.getStartIndex() + maxSticksNum; i++) {
+                StickEntity ohlc = getStickData().get(i);
+
+                if (i == this.getStartIndex()) {
+                    maxValue = ohlc.getHigh();
+                    minValue = ohlc.getLow();
+                } else {
+                    if (ohlc.getHigh() > maxValue) {
+                        maxValue = ohlc.getHigh();
+                    }
+                    if (ohlc.getLow() < minValue) {
+                        minValue = ohlc.getLow();
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public int getAxisXPos(Object value) {
+        float graduate = Float.valueOf(super.getAxisXGraduate(value));
+        int pos = (int) Math.floor(graduate * maxSticksNum);
+
+        if (pos >= maxSticksNum) {
+            pos = maxSticksNum - 1;
+        } else if (pos < 0) {
+            pos = 0;
+        }
+        pos += this.getStartIndex();
+
+        return pos;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param value
+     *
+     */
+    @Override
+    public String getAxisXValue(Object value) {
+        int index = getAxisXPos(value);
+
+        return String.valueOf(getStickData().get(index).getDate());
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @param value
+     *
+     */
+    @Override
+    public String getAxisYGraduate(Object value) {
+        float graduate = Float.valueOf(super.getAxisYGraduate(value));
+        return String.valueOf((int) Math.floor(graduate * (maxValue - minValue)
+                + minValue));
+    }
+
+	/*@Override
+    public void notifyEvent(GridChart chart) {
+		CandleStickChart candlechart = (CandleStickChart) chart;
+
+		this.maxSticksNum = candlechart.getMaxSticksNum();
+
+		super.setDisplayCrossYOnTouch(false);
+		// notifyEvent
+		super.notifyEvent(chart);
+		// notifyEventAll
+		super.notifyEventAll(this);
+	}*/
+
+    /**
+     * <p>
+     * initialize degrees on Y axis
+     * </p>
+     * <p>
+     * X軸の目盛を初期化
+     * </p>
+     * <p>
+     * 初始化X轴的坐标值
+     * </p>
+     */
+    protected void initAxisX() {
+        List<String> TitleX = new ArrayList<String>();
+        if (null != getStickData() && getStickData().size() > 0) {
+            float average = maxSticksNum / this.getLongitudeNum();
+            for (int i = 0; i < this.getLongitudeNum(); i++) {
+                int index = (int) Math.floor(i * average);
+                if (index > maxSticksNum - 1) {
+                    index = maxSticksNum - 1;
+                }
+                if (this.getStartIndex() + index < getStickData().size() && this.getStartIndex() >= 0) {
+                    TitleX.add(String.valueOf(getStickData().get(this.getStartIndex() + index).getDate()));
+                }
+            }
+            if (this.getStartIndex() + maxSticksNum - 1 < getStickData().size() && this.getStartIndex() >= 0) {
+                TitleX.add(String.valueOf(getStickData().get(this.getStartIndex() + maxSticksNum - 1).getDate()));
+            }
+        }
+        super.setAxisXTitles(TitleX);
+    }
+
+    /**
+     * <p>
+     * get current selected data index
+     * </p>
+     * <p>
+     * 選択したスティックのインデックス
+     * </p>
+     * <p>
+     * 获取当前选中的柱条的index
+     * </p>
+     *
+     * @return int
+     * <p>
+     * index
+     * </p>
+     * <p>
+     * インデックス
+     * </p>
+     * <p>
+     * index
+     * </p>
+     */
+    public int getSelectedIndex() {
+        if (null == super.getTouchPoint()) {
+            return 0;
+        }
+        float graduate = Float.valueOf(super.getAxisXGraduate(super
+                .getTouchPoint().x));
+        int index = (int) Math.floor(graduate * maxSticksNum);
+
+        if (index >= maxSticksNum) {
+            index = maxSticksNum - 1;
+        } else if (index < 0) {
+            index = 0;
+        }
+
+        return index;
+    }
+
+    /**
+     * <p>
+     * initialize degrees on Y axis
+     * </p>
+     * <p>
+     * Y軸の目盛を初期化
+     * </p>
+     * <p>
+     * 初始化Y轴的坐标值
+     * </p>
+     */
+    protected void initAxisY() {
+        List<String> TitleY = new ArrayList<String>();
+        double average = (maxValue - minValue) * 10000 / this.getLatitudeNum() / 10000;
+        // calculate degrees on Y axis
+        for (int i = 0; i < this.getLatitudeNum(); i++) {
+            String value = getNumberFormat().format(minValue + i * average);
+            if (value.length() < super.getAxisYMaxTitleLength()) {
+                while (value.length() < super.getAxisYMaxTitleLength()) {
+                    value = new String(" ") + value;
+                }
+            }
+            TitleY.add(value);
+        }
+        // calculate last degrees by use max value
+        String value = getNumberFormat().format(maxValue);
+        if (value.length() < super.getAxisYMaxTitleLength()) {
+            while (value.length() < super.getAxisYMaxTitleLength()) {
+                value = new String(" ") + value;
+            }
+        }
+        TitleY.add(value);
+
+        super.setAxisYTitles(TitleY);
+        if (super.isDisplayAxisYTitle()) {
+            int counts = super.getAxisYTitles().size();
+            Paint mPaintFont = new Paint();
+            mPaintFont.setTextSize(super.getLatitudeFontSize());
+            float textWidth = 0;
+            float maxAxisMarginLeft = 0;
+            for (int i = 0; i < counts; i++) {
+                textWidth = mPaintFont.measureText(super.getAxisYTitles().get(i)) + 10;
+                if (textWidth > maxAxisMarginLeft) {
+                    maxAxisMarginLeft = textWidth;
+                }
+            }
+
+            if (maxAxisMarginLeft > super.getNotifyAxisMarginLeft()) {
+                super.setAxisMarginLeft(maxAxisMarginLeft);
+                notifyEventAll(ITouchEventNotify.EVENT_MOVE, this);
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * draw sticks
+     * </p>
+     * <p>
+     * スティックを書く
+     * </p>
+     * <p>
+     * 绘制柱条
+     * </p>
+     *
+     * @param canvas
+     */
+    protected void drawSticks(Canvas canvas) {
+        float stickWidth = getItemWidth();
+        float stickShowWidth = getItemShowWidth();
+        float stickShowOffset = (stickWidth - stickShowWidth) / 2;
+        float stickX = super.getAxisMarginLeft() + super.getAxisMarginRight();//绘图区域左右对称缩进
+
+        Paint mPaintStick = new Paint();
+        mPaintStick.setColor(stickFillColor);
+
+        if (null != getStickData()) {
+
+            int minIndex = getStickData().size() - maxSticksNum;
+            if (this.getStartIndex() > minIndex && minIndex > 0) {
+                setStartIndex(minIndex);
+            }
+
+            for (int i = this.getStartIndex(); i >= 0 && i < getStickData().size() && i < this.getStartIndex() + maxSticksNum; i++) {
+                StickEntity ohlc = getStickData().get(i);
+
+                float highY = (float) ((1f - (ohlc.getHigh() - minValue)
+                        / (maxValue - minValue))
+                        * (super.getHeight() - super.getAxisMarginBottom()) - super
+                        .getAxisMarginTop());
+                float lowY = (float) ((1f - (ohlc.getLow() - minValue)
+                        / (maxValue - minValue))
+                        * (super.getHeight() - super.getAxisMarginBottom()) - super
+                        .getAxisMarginTop());
+
+                // stick or line?
+                float left = stickX + stickShowOffset;
+                if (stickWidth >= 2f) {
+                    canvas.drawRect(left, highY, left + stickShowWidth, lowY,
+                            mPaintStick);
+                } else {
+                    canvas.drawLine(left, highY, left, lowY, mPaintStick);
+                }
+
+                // next x
+                stickX = stickX + stickWidth;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Zoom in the graph
+     * </p>
+     * <p>
+     * 拡大表示する。
+     * </p>
+     * <p>
+     * 放大表示
+     * </p>
+     */
+    protected boolean zoomIn() {
+        if (maxSticksNum > 10) {
+            maxSticksNum = maxSticksNum - 3;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * <p>
+     * Zoom out the grid
+     * </p>
+     * <p>
+     * 縮小表示する。
+     * </p>
+     * <p>
+     * 缩小
+     * </p>
+     */
+    protected boolean zoomOut() {
+        if (maxSticksNum < getStickData().size() - 3) {
+            int temp = maxSticksNum;
+            maxSticksNum = maxSticksNum + 3;
+            if (this.getItemWidth() < 1) {
+                maxSticksNum = temp;
+                return false;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * 监听滑动
+     */
+    @Override
+    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
+                            float distanceY) {
+        // TODO Auto-generated method stub
+        if (moveChart(distanceX)) {
+            this.setTouchDistanceX(distanceX);
+            notifyEventAll(ITouchEventNotify.EVENT_MOVE, this);
+            this.setTouchDistanceX(0);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean onScale(ScaleGestureDetector detector) {
+        // TODO Auto-generated method stub
+        if (zoomChart(detector)) {
+            this.invalidate();
+            notifyEventAll(ITouchEventNotify.EVENT_SCALE, this);
+        }
+        return false;
+    }
+
+    public boolean zoomChart(ScaleGestureDetector detector) {
+        boolean isZoom = false;
+        float ps = detector.getPreviousSpan();
+        float cs = detector.getCurrentSpan();
+        if (ps > cs + 5) {
+            isZoom = zoomOut();
+        } else if (ps < cs - 5) {
+            isZoom = zoomIn();
+        }
+        return isZoom;
+    }
+
+    @Override
+    public void notifyEvent(int eventId, GridChart chart) {
+
+        switch (eventId) {
+            case ITouchEventNotify.EVENT_MOVE: {
+                moveChart(chart.getTouchDistanceX());
+                changeMarginLeft(chart.getAxisMarginLeft());
+                setNotifyAxisMarginLeft(chart.getAxisMarginLeft());
+            }
+            break;
+            case ITouchEventNotify.EVENT_SCALE:
+                zoomChart(chart.getScaleGestureDetector());
+                super.invalidate();
+                break;
+            case ITouchEventNotify.EVENT_CROSSONTOUCH:
+                this.setClickPostX(chart.getClickPostX());
+                this.setClickPostY(chart.getClickPostY());
+                super.invalidate();
+                break;
+            case ITouchEventNotify.EVENT_UP:
+                this.setClickPostX(0);
+                this.setClickPostY(0);
+                super.invalidate();
+                break;
+            default:
+                break;
+        }
+    }
+
+    private void changeMarginLeft(float marginLeft) {
+        if (super.isDisplayAxisYTitle() && super.getAxisYTitles() != null) {
+            int counts = super.getAxisYTitles().size();
+            Paint mPaintFont = new Paint();
+            mPaintFont.setTextSize(super.getLatitudeFontSize());
+            float textWidth = 0;
+            float maxAxisMarginLeft = 0;
+            for (int i = 0; i < counts; i++) {
+                textWidth = mPaintFont.measureText(super.getAxisYTitles().get(i)) + 10;
+                if (textWidth > maxAxisMarginLeft) {
+                    maxAxisMarginLeft = textWidth;
+                }
+            }
+
+            if (maxAxisMarginLeft < marginLeft && marginLeft != super.getAxisMarginLeft()) {
+                super.setAxisMarginLeft(marginLeft);
+                super.invalidate();
+            }
+        }
+    }
+
+    private boolean moveChart(float distance) {
+        float fMoveNum = distance / getItemWidth();
+        int nMoveNum = Math.round(fMoveNum);
+        int tempIdx = 0;
+        if (nMoveNum != 0) {
+            tempIdx = getStartIndex() + nMoveNum;
+            if (tempIdx <= getStickData().size() - this.getMaxSticksNum()) {
+                setStartIndex(tempIdx);
+                if (this.getStartIndex() < 0)
+                    setStartIndex(0);
+
+                super.invalidate();
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void checkMember() {
+        if (this.getStartIndex() < 0) {
+            int minIndex = getStickData().size() - maxSticksNum;
+            if (minIndex > 0) {
+                setStartIndex(minIndex);
+            } else if (getStickData().size() > 0) {
+                setStartIndex(0);
+                //setMaxSticksNum(getStickData().size());
+            }
+        }
+    }
+
+    @Override
+    protected float getClickPosXOffset() {
+        return getItemWidth() / 2;
+    }
+
+    @Override
+    protected float getItemWidth() {
+        return (super.getWidth() - super.getAxisMarginLeft() - 2 * super.getAxisMarginRight()) / maxSticksNum;
+    }
+
+    /**
+     * <p>
+     * 实际绘制的柱宽
+     * </p>
+     */
+    @Override
+    protected float getItemShowWidth() {
+        return getItemWidth() - 4f;
+    }
+
+    /**
+     * <p>
+     * add a new stick data to sticks and refresh this chart
+     * </p>
+     * <p>
+     * 新しいスティックデータを追加する,フラフをレフレシューする
+     * </p>
+     * <p>
+     * 追加一条新数据并刷新当前图表
+     * </p>
+     *
+     * @param entity <p>
+     *               data
+     *               </p>
+     *               <p>
+     *               データ
+     *               </p>
+     *               <p>
+     *               新数据
+     *               </p>
+     */
+    public void pushData(StickEntity entity) {
+        if (null != entity) {
+            addData(entity);
+            super.postInvalidate();
+        }
+    }
+
+    /**
+     * <p>
+     * add a new stick data to sticks
+     * </p>
+     * <p>
+     * 新しいスティックデータを追加する
+     * </p>
+     * <p>
+     * 追加一条新数据
+     * </p>
+     *
+     * @param entity <p>
+     *               data
+     *               </p>
+     *               <p>
+     *               データ
+     *               </p>
+     *               <p>
+     *               新数据
+     *               </p>
+     */
+    public void addData(StickEntity entity) {
+        if (null != entity) {
+            // data is null or empty
+            if (null == getStickData() || 0 == getStickData().size()) {
+                setStickData(new ArrayList<StickEntity>());
+                this.maxValue = ((int) entity.getHigh()) / 100 * 100;
+            }
+
+            // add
+            this.getStickData().add(entity);
+
+            if (this.maxValue < entity.getHigh()) {
+                this.maxValue = 100 + ((int) entity.getHigh()) / 100 * 100;
+            }
+
+			/*if (StickData.size() > maxSticksNum) {
+				maxSticksNum = maxSticksNum + 1;
+			}*/
+        }
+    }
+
+    /**
+     * @return the stickBorderColor
+     */
+    public int getStickBorderColor() {
+        return stickBorderColor;
+    }
+
+    /**
+     * @param stickBorderColor the stickBorderColor to set
+     */
+    public void setStickBorderColor(int stickBorderColor) {
+        this.stickBorderColor = stickBorderColor;
+    }
+
+    /**
+     * @return the stickFillColor
+     */
+    public int getStickFillColor() {
+        return stickFillColor;
+    }
+
+    /**
+     * @param stickFillColor the stickFillColor to set
+     */
+    public void setStickFillColor(int stickFillColor) {
+        this.stickFillColor = stickFillColor;
+    }
+
+    public void updateStickData() {
+        if (isChangeStickData) {
+            isChangeStickData = false;
+            if (newStickData != null) {
+                if (null == StickData) {
+                    StickData = new ArrayList<StickEntity>();
+                }
+                StickData.clear();
+                StickData.addAll(newStickData);
+				/*for (StickEntity e : newStickData) {
+					addData(e);
+				}*/
+            }
+        }
+    }
+
+    /**
+     * @return the stickData
+     */
+    public List<StickEntity> getStickData() {
+        if (StickData == null) {
+            StickData = new ArrayList<StickEntity>();
+        }
+        return StickData;
+    }
+
+    /**
+     * @return the stickData
+     */
+    public List<StickEntity> getNewStickData() {
+        if (newStickData == null) {
+            newStickData = new ArrayList<StickEntity>();
+        }
+        return newStickData;
+    }
+
+    /**
+     * @param stickData the stickData to set
+     */
+    public void setStickData(List<StickEntity> stickData) {
+        if (newStickData == null) {
+            newStickData = new ArrayList<StickEntity>();
+        }
+        newStickData.clear();
+        newStickData.addAll(stickData);
+        isChangeStickData = true;
+    }
+
+    /**
+     * @return the maxSticksNum
+     */
+    public int getMaxSticksNum() {
+        return maxSticksNum;
+    }
+
+    /**
+     * @param maxSticksNum the maxSticksNum to set
+     */
+    public void setMaxSticksNum(int maxSticksNum) {
+        this.maxSticksNum = maxSticksNum;
+    }
+
+    /**
+     * @return the maxValue
+     */
+    public double getMaxValue() {
+        return maxValue;
+    }
+
+    /**
+     * @param maxValue the maxValue to set
+     */
+    public void setMaxValue(double maxValue) {
+        this.maxValue = maxValue;
+    }
+
+    /**
+     * @return the minValue
+     */
+    public double getMinValue() {
+        return minValue;
+    }
+
+    /**
+     * @param minValue the minValue to set
+     */
+    public void setMinValue(double minValue) {
+        this.minValue = minValue;
+    }
+}

+ 37 - 0
RMA/chart/src/main/java/com/desfate/chart/data/BidChartTotalDatas.java

@@ -0,0 +1,37 @@
+package com.desfate.chart.data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 竞价总量
+ *
+ * @author Administrator
+ */
+public class BidChartTotalDatas {
+    private List<Double> total5 = null;
+    private List<Double> total10 = null;
+
+    public List<Double> getTotal10() {
+        if (total10 == null) {
+            total10 = new ArrayList<Double>();
+        }
+        return total10;
+    }
+
+    public void setTotal10(List<Double> total10) {
+        this.total10 = total10;
+    }
+
+    public List<Double> getTotal5() {
+        if (total5 == null) {
+            total5 = new ArrayList<Double>();
+        }
+        return total5;
+    }
+
+    public void setTotal5(List<Double> total5) {
+        this.total5 = total5;
+    }
+
+}

+ 44 - 0
RMA/chart/src/main/java/com/desfate/chart/data/ChartBIASDatas.java

@@ -0,0 +1,44 @@
+package com.desfate.chart.data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ChartBIASDatas {
+    private List<Double> bias6 = null;
+    private List<Double> bias12 = null;
+    private List<Double> bias24 = null;
+
+    public List<Double> getBias24() {
+        if (bias24 == null) {
+            bias24 = new ArrayList<Double>();
+        }
+        return bias24;
+    }
+
+    public void setBias24(List<Double> bias24) {
+        this.bias24 = bias24;
+    }
+
+    public List<Double> getBias12() {
+        if (bias12 == null) {
+            bias12 = new ArrayList<Double>();
+        }
+        return bias12;
+    }
+
+    public void setBias12(List<Double> bias12) {
+        this.bias12 = bias12;
+    }
+
+    public List<Double> getBias6() {
+        if (bias6 == null) {
+            bias6 = new ArrayList<Double>();
+        }
+        return bias6;
+    }
+
+    public void setBias6(List<Double> bias6) {
+        this.bias6 = bias6;
+    }
+
+}

+ 68 - 0
RMA/chart/src/main/java/com/desfate/chart/data/ChartBidFiveDatas.java

@@ -0,0 +1,68 @@
+package com.desfate.chart.data;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class ChartBidFiveDatas implements Parcelable {
+    private String buyNum = "";
+    private String buyPrice = "";
+
+    public ChartBidFiveDatas(String buyNum, String buyPrice) {
+        super();
+        this.buyNum = buyNum;
+        this.buyPrice = buyPrice;
+    }
+
+    public ChartBidFiveDatas() {
+        super();
+    }
+
+    protected ChartBidFiveDatas(Parcel in) {
+        buyNum = in.readString();
+        buyPrice = in.readString();
+    }
+
+    public static final Creator<ChartBidFiveDatas> CREATOR = new Creator<ChartBidFiveDatas>() {
+        @Override
+        public ChartBidFiveDatas createFromParcel(Parcel in) {
+            return new ChartBidFiveDatas(in);
+        }
+
+        @Override
+        public ChartBidFiveDatas[] newArray(int size) {
+            return new ChartBidFiveDatas[size];
+        }
+    };
+
+    public String getBuyNum() {
+        return buyNum;
+    }
+
+    public void setBuyNum(String buyNum) {
+        this.buyNum = buyNum;
+    }
+
+    public String getBuyPrice() {
+        return buyPrice;
+    }
+
+    public void setBuyPrice(String buyPrice) {
+        this.buyPrice = buyPrice;
+    }
+
+    @Override
+    public int describeContents() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        // TODO Auto-generated method stub
+
+        dest.writeString(buyNum);
+        dest.writeString(buyPrice);
+    }
+
+
+}

+ 31 - 0
RMA/chart/src/main/java/com/desfate/chart/data/ChartDMADatas.java

@@ -0,0 +1,31 @@
+package com.desfate.chart.data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ChartDMADatas {
+    private List<Double> DMA = null;
+    private List<Double> AMA = null;
+
+    public List<Double> getAMA() {
+        if (AMA == null) {
+            AMA = new ArrayList<Double>();
+        }
+        return AMA;
+    }
+
+    public void setAMA(List<Double> aMA) {
+        AMA = aMA;
+    }
+
+    public List<Double> getDMA() {
+        if (DMA == null) {
+            DMA = new ArrayList<Double>();
+        }
+        return DMA;
+    }
+
+    public void setDMA(List<Double> dMA) {
+        DMA = dMA;
+    }
+}

+ 188 - 0
RMA/chart/src/main/java/com/desfate/chart/data/ChartData.java

@@ -0,0 +1,188 @@
+package com.desfate.chart.data;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.desfate.chart.entity.OHLCEntity;
+
+
+public class ChartData extends OHLCEntity implements Parcelable {
+
+    /**
+     * 完美数据
+     */
+    public static final int Perfect = 0;
+
+    /**
+     * 自动画的
+     */
+    public static final int AutoDraw = 1;
+
+    /**
+     * 缺失补的
+     */
+    public static final int NoneDraw = 2;
+
+    /**
+     * 未设置
+     */
+    public static final int None = 3;
+
+    private String mAmount = "";//!< @brief 成交金额
+    private String mVolume = "";//!< @brief 总量
+
+    private double mEMA12;//!< @brief EMA(12)
+    private double mEMA26;//!< @brief EMA(26)
+    private double mDIF;//!< @brief DIF
+    private double mDEA;//!< @brief DEA
+    private double mMACD;//!< @brief MACD
+
+    /**
+     * 数据类型
+     * 0 正常数据,完美的数据
+     * 1 随时间自动加进去的数据,这种数据是最垃圾的,每次生成新的都要尝试去修补以前的,
+     * 而且不能计算入均价啥的里面
+     * 2 由于漏了数据,然后填补空缺的数据,这种数据不能计算入均价里面,但是不需要再次请求,
+     * 因为经过了服务端确认,没有数据
+     */
+    private int dataType = ChartData.None;
+    // 是否是自己创建的数据, 如果在更新这种数据的时候,要重新更新开高低收
+    private boolean isCreateBySelf = false;
+
+    public ChartData() {
+        super();
+    }
+
+    public ChartData(double open, double high, double low, double close, String time) {
+
+        super(open, high, low, close, time);
+    }
+
+    protected ChartData(Parcel in) {
+        mAmount = in.readString();
+        mVolume = in.readString();
+        mEMA12 = in.readDouble();
+        mEMA26 = in.readDouble();
+        mDIF = in.readDouble();
+        mDEA = in.readDouble();
+        mMACD = in.readDouble();
+        dataType = in.readInt();
+        isCreateBySelf = in.readByte() != 0;
+    }
+
+    public static final Creator<ChartData> CREATOR = new Creator<ChartData>() {
+        @Override
+        public ChartData createFromParcel(Parcel in) {
+            return new ChartData(in);
+        }
+
+        @Override
+        public ChartData[] newArray(int size) {
+            return new ChartData[size];
+        }
+    };
+
+    public void reset() {
+        setOpen(0);
+        setHigh(0);
+        setLow(0);
+        setClose(0);
+        setTime("");
+        mAmount = "";
+        mVolume = "";
+        mEMA12 = 0;
+        mEMA26 = 0;
+        mDIF = 0;
+        mDEA = 0;
+        mMACD = 0;
+    }
+
+    public String getAmount() {
+        return mAmount;
+    }
+
+    public void setAmount(String value) {
+        this.mAmount = value;
+    }
+
+    public String getVolume() {
+        if ("".equals(mVolume)) {
+            mVolume = "0";
+        }
+        return mVolume;
+    }
+
+    public void setVolume(String value) {
+        this.mVolume = value;
+    }
+
+    public int getDataType() {
+        return dataType;
+    }
+
+    public void setDataType(int dataType) {
+        this.dataType = dataType;
+    }
+
+    public double getEMA12() {
+        return mEMA12;
+    }
+
+    public void setEMA12(double value) {
+        this.mEMA12 = value;
+    }
+
+    public double getEMA26() {
+        return mEMA26;
+    }
+
+    public void setEMA26(double value) {
+        this.mEMA26 = value;
+    }
+
+    public double getDIF() {
+        return mDIF;
+    }
+
+    public void setDIF(double value) {
+        this.mDIF = value;
+    }
+
+    public double getDEA() {
+        return mDEA;
+    }
+
+    public void setDEA(double value) {
+        this.mDEA = value;
+    }
+
+    public double getMACD() {
+        return mMACD;
+    }
+
+    public void setMACD(double value) {
+        this.mMACD = value;
+    }
+
+    @Override
+    public int describeContents() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        // TODO Auto-generated method stub
+
+        dest.writeString(mAmount);
+        dest.writeString(mVolume);
+        dest.writeDouble(mEMA12);
+        dest.writeDouble(mEMA26);
+        dest.writeDouble(mDIF);
+        dest.writeDouble(mDEA);
+        dest.writeDouble(mMACD);
+        dest.writeInt(dataType);
+        dest.writeByte((byte) (isCreateBySelf ? 1 : 0));
+    }
+
+}

+ 44 - 0
RMA/chart/src/main/java/com/desfate/chart/data/ChartDataContainer.java

@@ -0,0 +1,44 @@
+package com.desfate.chart.data;
+
+
+import com.desfate.chart.entity.OHLCEntity;
+import com.desfate.chart.entity.StickEntity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ChartDataContainer {
+
+    public List<OHLCEntity> mOHLCList;
+    //public SortedMap<Date, OHLCEntity> sortedMap;
+
+    public List<StickEntity> mStickList;
+
+    public int mSize = 0;// !< @brief
+    // mSize是收到的图表json数据的实际大小,而mOhlc的size有时会比mSize大,有时会小
+
+    public ChartDataContainer() {
+        mOHLCList = new ArrayList<OHLCEntity>();
+        mStickList = new ArrayList<StickEntity>();
+//		sortedMap = new TreeMap<Date, OHLCEntity>(new Comparator<Date>() {
+//			@Override
+//			public int compare(Date arg0, Date arg1) {
+//				return arg0.compareTo(arg1);
+//			}
+//		});
+    }
+
+    public int getTrustNum() {
+        int a = 0;
+        for (int i = mOHLCList.size() - 1; i > 0; i--) {
+            if (mOHLCList.get(i) instanceof ChartData) {
+                if (((ChartData) mOHLCList.get(i)).getDataType() != 2) {
+                    return i;
+                }
+            } else {
+                return mSize;
+            }
+        }
+        return a;
+    }
+}

+ 56 - 0
RMA/chart/src/main/java/com/desfate/chart/data/ChartKDJDatas.java

@@ -0,0 +1,56 @@
+package com.desfate.chart.data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ChartKDJDatas {
+    private List<Double> rsv = null;
+
+    private List<Double> k = null;
+    private List<Double> d = null;
+    private List<Double> j = null;
+
+    public List<Double> getK() {
+        if (k == null) {
+            k = new ArrayList<Double>();
+        }
+        return k;
+    }
+
+    public void setK(List<Double> k) {
+        this.k = k;
+    }
+
+    public List<Double> getD() {
+        if (d == null) {
+            d = new ArrayList<Double>();
+        }
+        return d;
+    }
+
+    public void setD(List<Double> d) {
+        this.d = d;
+    }
+
+    public List<Double> getJ() {
+        if (j == null) {
+            j = new ArrayList<Double>();
+        }
+        return j;
+    }
+
+    public void setJ(List<Double> j) {
+        this.j = j;
+    }
+
+    public List<Double> getRsv() {
+        if (rsv == null) {
+            rsv = new ArrayList<Double>();
+        }
+        return rsv;
+    }
+
+    public void setRsv(List<Double> rsv) {
+        this.rsv = rsv;
+    }
+}

+ 48 - 0
RMA/chart/src/main/java/com/desfate/chart/data/ChartLineColors.java

@@ -0,0 +1,48 @@
+package com.desfate.chart.data;
+
+import android.graphics.Color;
+
+public class ChartLineColors {
+
+    /**
+     * rsi
+     */
+    public static int RSI_14_COLOR = Color.RED;
+
+    /**
+     * psy
+     */
+    public static int PSY_PSY_LINE_COLOR = Color.RED;
+    public static int PSY_PSYMA_LINE_COLOR = Color.BLACK;
+
+    /**
+     * dma
+     */
+    public static int DMA_DMA_LINE_COLOR = Color.RED;
+    public static int DMA_AMA_LINE_COLOR = Color.BLACK;
+
+    /**
+     * cci
+     */
+    public static int CCI_CCI_LINE_COLOR = Color.RED;
+
+    /**
+     * kdj
+     */
+    public static int KDJ_K_LINE_COLOR = Color.BLUE;
+    public static int KDJ_D_LINE_COLOR = Color.RED;
+    public static int KDJ_J_LINE_COLOR = Color.BLACK;
+
+    /**
+     * bias
+     */
+    public static int BIAS_BIAS6_LINE_COLOR = Color.BLUE;
+    public static int BIAS_BIAS12_LINE_COLOR = Color.RED;
+    public static int BIAS_BIAS24_LINE_COLOR = Color.BLACK;
+
+    /**
+     * 边框颜色
+     */
+    public static int BORDERCOLOR = Color.LTGRAY;
+
+}

+ 40 - 0
RMA/chart/src/main/java/com/desfate/chart/data/ChartMADatas.java

@@ -0,0 +1,40 @@
+package com.desfate.chart.data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ChartMADatas {
+    private int size = 0;
+    private List<Double> MA10;
+    private List<Double> MA50;
+
+    public List<Double> getMA10() {
+        if (MA10 == null) {
+            MA10 = new ArrayList<Double>();
+        }
+        return MA10;
+    }
+
+    public void setMA10(List<Double> mA10) {
+        MA10 = mA10;
+    }
+
+    public List<Double> getMA50() {
+        if (MA50 == null) {
+            MA50 = new ArrayList<Double>();
+        }
+        return MA50;
+    }
+
+    public void setMA50(List<Double> mA50) {
+        MA50 = mA50;
+    }
+
+    public int getSize() {
+        return size;
+    }
+
+    public void setSize(int size) {
+        this.size = size;
+    }
+}

+ 56 - 0
RMA/chart/src/main/java/com/desfate/chart/data/ChartMarketGoodsDatas.java

@@ -0,0 +1,56 @@
+package com.desfate.chart.data;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class ChartMarketGoodsDatas implements Parcelable {
+    private String goodsName;//商品名称
+    private String goodsCode;//商品代码
+    private String ExchangeCode;//交易所代码
+    private String Sort;//商品组代码
+
+    public String getGoodsName() {
+        return goodsName;
+    }
+
+    public void setGoodsName(String goodsName) {
+        this.goodsName = goodsName;
+    }
+
+    public String getGoodsCode() {
+        return goodsCode;
+    }
+
+    public void setGoodsCode(String goodsCode) {
+        this.goodsCode = goodsCode;
+    }
+
+    public String getExchangeCode() {
+        return ExchangeCode;
+    }
+
+    public void setExchangeCode(String exchangeCode) {
+        ExchangeCode = exchangeCode;
+    }
+
+    public String getSort() {
+        return Sort;
+    }
+
+    public void setSort(String sort) {
+        Sort = sort;
+    }
+
+    @Override
+    public int describeContents() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        // TODO Auto-generated method stub
+
+    }
+
+}

+ 38 - 0
RMA/chart/src/main/java/com/desfate/chart/data/ChartPSYDatas.java

@@ -0,0 +1,38 @@
+package com.desfate.chart.data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <li>psy
+ * <li>psyMa
+ *
+ * @author Administrator
+ */
+public class ChartPSYDatas {
+    private List<Double> psy = null;
+    private List<Double> psyMa = null;
+
+    public List<Double> getPsyMa() {
+        if (psyMa == null) {
+            psyMa = new ArrayList<Double>();
+        }
+        return psyMa;
+    }
+
+    public void setPsyMa(List<Double> psyMa) {
+        this.psyMa = psyMa;
+    }
+
+    public List<Double> getPsy() {
+        if (psy == null) {
+            psy = new ArrayList<Double>();
+        }
+        return psy;
+    }
+
+    public void setPsy(List<Double> psy) {
+        this.psy = psy;
+    }
+
+}

+ 38 - 0
RMA/chart/src/main/java/com/desfate/chart/data/ChartSelectItemData.java

@@ -0,0 +1,38 @@
+package com.desfate.chart.data;
+
+import android.annotation.SuppressLint;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+@SuppressLint("ParcelCreator")
+public class ChartSelectItemData implements Parcelable {
+    private String goodsName = "";
+    private String goodsCode = "";
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+
+    }
+
+    public String getGoodsName() {
+        return goodsName;
+    }
+
+    public void setGoodsName(String goodsName) {
+        this.goodsName = goodsName;
+    }
+
+    public String getGoodsCode() {
+        return goodsCode;
+    }
+
+    public void setGoodsCode(String goodsCode) {
+        this.goodsCode = goodsCode;
+    }
+
+}

+ 203 - 0
RMA/chart/src/main/java/com/desfate/chart/data/ChartSelectPlans.java

@@ -0,0 +1,203 @@
+package com.desfate.chart.data;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Date;
+
+public class ChartSelectPlans implements Parcelable {
+
+    private Date CloseTime;
+    private Date CloseTime2;
+    private Date CloseTime3;
+    private Date EntryTime;
+    private String ExchangeCode;
+    private String ExchangeDate;
+    private Date OpenTime;
+    private Date OpenTime2;
+    private Date OpenTime3;
+    private Date SettlementTime;
+    private int Sort;
+    private int TikKind;
+
+    public ChartSelectPlans() {
+        super();
+    }
+
+
+    public ChartSelectPlans(Date closeTime, Date closeTime2, Date closeTime3,
+                            Date entryTime, String exchangeCode, String exchangeDate,
+                            Date openTime, Date openTime2, Date openTime3, Date settlementTime,
+                            int sort, int tikKind) {
+        super();
+        CloseTime = closeTime;
+        CloseTime2 = closeTime2;
+        CloseTime3 = closeTime3;
+        EntryTime = entryTime;
+        ExchangeCode = exchangeCode;
+        ExchangeDate = exchangeDate;
+        OpenTime = openTime;
+        OpenTime2 = openTime2;
+        OpenTime3 = openTime3;
+        SettlementTime = settlementTime;
+        Sort = sort;
+        TikKind = tikKind;
+    }
+
+
+    protected ChartSelectPlans(Parcel in) {
+        ExchangeCode = in.readString();
+        ExchangeDate = in.readString();
+        Sort = in.readInt();
+        TikKind = in.readInt();
+    }
+
+    public static final Creator<ChartSelectPlans> CREATOR = new Creator<ChartSelectPlans>() {
+        @Override
+        public ChartSelectPlans createFromParcel(Parcel in) {
+            return new ChartSelectPlans(in);
+        }
+
+        @Override
+        public ChartSelectPlans[] newArray(int size) {
+            return new ChartSelectPlans[size];
+        }
+    };
+
+    public Date getCloseTime() {
+        return CloseTime;
+    }
+
+
+    public void setCloseTime(Date closeTime) {
+        CloseTime = closeTime;
+    }
+
+
+    public Date getCloseTime2() {
+        return CloseTime2;
+    }
+
+
+    public void setCloseTime2(Date closeTime2) {
+        CloseTime2 = closeTime2;
+    }
+
+
+    public Date getCloseTime3() {
+        return CloseTime3;
+    }
+
+
+    public void setCloseTime3(Date closeTime3) {
+        CloseTime3 = closeTime3;
+    }
+
+
+    public Date getEntryTime() {
+        return EntryTime;
+    }
+
+
+    public void setEntryTime(Date entryTime) {
+        EntryTime = entryTime;
+    }
+
+
+    public String getExchangeCode() {
+        return ExchangeCode;
+    }
+
+
+    public void setExchangeCode(String exchangeCode) {
+        ExchangeCode = exchangeCode;
+    }
+
+
+    public String getExchangeDate() {
+        return ExchangeDate;
+    }
+
+
+    public void setExchangeDate(String exchangeDate) {
+        ExchangeDate = exchangeDate;
+    }
+
+
+    public Date getOpenTime() {
+        return OpenTime;
+    }
+
+
+    public void setOpenTime(Date openTime) {
+        OpenTime = openTime;
+    }
+
+
+    public Date getOpenTime2() {
+        return OpenTime2;
+    }
+
+
+    public void setOpenTime2(Date openTime2) {
+        OpenTime2 = openTime2;
+    }
+
+
+    public Date getOpenTime3() {
+        return OpenTime3;
+    }
+
+
+    public void setOpenTime3(Date openTime3) {
+        OpenTime3 = openTime3;
+    }
+
+
+    public Date getSettlementTime() {
+        return SettlementTime;
+    }
+
+
+    public void setSettlementTime(Date settlementTime) {
+        SettlementTime = settlementTime;
+    }
+
+
+    public int getSort() {
+        return Sort;
+    }
+
+
+    public void setSort(int sort) {
+        Sort = sort;
+    }
+
+
+    public int getTikKind() {
+        return TikKind;
+    }
+
+
+    public void setTikKind(int tikKind) {
+        TikKind = tikKind;
+    }
+
+
+    @Override
+    public int describeContents() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel arg0, int arg1) {
+        // TODO Auto-generated method stub
+
+        arg0.writeString(ExchangeCode);
+        arg0.writeString(ExchangeDate);
+        arg0.writeInt(Sort);
+        arg0.writeInt(TikKind);
+    }
+
+}

+ 53 - 0
RMA/chart/src/main/java/com/desfate/chart/data/XChartDatas.java

@@ -0,0 +1,53 @@
+package com.desfate.chart.data;
+
+
+import com.desfate.chart.entity.OHLCEntity;
+import com.desfate.chart.entity.StickEntity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+
+/**
+ * 数据缓冲区
+ *
+ * @author
+ */
+public class XChartDatas {
+    private List<OHLCEntity> ohlcEntities;// 开高低收缓冲区
+    private List<StickEntity> stickEntities;// 柱状图形的高低数据集合
+
+    private int chartSize = 0;// chartSize是报文中的实际大小
+
+    public int getChartSize() {
+        return chartSize;
+    }
+
+    public void setChartSize(int chartSize) {
+        this.chartSize = chartSize;
+    }
+
+    public List<StickEntity> getStickEntities() {
+        if (stickEntities == null) {
+            stickEntities = new ArrayList<StickEntity>();
+        }
+        return stickEntities;
+    }
+
+    public void setStickEntities(List<StickEntity> stickEntities) {
+        this.stickEntities = stickEntities;
+    }
+
+    public List<OHLCEntity> getOhlcEntities() {
+        if (ohlcEntities == null) {
+            ohlcEntities = new ArrayList<OHLCEntity>();
+
+        }
+        return ohlcEntities;
+    }
+
+    public void setOhlcEntities(List<OHLCEntity> ohlcEntities) {
+        this.ohlcEntities = ohlcEntities;
+    }
+}

+ 171 - 0
RMA/chart/src/main/java/com/desfate/chart/entity/LineEntity.java

@@ -0,0 +1,171 @@
+package com.desfate.chart.entity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <p>Entity data which is use for display a single line in LineChart</p>
+ * <p>LineChartの線表示用データです。単線です。</p>
+ * <p>保存线图表示用单个线的对象、多条线的时候请使用相应的数据结构保存数据</p>
+ */
+public class LineEntity {
+
+    /**
+     * 允许设置画图的起始数组索引
+     */
+    private int startIndex = 0;
+
+    /**
+     * <p>Data for draw this line</p>
+     * <p>ラインを表示用データ</p>
+     * <p>线表示数据</p>
+     */
+    private List<Double> lineData;
+
+    /**
+     * <p>线的时间</p>
+     */
+    private List<String> lineTime;
+
+    /**
+     * <p>Title for this line</p>
+     * <p>ラインの表示タイトル</p>
+     * <p>线的标题,用于标识别这条线</p>
+     */
+    private String title;
+
+    /**
+     * <p>Line Color</p>
+     * <p>ラインの色</p>
+     * <p>线的颜色</p>
+     */
+    private int lineColor;
+
+    /**
+     * <p>Should display this line?</p>
+     * <p>ラインをチャードで表面で表示するか?</p>
+     * <p>是否在图表上显示该线</p>
+     */
+    private boolean display = true;
+
+    /**
+     * <p>Constructor of LineEntity</p>
+     * <p>LineEntity类对象的构造函数</p>
+     * <p>LineEntityのコンストラクター</p>
+     */
+    public LineEntity() {
+        super();
+    }
+
+    /**
+     * <p>Constructor of LineEntity</p>
+     * <p>LineEntity类对象的构造函数</p>
+     * <p>LineEntityのコンストラクター</p>
+     *
+     * @param lineData  <p>Data for draw this line</p>
+     *                  <p>ラインを表示用データ</p>
+     *                  <p>线表示数据</p>
+     * @param title     <p>Title for this line</p>
+     *                  <p>ラインの表示タイトル</p>
+     *                  <p>线的标题,用于标识别这条线</p>
+     * @param lineColor <p>Line Color</p>
+     *                  <p>ラインの色</p>
+     *                  <p>线的颜色</p>
+     */
+    public LineEntity(List<Double> lineData, String title, int lineColor) {
+        super();
+        this.lineData = lineData;
+        this.title = title;
+        this.lineColor = lineColor;
+    }
+
+    /**
+     * @param value
+     */
+    public void put(double value) {
+        if (null == lineData) {
+            lineData = new ArrayList<Double>();
+        }
+        lineData.add(value);
+    }
+
+    /**
+     * @return the lineData
+     */
+    public List<Double> getLineData() {
+        return lineData;
+    }
+
+    /**
+     * @param lineData the lineData to set
+     */
+    public void setLineData(List<Double> lineData) {
+        this.lineData = lineData;
+    }
+
+    /**
+     * @return the title
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * @param title the title to set
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * @return the lineColor
+     */
+    public int getLineColor() {
+        return lineColor;
+    }
+
+    /**
+     * @param lineColor the lineColor to set
+     */
+    public void setLineColor(int lineColor) {
+        this.lineColor = lineColor;
+    }
+
+    /**
+     * @return the display
+     */
+    public boolean isDisplay() {
+        return display;
+    }
+
+    /**
+     * @param display the display to set
+     */
+    public void setDisplay(boolean display) {
+        this.display = display;
+    }
+
+    public List<String> getLineTime() {
+        return lineTime;
+    }
+
+    public void setLineTime(List<String> lineTime) {
+        this.lineTime = lineTime;
+    }
+
+    /**
+     * @return 返回允许设置画图的起始数组索引
+     */
+    public int getStartIndex() {
+        return startIndex;
+    }
+
+    /**
+     * 默认设置为0
+     *
+     * @param startIndex 设置画图的起始数组索引
+     */
+    public void setStartIndex(int startIndex) {
+        this.startIndex = startIndex;
+    }
+}

+ 244 - 0
RMA/chart/src/main/java/com/desfate/chart/entity/OHLCEntity.java

@@ -0,0 +1,244 @@
+package com.desfate.chart.entity;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * <p>
+ * Entity data which is use for display a candleStick in CandleStickChart use
+ * OHLC(Open,High,Low,Close) four values for a candle
+ * </p>
+ * <p>
+ * CandleStickChartのロウソク線表示用データです、四本値を格納用オブジェクトです。
+ * </p>
+ * <p>
+ * 保存蜡烛线表示用的OHLC四个值的实体对象
+ * </p>
+ */
+public class OHLCEntity {
+
+    /**
+     * <p>
+     * Open Value
+     * </p>
+     * <p>
+     * 始値
+     * </p>
+     * <p>
+     * 开盘价
+     * </p>
+     */
+    private double open;
+
+    /**
+     * <p>
+     * High Value
+     * </p>
+     * <p>
+     * 高値
+     * </p>
+     * <p>
+     * 最高价
+     * </p>
+     */
+    private double high;
+
+    /**
+     * <p>
+     * Low Value
+     * </p>
+     * <p>
+     * 低値
+     * </p>
+     * <p>
+     * 最低价
+     * </p>
+     */
+    private double low;
+
+    /**
+     * <p>
+     * Close Value
+     * </p>
+     * <p>
+     * 終値
+     * </p>
+     * <p>
+     * 收盘价
+     * </p>
+     */
+    private double close;
+
+    /**
+     * <p>
+     * Date
+     * </p>
+     * <p>
+     * 日付
+     * </p>
+     * <p>
+     * 日期
+     * </p>
+     */
+    private String time;
+
+    /**
+     * <p>
+     * Constructor of OHLCEntity
+     * </p>
+     * <p>
+     * OHLCEntity类对象的构造函数
+     * </p>
+     * <p>
+     * OHLCEntityのコンストラクター
+     * </p>
+     *
+     * @param open  <p>
+     *              Open Value
+     *              </p>
+     *              <p>
+     *              始値
+     *              </p>
+     *              <p>
+     *              开盘价
+     *              </p>
+     * @param high  <p>
+     *              High Value
+     *              </p>
+     *              <p>
+     *              高値
+     *              </p>
+     *              <p>
+     *              最高价
+     *              </p>
+     * @param low   <p>
+     *              Low Value
+     *              </p>
+     *              <p>
+     *              低値
+     *              </p>
+     *              <p>
+     *              最低价
+     *              </p>
+     * @param close <p>
+     *              Close Value
+     *              </p>
+     *              <p>
+     *              終値
+     *              </p>
+     *              <p>
+     *              收盘价
+     *              </p>
+     * @param date  <p>
+     *              Date
+     *              </p>
+     *              <p>
+     *              日付
+     *              </p>
+     *              <p>
+     *              日期
+     *              </p>
+     */
+    public OHLCEntity(double open, double high, double low, double close, String time) {
+        super();
+        this.open = open;
+        this.high = high;
+        this.low = low;
+        this.close = close;
+        this.time = time;
+    }
+
+    /**
+     * <p>
+     * Constructor of OHLCEntity
+     * </p>
+     * <p>
+     * OHLCEntity类对象的构造函数
+     * </p>
+     * <p>
+     * OHLCEntityのコンストラクター
+     * </p>
+     */
+    public OHLCEntity() {
+        super();
+    }
+
+    /**
+     * @return the open
+     */
+    public double getOpen() {
+        return open;
+    }
+
+    /**
+     * @param open the open to set
+     */
+    public void setOpen(double open) {
+        this.open = open;
+    }
+
+    /**
+     * @return the high
+     */
+    public double getHigh() {
+        return high;
+    }
+
+    /**
+     * @param high the high to set
+     */
+    public void setHigh(double high) {
+        this.high = high;
+    }
+
+    /**
+     * @return the low
+     */
+    public double getLow() {
+        return low;
+    }
+
+    /**
+     * @param low the low to set
+     */
+    public void setLow(double low) {
+        this.low = low;
+    }
+
+    /**
+     * @return the close
+     */
+    public double getClose() {
+        return close;
+    }
+
+    /**
+     * @param close the close to set
+     */
+    public void setClose(double close) {
+        this.close = close;
+    }
+
+    /**
+     * @return the time
+     */
+    public String getTime() {
+        if (time.indexOf(".") > -1) {
+            time = time.split("\\.")[0];
+        }
+        return time;
+    }
+
+    public String getTime1() {
+        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MM-dd HH:mm");
+        time = simpleDateFormat.format(new Date(Long.parseLong(time)));
+        return time;
+    }
+
+    /**
+     * @param time the time to set
+     */
+    public void setTime(String time) {
+        this.time = time;
+    }
+}

+ 168 - 0
RMA/chart/src/main/java/com/desfate/chart/entity/StickEntity.java

@@ -0,0 +1,168 @@
+package com.desfate.chart.entity;
+
+/**
+ * <p>
+ * Entity data which is use for display a stick in CCSStickChart
+ * </p>
+ * <p>
+ * StickChartのスティック表示用データです、高安値を格納用オブジェクトです。
+ * </p>
+ * <p>
+ * CCSStickChart保存柱条表示用的高低值的实体对象
+ * </p>
+ */
+public class StickEntity {
+
+    /**
+     * <p>
+     * High Value
+     * </p>
+     * <p>
+     * 高値
+     * </p>
+     * <p>
+     * 最高值
+     * </p>
+     */
+    private double high;
+
+    /**
+     * <p>
+     * Low Value
+     * </p>
+     * <p>
+     * 低値
+     * </p>
+     * <p>
+     * 最低值
+     * </p>
+     */
+    private double low;
+
+    /**
+     * <p>
+     * Date
+     * </p>
+     * <p>
+     * 日付
+     * </p>
+     * <p>
+     * 日期
+     * </p>
+     */
+    private int date;
+
+    private int colorFlag = -2;
+
+    /**
+     * <p>
+     * Constructor of StickEntity
+     * </p>
+     * <p>
+     * StickEntity类对象的构造函数
+     * </p>
+     * <p>
+     * StickEntityのコンストラクター
+     * </p>
+     *
+     * @param high <p>
+     *             High Value
+     *             </p>
+     *             <p>
+     *             高値
+     *             </p>
+     *             <p>
+     *             最高价
+     *             </p>
+     * @param low  <p>
+     *             Low Value
+     *             </p>
+     *             <p>
+     *             低値
+     *             </p>
+     *             <p>
+     *             最低值
+     *             </p>
+     * @param date <p>
+     *             Date
+     *             </p>
+     *             <p>
+     *             日付
+     *             </p>
+     *             <p>
+     *             日期
+     *             </p>
+     */
+    public StickEntity(double high, double low, int date) {
+        super();
+        this.high = high;
+        this.low = low;
+        this.date = date;
+    }
+
+    /**
+     * <p>
+     * Constructor of StickEntity
+     * </p>
+     * <p>
+     * StickEntity类对象的构造函数
+     * </p>
+     * <p>
+     * StickEntityのコンストラクター
+     * </p>
+     */
+    public StickEntity() {
+        super();
+    }
+
+    /**
+     * @return the high
+     */
+    public double getHigh() {
+        return high;
+    }
+
+    /**
+     * @param high the high to set
+     */
+    public void setHigh(double high) {
+        this.high = high;
+    }
+
+    /**
+     * @return the low
+     */
+    public double getLow() {
+        return low;
+    }
+
+    /**
+     * @param low the low to set
+     */
+    public void setLow(double low) {
+        this.low = low;
+    }
+
+    /**
+     * @return the date
+     */
+    public int getDate() {
+        return date;
+    }
+
+    /**
+     * @param date the date to set
+     */
+    public void setDate(int date) {
+        this.date = date;
+    }
+
+
+    public int getColorFlag() {
+        return colorFlag;
+    }
+
+    public void setColorFlag(int colorFlag) {
+        this.colorFlag = colorFlag;
+    }
+}

+ 20 - 0
RMA/chart/src/main/java/com/desfate/chart/entity/TimeLineEntity.java

@@ -0,0 +1,20 @@
+package com.desfate.chart.entity;
+
+import java.util.Date;
+import java.util.List;
+
+public class TimeLineEntity extends LineEntity {
+
+    /**
+     * <p>线的时间</p>
+     */
+    private List<Date> lineDate;
+
+    public List<Date> getLineDate() {
+        return lineDate;
+    }
+
+    public void setLineDate(List<Date> lineDate) {
+        this.lineDate = lineDate;
+    }
+}

+ 60 - 0
RMA/chart/src/main/java/com/desfate/chart/event/ITouchEventNotify.java

@@ -0,0 +1,60 @@
+package com.desfate.chart.event;
+
+
+import com.desfate.chart.GridChart;
+
+/**
+ * <p>Interface for chart which is support send notify after touch event happened</p>
+ * <p>タッチイベントは通知可能のオブジェクトのインタフェース</p>
+ * <p>touch事件发生后,支持对外发送事件消息的此类对象接口</p>
+ *
+ * @see
+ */
+public interface ITouchEventNotify {
+
+    int EVENT_MOVE = 0;
+    int EVENT_SCALE = 1;
+    int EVENT_CROSSONTOUCH = 2;
+    int EVENT_UP = 3;
+    int EVENT_LEFTEND = 4; //yzq 2016 12-06
+
+    /**
+     * <p>Notify all ITouchEventResponse objects</p>
+     * <p>全部ITouchEventResponseレスポンスオブジェクトを通知</p>
+     * <p>通知全部ITouchEventResponse响应对象</p>
+     *
+     * @param chart <p>source chart</p>
+     *              <p>ソースチャート</p>
+     *              <p>源头对象</p>
+     */
+    void notifyEventAll(int eventId, GridChart chart);
+
+    /**
+     * <p>Add a ITouchEventResponse object by its index</p>
+     * <p>ITouchEventResponseレスポンスオブジェクトを追加</p>
+     * <p>增加ITouchEventResponse响应对象</p>
+     *
+     * @param notify <p>ITouchEventResponse object</p>
+     *               <p>ITouchEventResponse オブジェクト</p>
+     *               <p>对象</p>
+     */
+    void addNotify(ITouchEventResponse notify);
+
+    /**
+     * <p>Remove a ITouchEventResponse object by its index</p>
+     * <p>ITouchEventResponseレスポンスオブジェクトを削除</p>
+     * <p>删除ITouchEventResponse响应对象</p>
+     *
+     * @param i <p>index</p>
+     *          <p>インデックス</p>
+     *          <p>index</p>
+     */
+    void removeNotify(int i);
+
+    /**
+     * <p>Remove all ITouchEventResponse objects</p>
+     * <p>全部ITouchEventResponseレスポンスオブジェクトを削除</p>
+     * <p>删除全部ITouchEventResponse响应对象</p>
+     */
+    void removeAllNotify();
+}

+ 25 - 0
RMA/chart/src/main/java/com/desfate/chart/event/ITouchEventResponse.java

@@ -0,0 +1,25 @@
+package com.desfate.chart.event;
+
+
+import com.desfate.chart.GridChart;
+
+/**
+ * <p>Interface for chart which is support response notify from other object</p>
+ * <p>タッチイベント通知はレスピンズのオブジェクトのインタフェース</p>
+ * <p>支持响应事件消息的此类对象接口</p>
+ *
+ * @see ITouchEventNotify
+ */
+public interface ITouchEventResponse {
+
+    /**
+     * <p>Response Notify </p>
+     * <p>レスポンスをする</p>
+     * <p>响应通知</p>
+     *
+     * @param chart <p>source chart</p>
+     *              <p>ソースチャート</p>
+     *              <p>源头对象</p>
+     */
+    void notifyEvent(int eventId, GridChart chart);
+}

+ 82 - 0
RMA/chart/src/main/java/com/desfate/chart/util/LogUtils.java

@@ -0,0 +1,82 @@
+package com.desfate.chart.util;
+
+import android.util.Log;
+
+/**
+ * log输出<li>含有保护机制
+ * @author Administrator
+ */
+public class LogUtils {
+	private static boolean isDebug = true;//将全部的log都关闭或者打开
+
+
+    private static boolean isLEnable = true; //只关闭 l 类型的log  //链路层log
+
+
+	private static boolean isWEnable = true;// 只关闭 w 类型的log
+	private static boolean isDEnable = true;// 只关闭 d 类型的log
+	private static boolean isIEnable = true;// 只关闭 i 类型的log
+	private static boolean isEEnable = true;// 只关闭 e 类型的log
+
+
+    /**
+     * 用于发送链路层log  请勿乱用
+     *
+     * @param tag
+     * @param msg
+     */
+    public static void l(String tag, String msg) {
+        if (msg == null) {
+            return;
+        }
+        if (isDebug)
+            Log.i(tag, msg);
+    }
+
+
+
+	public static void w(String tag, String msg) {
+		if (msg == null) {
+			return;
+		}
+		if (isDebug)
+			Log.w(tag, msg);
+	}
+	
+	public static void w(String tag, Throwable tr) {
+		if (tr == null) {
+			return;
+		}
+		if (isDebug && isWEnable)
+			Log.w(tag, tr);
+	}
+	
+	public static void d(String tag, String msg) {
+		if (msg == null) {
+			return;
+		}
+		if (isDebug && isDEnable)
+			Log.d(tag, msg);
+	}
+	
+	public static void i(String tag, String msg) {
+		if (msg == null) {
+			return;
+		}
+		if (isDebug && isIEnable)
+			Log.i(tag, msg);
+	}
+	
+	public static void e(String tag, String msg) {
+		if (msg == null) {
+			return;
+		}
+		if (isDebug && isEEnable)
+			Log.e(tag, msg);
+	}
+	
+	public static boolean isDebug() {
+		return isDebug;
+	}
+	
+}

+ 428 - 0
RMA/chart/src/main/java/com/desfate/chart/util/MathUtil.java

@@ -0,0 +1,428 @@
+package com.desfate.chart.util;
+
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+/**
+ * Created by cxl on 2018/4/11.
+ */
+
+public class MathUtil {
+
+    /**
+     * 四舍五入保留小数点后两位
+     *
+     * @param string
+     * @param num    预留小数位
+     * @return
+     */
+    public static String roundNum(String string, int num) {
+        if ("".equals(string)) {
+            return string;
+        }
+        String s = "0";
+        switch (num) {
+            case 0:
+                s = "0";
+                break;
+            case 1:
+                s = "0.0";
+                break;
+            case 2:
+                s = "0.00";
+                break;
+            case 3:
+                s = "0.000";
+                break;
+            case 4:
+                s = "0.0000";
+                break;
+            case 5:
+                s = "0.00000";
+                break;
+            default:
+                s = "0.00000";
+                break;
+        }
+        DecimalFormat decimalFormat = new DecimalFormat(s);
+        return decimalFormat.format(Double.parseDouble(string));
+    }
+
+    /**
+     * 四舍五入保留小数点后两位
+     *
+     * @param value
+     * @param num   预留小数位
+     * @return
+     */
+    public static String roundNum(double value, int num) {
+        String s = "0";
+        switch (num) {
+            case 0:
+                s = "0";
+                break;
+            case 1:
+                s = "0.0";
+                break;
+            case 2:
+                s = "0.00";
+                break;
+            case 3:
+                s = "0.000";
+                break;
+            case 4:
+                s = "0.0000";
+                break;
+            case 5:
+                s = "0.00000";
+                break;
+            default:
+                s = "0.00000";
+                break;
+        }
+        DecimalFormat decimalFormat = new DecimalFormat(s);
+        return decimalFormat.format(value);
+    }
+
+    // FIXME: 2018/10/20  要加个方法  2位以内 不限制  4位再限制
+
+
+    /**
+     * 四舍五入保留小数点后两位
+     *
+     * @param string
+     * @param num    预留小数位
+     * @return
+     */
+    public static String roundNoNum(String string, int num) {
+        if ("".equals(string)) {
+            return string;
+        }
+        String s = "0";
+        switch (num) {
+            case 0:
+                s = "0";
+                break;
+            case 1:
+                s = "0.0";
+                break;
+            case 2:
+                s = "0.00";
+                break;
+            case 3:
+                s = "0.000";
+                break;
+            case 4:
+                s = "0.0000";
+                break;
+            case 5:
+                s = "0.00000";
+                break;
+            default:
+                s = "0.00000";
+                break;
+        }
+        DecimalFormat decimalFormat = new DecimalFormat(s);
+        return decimalFormat.format(Double.parseDouble(string));
+    }
+
+    /**
+     * 判断字符串是否为大于0 的有效数字
+     *
+     * @param number
+     * @return
+     */
+    public static boolean isZeroOrNull(String number) {
+        if (number == null || number.equals("") || number.equals("0")) {
+            return true;
+        }
+
+        double temp = 0;
+        try {
+            temp = Double.parseDouble(number);
+        } catch (Exception e) {
+            // TODO: handle exception
+        }
+
+        return !(temp > 0);
+    }
+
+
+    public static boolean isEmpty(String str) {
+        return !(str == null || str.equals("") || str.equals("null"));
+    }
+
+    /**
+     * 向上取整
+     *
+     * @param value
+     * @param num
+     * @return
+     */
+    public static String roundNumDown(double value, int num) {
+        String s = "0";
+        switch (num) {
+            case 0:
+                s = "0";
+                break;
+            case 1:
+                s = "0.0";
+                break;
+            case 2:
+                s = "0.00";
+                break;
+            case 3:
+                s = "0.000";
+                break;
+            case 4:
+                s = "0.0000";
+                break;
+            case 5:
+                s = "0.00000";
+                break;
+            default:
+                s = "0.00000";
+                break;
+        }
+        DecimalFormat decimalFormat = new DecimalFormat(s);
+        decimalFormat.setRoundingMode(RoundingMode.DOWN);
+        return decimalFormat.format(value);
+    }
+
+    /**
+     * 向上取整
+     *
+     * @param string
+     * @param num    预留小数位
+     * @return
+     */
+    public static String roundNumDown(String string, int num) {
+        if ("".equals(string)) {
+            return string;
+        }
+        String s = "0";
+        switch (num) {
+            case 0:
+                s = "0";
+                break;
+            case 1:
+                s = "0.0";
+                break;
+            case 2:
+                s = "0.00";
+                break;
+            case 3:
+                s = "0.000";
+                break;
+            case 4:
+                s = "0.0000";
+                break;
+            case 5:
+                s = "0.00000";
+                break;
+            default:
+                s = "0.00000";
+                break;
+        }
+        DecimalFormat decimalFormat = new DecimalFormat(s);
+        decimalFormat.setRoundingMode(RoundingMode.DOWN);
+        return decimalFormat.format(Double.parseDouble(string));
+    }
+
+
+    /**
+     * 向上取整
+     *
+     * @param value
+     * @param num
+     * @return
+     */
+    public static String roundNumUP(double value, int num) {
+        String s = "0";
+        switch (num) {
+            case 0:
+                s = "0";
+                break;
+            case 1:
+                s = "0.0";
+                break;
+            case 2:
+                s = "0.00";
+                break;
+            case 3:
+                s = "0.000";
+                break;
+            case 4:
+                s = "0.0000";
+                break;
+            case 5:
+                s = "0.00000";
+                break;
+            default:
+                s = "0.00000";
+                break;
+        }
+        DecimalFormat decimalFormat = new DecimalFormat(s);
+        decimalFormat.setRoundingMode(RoundingMode.UP);
+        return decimalFormat.format(value);
+    }
+
+    /**
+     * 向上取整
+     *
+     * @param string
+     * @param num    预留小数位
+     * @return
+     */
+    public static String roundNumUP(String string, int num) {
+        if ("".equals(string)) {
+            return string;
+        }
+        String s = "0";
+        switch (num) {
+            case 0:
+                s = "0";
+                break;
+            case 1:
+                s = "0.0";
+                break;
+            case 2:
+                s = "0.00";
+                break;
+            case 3:
+                s = "0.000";
+                break;
+            case 4:
+                s = "0.0000";
+                break;
+            case 5:
+                s = "0.00000";
+                break;
+            default:
+                s = "0.00000";
+                break;
+        }
+        DecimalFormat decimalFormat = new DecimalFormat(s);
+        decimalFormat.setRoundingMode(RoundingMode.UP);
+        return decimalFormat.format(Double.parseDouble(string));
+    }
+
+
+
+
+    /**
+     * 获取数据有几位小数
+     *
+     * @param price
+     * @return
+     */
+    public static int getPointNum(double price) {
+        String myPrice = String.valueOf(price);
+        String[] split = myPrice.split("\\.");
+        if (split.length == 2) {
+            return split[1].length();
+        } else {
+            return 0;
+        }
+    }
+
+    public static int getPointNum(String price) {
+        String myPrice = price;
+        String[] split = myPrice.split("\\.");
+        if (split.length == 2) {
+            return split[1].length();
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * 获取有几位整数
+     *
+     * @param price
+     * @return
+     */
+    public static int getIntegerNum(String price) {
+        String myPrice = price;
+        String[] split = myPrice.split("\\.");
+        if (split.length == 2) {
+            return split[0].length();
+        } else {
+            return split[0].length();
+        }
+    }
+
+
+
+    //去除尾部不需要的0
+    public static String removeBackZero(String num) {
+        String[] split = num.split("\\.");
+        if (split.length == 2) {
+            if (split[1].equals("0")) {
+                return split[0];
+            } else if (split[1].equals("00")) {
+                return split[0];
+            } else if (split[1].equals("000")) {
+                return split[0];
+            } else if (split[1].equals("0000")) {
+                return split[0];
+            } else {
+                return num;
+            }
+        } else {
+            return num;
+        }
+    }
+
+
+    public static String doubleToString(double info) {
+        DecimalFormat decimalFormat = new DecimalFormat("#,##0.00");//格式化设置
+        return decimalFormat.format(info);
+    }
+
+    /**
+     * Double 转string 去除科学记数法显示
+     *
+     * @param d
+     * @return
+     */
+    public static String double2Str(Double d) {
+        if (d == null) {
+            return "";
+        }
+        NumberFormat nf = NumberFormat.getInstance();
+        nf.setGroupingUsed(false);
+        return (nf.format(d));
+    }
+
+    //判断是否是数字
+    public static boolean isNumeric(String str)
+    {
+        if(str == null || str.length() == 0){
+            return false;
+        }
+        for (int i = 0; i < str.length(); i++) {
+            if (!Character.isDigit(str.charAt(i))) {
+            return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 校验 是否是double
+     * @param str
+     * @return
+     */
+    public static boolean isDouble(String str){
+        if(str == null || str.length() == 0){
+            return false;
+        }
+        try{
+            Double.valueOf(str);
+            return true;
+        }catch (NumberFormatException e){
+            return false;
+        }
+    }
+}

+ 1 - 0
RMA/settings.gradle

@@ -1,2 +1,3 @@
+include ':chart'
 include ':app'
 rootProject.name = "RMA"