的
@@ -0,0 +1,10 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -0,0 +1,36 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
android {
|
||||
compileSdkVersion 27
|
||||
defaultConfig {
|
||||
applicationId "com.github.fujianlian.klinechartdemo"
|
||||
minSdkVersion 17
|
||||
targetSdkVersion 27
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
|
||||
implementation 'com.android.support:appcompat-v7:27.1.1'
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
||||
implementation project(':KLineChartLib')
|
||||
implementation 'com.google.code.gson:gson:2.2.4'
|
||||
implementation 'org.jetbrains.anko:anko-sdk19:+'
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.github.fujianlian.klinechartdemo
|
||||
|
||||
import android.support.test.InstrumentationRegistry
|
||||
import android.support.test.runner.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getTargetContext()
|
||||
assertEquals("com.github.fujianlian.klinechartdemo", appContext.packageName)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.github.fujianlian.klinechartdemo">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/logo"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity android:name=".MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.github.fujianlian.klinechartdemo;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.github.fujianlian.klinechart.DataHelper;
|
||||
import com.github.fujianlian.klinechart.KLineEntity;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 模拟网络请求
|
||||
* Created by tifezh on 2017/7/3.
|
||||
*/
|
||||
|
||||
public class DataRequest {
|
||||
private static List<KLineEntity> datas = null;
|
||||
|
||||
public static String getStringFromAssert(Context context, String fileName) {
|
||||
try {
|
||||
InputStream in = context.getResources().getAssets().open(fileName);
|
||||
int length = in.available();
|
||||
byte[] buffer = new byte[length];
|
||||
in.read(buffer);
|
||||
return new String(buffer, 0, buffer.length, "UTF-8");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static List<KLineEntity> getALL(Context context) {
|
||||
if (datas == null) {
|
||||
final List<KLineEntity> data = new Gson().fromJson(getStringFromAssert(context, "ibm.json"), new TypeToken<List<KLineEntity>>() {
|
||||
}.getType());
|
||||
DataHelper.calculate(data);
|
||||
datas = data;
|
||||
}
|
||||
return datas;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
*
|
||||
* @param context
|
||||
* @param offset 开始的索引
|
||||
* @param size 每次查询的条数
|
||||
*/
|
||||
public static List<KLineEntity> getData(Context context, int offset, int size) {
|
||||
List<KLineEntity> all = getALL(context);
|
||||
List<KLineEntity> data = new ArrayList<>();
|
||||
int start = Math.max(0, all.size() - 1 - offset - size);
|
||||
int stop = Math.min(all.size(), all.size() - offset);
|
||||
for (int i = start; i < stop; i++) {
|
||||
data.add(all.get(i));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.github.fujianlian.klinechartdemo
|
||||
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.widget.TextView
|
||||
import com.github.fujianlian.klinechart.DataHelper
|
||||
import com.github.fujianlian.klinechart.KLineChartAdapter
|
||||
import com.github.fujianlian.klinechart.KLineEntity
|
||||
import com.github.fujianlian.klinechart.draw.Status
|
||||
import com.github.fujianlian.klinechart.formatter.DateFormatter
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import org.jetbrains.anko.doAsync
|
||||
import org.jetbrains.anko.textColor
|
||||
import java.util.*
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var datas: List<KLineEntity>
|
||||
|
||||
private val adapter by lazy { KLineChartAdapter() }
|
||||
|
||||
private val subTexts: ArrayList<TextView> by lazy { arrayListOf(macdText, kdjText, rsiText, wrText) }
|
||||
// 主图指标下标
|
||||
private var mainIndex = 0
|
||||
// 副图指标下标
|
||||
private var subIndex = -1
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
kLineChartView.adapter = adapter
|
||||
kLineChartView.dateTimeFormatter = DateFormatter()
|
||||
kLineChartView.setGridRows(4)
|
||||
kLineChartView.setGridColumns(4)
|
||||
initData()
|
||||
initListener()
|
||||
}
|
||||
|
||||
private fun initData() {
|
||||
kLineChartView.justShowLoading()
|
||||
doAsync {
|
||||
datas = DataRequest.getALL(this@MainActivity).subList(0, 500)
|
||||
DataHelper.calculate(datas)
|
||||
runOnUiThread {
|
||||
adapter.addFooterData(datas)
|
||||
adapter.notifyDataSetChanged()
|
||||
kLineChartView.startAnimation()
|
||||
kLineChartView.refreshEnd()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initListener() {
|
||||
maText.setOnClickListener {
|
||||
if (mainIndex != 0) {
|
||||
kLineChartView.hideSelectData()
|
||||
mainIndex = 0
|
||||
maText.textColor = Color.parseColor("#eeb350")
|
||||
bollText.textColor = Color.WHITE
|
||||
kLineChartView.changeMainDrawType(Status.MA)
|
||||
}
|
||||
}
|
||||
bollText.setOnClickListener {
|
||||
if (mainIndex != 1) {
|
||||
kLineChartView.hideSelectData()
|
||||
mainIndex = 1
|
||||
bollText.textColor = Color.parseColor("#eeb350")
|
||||
maText.textColor = Color.WHITE
|
||||
kLineChartView.changeMainDrawType(Status.BOLL)
|
||||
}
|
||||
}
|
||||
mainHide.setOnClickListener {
|
||||
if (mainIndex != -1) {
|
||||
kLineChartView.hideSelectData()
|
||||
mainIndex = -1
|
||||
bollText.textColor = Color.WHITE
|
||||
maText.textColor = Color.WHITE
|
||||
kLineChartView.changeMainDrawType(Status.NONE)
|
||||
}
|
||||
}
|
||||
for ((index, text) in subTexts.withIndex()) {
|
||||
text.setOnClickListener {
|
||||
if (subIndex != index) {
|
||||
kLineChartView.hideSelectData()
|
||||
if (subIndex != -1) {
|
||||
subTexts[subIndex].textColor = Color.WHITE
|
||||
}
|
||||
subIndex = index
|
||||
text.textColor = Color.parseColor("#eeb350")
|
||||
kLineChartView.setChildDraw(subIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
subHide.setOnClickListener {
|
||||
if (subIndex != -1) {
|
||||
kLineChartView.hideSelectData()
|
||||
subTexts[subIndex].textColor = Color.WHITE
|
||||
subIndex = -1
|
||||
kLineChartView.hideChildDraw()
|
||||
}
|
||||
}
|
||||
fenText.setOnClickListener {
|
||||
kLineChartView.hideSelectData()
|
||||
fenText.textColor = Color.parseColor("#eeb350")
|
||||
kText.textColor = Color.WHITE
|
||||
kLineChartView.setMainDrawLine(true)
|
||||
}
|
||||
kText.setOnClickListener {
|
||||
kLineChartView.hideSelectData()
|
||||
kText.textColor = Color.parseColor("#eeb350")
|
||||
fenText.textColor = Color.WHITE
|
||||
kLineChartView.setMainDrawLine(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108">
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="78.5885"
|
||||
android:endY="90.9159"
|
||||
android:startX="48.7653"
|
||||
android:startY="61.0927"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1" />
|
||||
</vector>
|
||||
@@ -0,0 +1,170 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108">
|
||||
<path
|
||||
android:fillColor="#26A69A"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
</vector>
|
||||
@@ -0,0 +1,156 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/colorPrimary"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text1"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="40dp"
|
||||
android:gravity="center"
|
||||
android:text="主图"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/maText"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_toRightOf="@+id/text1"
|
||||
android:gravity="center"
|
||||
android:text="MA"
|
||||
android:textColor="@color/colorAccent" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bollText"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_toRightOf="@+id/maText"
|
||||
android:gravity="center"
|
||||
android:text="BOLL"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/mainHide"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_toRightOf="@+id/bollText"
|
||||
android:gravity="center"
|
||||
android:text="隐藏"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text2"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_below="@+id/text1"
|
||||
android:layout_marginTop="10dp"
|
||||
android:gravity="center"
|
||||
android:text="副图"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/macdText"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_alignBottom="@+id/text2"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_toRightOf="@+id/text2"
|
||||
android:gravity="center"
|
||||
android:text="MACD"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/kdjText"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_alignBottom="@+id/macdText"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_toRightOf="@+id/macdText"
|
||||
android:gravity="center"
|
||||
android:text="KDJ"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rsiText"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_alignBottom="@+id/kdjText"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_toRightOf="@+id/kdjText"
|
||||
android:gravity="center"
|
||||
android:text="RSI"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/wrText"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_alignBottom="@+id/rsiText"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_toRightOf="@+id/rsiText"
|
||||
android:gravity="center"
|
||||
android:text="WR"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subHide"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_alignBottom="@+id/wrText"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_toRightOf="@+id/wrText"
|
||||
android:gravity="center"
|
||||
android:text="隐藏"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/fenText"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_below="@+id/text2"
|
||||
android:gravity="center"
|
||||
android:text="分时"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/kText"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_below="@+id/text2"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_toRightOf="@+id/text1"
|
||||
android:gravity="center"
|
||||
android:text="k线图"
|
||||
android:textColor="@color/colorAccent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/view1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="10dp"
|
||||
android:layout_below="@+id/fenText"
|
||||
android:background="#17181f" />
|
||||
|
||||
<com.github.fujianlian.klinechart.KLineChartView
|
||||
android:id="@+id/kLineChartView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_above="@+id/view2"
|
||||
android:layout_below="@+id/view1" />
|
||||
|
||||
<View
|
||||
android:id="@+id/view2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="10dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:background="#17181f" />
|
||||
</RelativeLayout>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#1d1f26</color>
|
||||
<color name="colorPrimaryDark">#1d1f26</color>
|
||||
<color name="colorAccent">#efb350</color>
|
||||
<color name="white">#ffffff</color>
|
||||
</resources>
|
||||
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">KLineChart</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,11 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.github.fujianlian.klinechartdemo
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
@Test
|
||||
fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -0,0 +1,32 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 27
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 27
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
implementation 'com.android.support:appcompat-v7:27.1.1'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.github.fujianlian.klinechart;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getTargetContext();
|
||||
|
||||
assertEquals("com.github.fujianlian.klinechart.test", appContext.getPackageName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.github.fujianlian.klinechart" />
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.github.fujianlian.klinechart;
|
||||
|
||||
import android.database.DataSetObservable;
|
||||
import android.database.DataSetObserver;
|
||||
|
||||
import com.github.fujianlian.klinechart.base.IAdapter;
|
||||
|
||||
/**
|
||||
* k线图的数据适配器
|
||||
* Created by tifezh on 2016/6/9.
|
||||
*/
|
||||
|
||||
public abstract class BaseKLineChartAdapter implements IAdapter {
|
||||
|
||||
private final DataSetObservable mDataSetObservable = new DataSetObservable();
|
||||
|
||||
@Override
|
||||
public void notifyDataSetChanged() {
|
||||
if (getCount() > 0) {
|
||||
mDataSetObservable.notifyChanged();
|
||||
} else {
|
||||
mDataSetObservable.notifyInvalidated();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void registerDataSetObserver(DataSetObserver observer) {
|
||||
mDataSetObservable.registerObserver(observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterDataSetObserver(DataSetObserver observer) {
|
||||
mDataSetObservable.unregisterObserver(observer);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
package com.github.fujianlian.klinechart;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据辅助类 计算macd rsi等
|
||||
* Created by tifezh on 2016/11/26.
|
||||
*/
|
||||
public class DataHelper {
|
||||
|
||||
/**
|
||||
* 计算RSI
|
||||
*
|
||||
* @param dataList
|
||||
*/
|
||||
static void calculateRSI(List<KLineEntity> dataList) {
|
||||
Float rsi;
|
||||
float rsiABSEma = 0;
|
||||
float rsiMaxEma = 0;
|
||||
for (int i = 0; i < dataList.size(); i++) {
|
||||
KLineEntity point = dataList.get(i);
|
||||
final float closePrice = point.getClosePrice();
|
||||
if (i == 0) {
|
||||
rsi = 0f;
|
||||
rsiABSEma = 0;
|
||||
rsiMaxEma = 0;
|
||||
} else {
|
||||
float Rmax = Math.max(0, closePrice - dataList.get(i - 1).getClosePrice());
|
||||
float RAbs = Math.abs(closePrice - dataList.get(i - 1).getClosePrice());
|
||||
|
||||
rsiMaxEma = (Rmax + (14f - 1) * rsiMaxEma) / 14f;
|
||||
rsiABSEma = (RAbs + (14f - 1) * rsiABSEma) / 14f;
|
||||
rsi = (rsiMaxEma / rsiABSEma) * 100;
|
||||
}
|
||||
if (i < 13) {
|
||||
rsi = 0f;
|
||||
}
|
||||
if (rsi.isNaN())
|
||||
rsi = 0f;
|
||||
point.rsi = rsi;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算kdj
|
||||
*
|
||||
* @param dataList
|
||||
*/
|
||||
static void calculateKDJ(List<KLineEntity> dataList) {
|
||||
float k = 0;
|
||||
float d = 0;
|
||||
for (int i = 0; i < dataList.size(); i++) {
|
||||
KLineEntity point = dataList.get(i);
|
||||
final float closePrice = point.getClosePrice();
|
||||
int startIndex = i - 13;
|
||||
if (startIndex < 0) {
|
||||
startIndex = 0;
|
||||
}
|
||||
float max14 = Float.MIN_VALUE;
|
||||
float min14 = Float.MAX_VALUE;
|
||||
for (int index = startIndex; index <= i; index++) {
|
||||
max14 = Math.max(max14, dataList.get(index).getHighPrice());
|
||||
min14 = Math.min(min14, dataList.get(index).getLowPrice());
|
||||
}
|
||||
Float rsv = 100f * (closePrice - min14) / (max14 - min14);
|
||||
if (rsv.isNaN()) {
|
||||
rsv = 0f;
|
||||
}
|
||||
if (i == 0) {
|
||||
k = 50;
|
||||
d = 50;
|
||||
} else {
|
||||
k = (rsv + 2f * k) / 3f;
|
||||
d = (k + 2f * d) / 3f;
|
||||
}
|
||||
if (i < 13) {
|
||||
point.k = 0;
|
||||
point.d = 0;
|
||||
point.j = 0;
|
||||
} else if (i == 13 || i == 14) {
|
||||
point.k = k;
|
||||
point.d = 0;
|
||||
point.j = 0;
|
||||
} else {
|
||||
point.k = k;
|
||||
point.d = d;
|
||||
point.j = 3f * k - 2 * d;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算wr
|
||||
*
|
||||
* @param dataList
|
||||
*/
|
||||
static void calculateWR(List<KLineEntity> dataList) {
|
||||
Float r;
|
||||
for (int i = 0; i < dataList.size(); i++) {
|
||||
KLineEntity point = dataList.get(i);
|
||||
int startIndex = i - 14;
|
||||
if (startIndex < 0) {
|
||||
startIndex = 0;
|
||||
}
|
||||
float max14 = Float.MIN_VALUE;
|
||||
float min14 = Float.MAX_VALUE;
|
||||
for (int index = startIndex; index <= i; index++) {
|
||||
max14 = Math.max(max14, dataList.get(index).getHighPrice());
|
||||
min14 = Math.min(min14, dataList.get(index).getLowPrice());
|
||||
}
|
||||
if (i < 13) {
|
||||
point.r = -10;
|
||||
} else {
|
||||
r = -100 * (max14 - dataList.get(i).getClosePrice()) / (max14 - min14);
|
||||
if (r.isNaN()) {
|
||||
point.r = 0;
|
||||
} else {
|
||||
point.r = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算macd
|
||||
*
|
||||
* @param dataList
|
||||
*/
|
||||
static void calculateMACD(List<KLineEntity> dataList) {
|
||||
float ema12 = 0;
|
||||
float ema26 = 0;
|
||||
float dif = 0;
|
||||
float dea = 0;
|
||||
float macd = 0;
|
||||
|
||||
for (int i = 0; i < dataList.size(); i++) {
|
||||
KLineEntity point = dataList.get(i);
|
||||
final float closePrice = point.getClosePrice();
|
||||
if (i == 0) {
|
||||
ema12 = closePrice;
|
||||
ema26 = closePrice;
|
||||
} else {
|
||||
// EMA(12) = 前一日EMA(12) X 11/13 + 今日收盘价 X 2/13
|
||||
ema12 = ema12 * 11f / 13f + closePrice * 2f / 13f;
|
||||
// EMA(26) = 前一日EMA(26) X 25/27 + 今日收盘价 X 2/27
|
||||
ema26 = ema26 * 25f / 27f + closePrice * 2f / 27f;
|
||||
}
|
||||
// DIF = EMA(12) - EMA(26) 。
|
||||
// 今日DEA = (前一日DEA X 8/10 + 今日DIF X 2/10)
|
||||
// 用(DIF-DEA)*2即为MACD柱状图。
|
||||
dif = ema12 - ema26;
|
||||
dea = dea * 8f / 10f + dif * 2f / 10f;
|
||||
macd = (dif - dea) * 2f;
|
||||
point.dif = dif;
|
||||
point.dea = dea;
|
||||
point.macd = macd;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算 BOLL 需要在计算ma之后进行
|
||||
*
|
||||
* @param dataList
|
||||
*/
|
||||
static void calculateBOLL(List<KLineEntity> dataList) {
|
||||
for (int i = 0; i < dataList.size(); i++) {
|
||||
KLineEntity point = dataList.get(i);
|
||||
if (i < 19) {
|
||||
point.mb = 0;
|
||||
point.up = 0;
|
||||
point.dn = 0;
|
||||
} else {
|
||||
int n = 20;
|
||||
float md = 0;
|
||||
for (int j = i - n + 1; j <= i; j++) {
|
||||
float c = dataList.get(j).getClosePrice();
|
||||
float m = point.getMA20Price();
|
||||
float value = c - m;
|
||||
md += value * value;
|
||||
}
|
||||
md = md / (n - 1);
|
||||
md = (float) Math.sqrt(md);
|
||||
point.mb = point.getMA20Price();
|
||||
point.up = point.mb + 2f * md;
|
||||
point.dn = point.mb - 2f * md;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算ma
|
||||
*
|
||||
* @param dataList
|
||||
*/
|
||||
static void calculateMA(List<KLineEntity> dataList) {
|
||||
float ma5 = 0;
|
||||
float ma10 = 0;
|
||||
float ma20 = 0;
|
||||
float ma30 = 0;
|
||||
float ma60 = 0;
|
||||
|
||||
for (int i = 0; i < dataList.size(); i++) {
|
||||
KLineEntity point = dataList.get(i);
|
||||
final float closePrice = point.getClosePrice();
|
||||
|
||||
ma5 += closePrice;
|
||||
ma10 += closePrice;
|
||||
ma20 += closePrice;
|
||||
ma30 += closePrice;
|
||||
ma60 += closePrice;
|
||||
if (i == 4) {
|
||||
point.MA5Price = ma5 / 5f;
|
||||
} else if (i >= 5) {
|
||||
ma5 -= dataList.get(i - 5).getClosePrice();
|
||||
point.MA5Price = ma5 / 5f;
|
||||
} else {
|
||||
point.MA5Price = 0f;
|
||||
}
|
||||
if (i == 9) {
|
||||
point.MA10Price = ma10 / 10f;
|
||||
} else if (i >= 10) {
|
||||
ma10 -= dataList.get(i - 10).getClosePrice();
|
||||
point.MA10Price = ma10 / 10f;
|
||||
} else {
|
||||
point.MA10Price = 0f;
|
||||
}
|
||||
if (i == 19) {
|
||||
point.MA20Price = ma20 / 20f;
|
||||
} else if (i >= 20) {
|
||||
ma20 -= dataList.get(i - 20).getClosePrice();
|
||||
point.MA20Price = ma20 / 20f;
|
||||
} else {
|
||||
point.MA20Price = 0f;
|
||||
}
|
||||
if (i == 29) {
|
||||
point.MA30Price = ma30 / 30f;
|
||||
} else if (i >= 30) {
|
||||
ma30 -= dataList.get(i - 30).getClosePrice();
|
||||
point.MA30Price = ma30 / 30f;
|
||||
} else {
|
||||
point.MA30Price = 0f;
|
||||
}
|
||||
if (i == 59) {
|
||||
point.MA60Price = ma60 / 60f;
|
||||
} else if (i >= 60) {
|
||||
ma60 -= dataList.get(i - 60).getClosePrice();
|
||||
point.MA60Price = ma60 / 60f;
|
||||
} else {
|
||||
point.MA60Price = 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算MA BOLL RSI KDJ MACD
|
||||
*
|
||||
* @param dataList
|
||||
*/
|
||||
public static void calculate(List<KLineEntity> dataList) {
|
||||
calculateMA(dataList);
|
||||
calculateMACD(dataList);
|
||||
calculateBOLL(dataList);
|
||||
calculateRSI(dataList);
|
||||
calculateKDJ(dataList);
|
||||
calculateWR(dataList);
|
||||
calculateVolumeMA(dataList);
|
||||
}
|
||||
|
||||
private static void calculateVolumeMA(List<KLineEntity> entries) {
|
||||
float volumeMa5 = 0;
|
||||
float volumeMa10 = 0;
|
||||
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
KLineEntity entry = entries.get(i);
|
||||
|
||||
volumeMa5 += entry.getVolume();
|
||||
volumeMa10 += entry.getVolume();
|
||||
|
||||
if (i == 4) {
|
||||
entry.MA5Volume = (volumeMa5 / 5f);
|
||||
} else if (i > 4) {
|
||||
volumeMa5 -= entries.get(i - 5).getVolume();
|
||||
entry.MA5Volume = volumeMa5 / 5f;
|
||||
} else {
|
||||
entry.MA5Volume = 0f;
|
||||
}
|
||||
|
||||
if (i == 9) {
|
||||
entry.MA10Volume = volumeMa10 / 10f;
|
||||
} else if (i > 9) {
|
||||
volumeMa10 -= entries.get(i - 10).getVolume();
|
||||
entry.MA10Volume = volumeMa10 / 10f;
|
||||
} else {
|
||||
entry.MA10Volume = 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.github.fujianlian.klinechart;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据适配器
|
||||
* Created by tifezh on 2016/6/18.
|
||||
*/
|
||||
public class KLineChartAdapter extends BaseKLineChartAdapter {
|
||||
|
||||
private List<KLineEntity> datas = new ArrayList<>();
|
||||
|
||||
public KLineChartAdapter() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return datas.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return datas.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDate(int position) {
|
||||
return datas.get(position).Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* 向头部添加数据
|
||||
*/
|
||||
public void addHeaderData(List<KLineEntity> data) {
|
||||
if (data != null && !data.isEmpty()) {
|
||||
datas.clear();
|
||||
datas.addAll(data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向尾部添加数据
|
||||
*/
|
||||
public void addFooterData(List<KLineEntity> data) {
|
||||
if (data != null && !data.isEmpty()) {
|
||||
datas.clear();
|
||||
datas.addAll(0, data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 改变某个点的值
|
||||
*
|
||||
* @param position 索引值
|
||||
*/
|
||||
public void changeItem(int position, KLineEntity data) {
|
||||
datas.set(position, data);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据清除
|
||||
*/
|
||||
public void clearData() {
|
||||
datas.clear();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,466 @@
|
||||
package com.github.fujianlian.klinechart;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.support.annotation.ColorRes;
|
||||
import android.support.annotation.DimenRes;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import com.github.fujianlian.klinechart.draw.KDJDraw;
|
||||
import com.github.fujianlian.klinechart.draw.MACDDraw;
|
||||
import com.github.fujianlian.klinechart.draw.MainDraw;
|
||||
import com.github.fujianlian.klinechart.draw.RSIDraw;
|
||||
import com.github.fujianlian.klinechart.draw.VolumeDraw;
|
||||
import com.github.fujianlian.klinechart.draw.WRDraw;
|
||||
|
||||
/**
|
||||
* k线图
|
||||
* Created by tian on 2016/5/20.
|
||||
*/
|
||||
public class KLineChartView extends BaseKLineChartView {
|
||||
|
||||
ProgressBar mProgressBar;
|
||||
private boolean isRefreshing = false;
|
||||
private boolean isLoadMoreEnd = false;
|
||||
private boolean mLastScrollEnable;
|
||||
private boolean mLastScaleEnable;
|
||||
|
||||
private KChartRefreshListener mRefreshListener;
|
||||
|
||||
private MACDDraw mMACDDraw;
|
||||
private RSIDraw mRSIDraw;
|
||||
private MainDraw mMainDraw;
|
||||
private KDJDraw mKDJDraw;
|
||||
private WRDraw mWRDraw;
|
||||
private VolumeDraw mVolumeDraw;
|
||||
|
||||
|
||||
public KLineChartView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public KLineChartView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public KLineChartView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initView();
|
||||
initAttrs(attrs);
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
mProgressBar = new ProgressBar(getContext());
|
||||
LayoutParams layoutParams = new LayoutParams(dp2px(50), dp2px(50));
|
||||
layoutParams.addRule(CENTER_IN_PARENT);
|
||||
addView(mProgressBar, layoutParams);
|
||||
mProgressBar.setVisibility(GONE);
|
||||
mVolumeDraw = new VolumeDraw(this);
|
||||
mMACDDraw = new MACDDraw(this);
|
||||
mWRDraw = new WRDraw(this);
|
||||
mKDJDraw = new KDJDraw(this);
|
||||
mRSIDraw = new RSIDraw(this);
|
||||
mMainDraw = new MainDraw(this);
|
||||
addChildDraw(mMACDDraw);
|
||||
addChildDraw(mKDJDraw);
|
||||
addChildDraw(mRSIDraw);
|
||||
addChildDraw(mWRDraw);
|
||||
setVolDraw(mVolumeDraw);
|
||||
setMainDraw(mMainDraw);
|
||||
}
|
||||
|
||||
private void initAttrs(AttributeSet attrs) {
|
||||
TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.KLineChartView);
|
||||
if (array != null) {
|
||||
try {
|
||||
//public
|
||||
setPointWidth(array.getDimension(R.styleable.KLineChartView_kc_point_width, getDimension(R.dimen.chart_point_width)));
|
||||
setTextSize(array.getDimension(R.styleable.KLineChartView_kc_text_size, getDimension(R.dimen.chart_text_size)));
|
||||
setTextColor(array.getColor(R.styleable.KLineChartView_kc_text_color, getColor(R.color.chart_text)));
|
||||
setMTextSize(array.getDimension(R.styleable.KLineChartView_kc_text_size, getDimension(R.dimen.chart_text_size)));
|
||||
setMTextColor(array.getColor(R.styleable.KLineChartView_kc_text_color, getColor(R.color.chart_white)));
|
||||
setLineWidth(array.getDimension(R.styleable.KLineChartView_kc_line_width, getDimension(R.dimen.chart_line_width)));
|
||||
setBackgroundColor(array.getColor(R.styleable.KLineChartView_kc_background_color, getColor(R.color.chart_bac)));
|
||||
setSelectPointColor(array.getColor(R.styleable.KLineChartView_kc_background_color, getColor(R.color.chart_point_bac)));
|
||||
|
||||
setSelectedXLineColor(Color.WHITE);
|
||||
setSelectedXLineWidth(getDimension(R.dimen.chart_line_width));
|
||||
|
||||
setSelectedYLineColor(Color.parseColor("#8040424D"));
|
||||
setSelectedYLineWidth(getDimension(R.dimen.chart_point_width));
|
||||
|
||||
setGridLineWidth(array.getDimension(R.styleable.KLineChartView_kc_grid_line_width, getDimension(R.dimen.chart_grid_line_width)));
|
||||
setGridLineColor(array.getColor(R.styleable.KLineChartView_kc_grid_line_color, getColor(R.color.chart_grid_line)));
|
||||
//macd
|
||||
setMACDWidth(array.getDimension(R.styleable.KLineChartView_kc_macd_width, getDimension(R.dimen.chart_candle_width)));
|
||||
setDIFColor(array.getColor(R.styleable.KLineChartView_kc_dif_color, getColor(R.color.chart_ma5)));
|
||||
setDEAColor(array.getColor(R.styleable.KLineChartView_kc_dea_color, getColor(R.color.chart_ma10)));
|
||||
setMACDColor(array.getColor(R.styleable.KLineChartView_kc_macd_color, getColor(R.color.chart_ma30)));
|
||||
//kdj
|
||||
setKColor(array.getColor(R.styleable.KLineChartView_kc_dif_color, getColor(R.color.chart_ma5)));
|
||||
setDColor(array.getColor(R.styleable.KLineChartView_kc_dea_color, getColor(R.color.chart_ma10)));
|
||||
setJColor(array.getColor(R.styleable.KLineChartView_kc_macd_color, getColor(R.color.chart_ma30)));
|
||||
//wr
|
||||
setRColor(array.getColor(R.styleable.KLineChartView_kc_dif_color, getColor(R.color.chart_ma5)));
|
||||
//rsi
|
||||
setRSI1Color(array.getColor(R.styleable.KLineChartView_kc_dif_color, getColor(R.color.chart_ma5)));
|
||||
setRSI2Color(array.getColor(R.styleable.KLineChartView_kc_dea_color, getColor(R.color.chart_ma10)));
|
||||
setRSI3Color(array.getColor(R.styleable.KLineChartView_kc_macd_color, getColor(R.color.chart_ma30)));
|
||||
//main
|
||||
setMa5Color(array.getColor(R.styleable.KLineChartView_kc_dif_color, getColor(R.color.chart_ma5)));
|
||||
setMa10Color(array.getColor(R.styleable.KLineChartView_kc_dea_color, getColor(R.color.chart_ma10)));
|
||||
setMa30Color(array.getColor(R.styleable.KLineChartView_kc_macd_color, getColor(R.color.chart_ma30)));
|
||||
setCandleWidth(array.getDimension(R.styleable.KLineChartView_kc_candle_width, getDimension(R.dimen.chart_candle_width)));
|
||||
setCandleLineWidth(array.getDimension(R.styleable.KLineChartView_kc_candle_line_width, getDimension(R.dimen.chart_candle_line_width)));
|
||||
setSelectorBackgroundColor(array.getColor(R.styleable.KLineChartView_kc_selector_background_color, getColor(R.color.chart_selector)));
|
||||
setSelectorTextSize(array.getDimension(R.styleable.KLineChartView_kc_selector_text_size, getDimension(R.dimen.chart_selector_text_size)));
|
||||
setCandleSolid(array.getBoolean(R.styleable.KLineChartView_kc_candle_solid, true));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
array.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private float getDimension(@DimenRes int resId) {
|
||||
return getResources().getDimension(resId);
|
||||
}
|
||||
|
||||
private int getColor(@ColorRes int resId) {
|
||||
return ContextCompat.getColor(getContext(), resId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLeftSide() {
|
||||
showLoading();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRightSide() {
|
||||
}
|
||||
|
||||
public void showLoading() {
|
||||
if (!isLoadMoreEnd && !isRefreshing) {
|
||||
isRefreshing = true;
|
||||
if (mProgressBar != null) {
|
||||
mProgressBar.setVisibility(View.VISIBLE);
|
||||
}
|
||||
if (mRefreshListener != null) {
|
||||
mRefreshListener.onLoadMoreBegin(this);
|
||||
}
|
||||
mLastScaleEnable = isScaleEnable();
|
||||
mLastScrollEnable = isScrollEnable();
|
||||
super.setScrollEnable(false);
|
||||
super.setScaleEnable(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void justShowLoading() {
|
||||
if (!isRefreshing) {
|
||||
isLongPress = false;
|
||||
isRefreshing = true;
|
||||
if (mProgressBar != null) {
|
||||
mProgressBar.setVisibility(View.VISIBLE);
|
||||
}
|
||||
if (mRefreshListener != null) {
|
||||
mRefreshListener.onLoadMoreBegin(this);
|
||||
}
|
||||
mLastScaleEnable = isScaleEnable();
|
||||
mLastScrollEnable = isScrollEnable();
|
||||
super.setScrollEnable(false);
|
||||
super.setScaleEnable(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void hideLoading() {
|
||||
if (mProgressBar != null) {
|
||||
mProgressBar.setVisibility(View.GONE);
|
||||
}
|
||||
super.setScrollEnable(mLastScrollEnable);
|
||||
super.setScaleEnable(mLastScaleEnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏选择器内容
|
||||
*/
|
||||
public void hideSelectData() {
|
||||
isLongPress = false;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新完成
|
||||
*/
|
||||
public void refreshComplete() {
|
||||
isRefreshing = false;
|
||||
hideLoading();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新完成,没有数据
|
||||
*/
|
||||
public void refreshEnd() {
|
||||
isLoadMoreEnd = true;
|
||||
isRefreshing = false;
|
||||
hideLoading();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置加载更多
|
||||
*/
|
||||
public void resetLoadMoreEnd() {
|
||||
isLoadMoreEnd = false;
|
||||
}
|
||||
|
||||
public void setLoadMoreEnd() {
|
||||
isLoadMoreEnd = true;
|
||||
}
|
||||
|
||||
public interface KChartRefreshListener {
|
||||
/**
|
||||
* 加载更多
|
||||
*
|
||||
* @param chart
|
||||
*/
|
||||
void onLoadMoreBegin(KLineChartView chart);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScaleEnable(boolean scaleEnable) {
|
||||
if (isRefreshing) {
|
||||
throw new IllegalStateException("请勿在刷新状态设置属性");
|
||||
}
|
||||
super.setScaleEnable(scaleEnable);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScrollEnable(boolean scrollEnable) {
|
||||
if (isRefreshing) {
|
||||
throw new IllegalStateException("请勿在刷新状态设置属性");
|
||||
}
|
||||
super.setScrollEnable(scrollEnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置DIF颜色
|
||||
*/
|
||||
public void setDIFColor(int color) {
|
||||
mMACDDraw.setDIFColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置DEA颜色
|
||||
*/
|
||||
public void setDEAColor(int color) {
|
||||
mMACDDraw.setDEAColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置MACD颜色
|
||||
*/
|
||||
public void setMACDColor(int color) {
|
||||
mMACDDraw.setMACDColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置MACD的宽度
|
||||
*
|
||||
* @param MACDWidth
|
||||
*/
|
||||
public void setMACDWidth(float MACDWidth) {
|
||||
mMACDDraw.setMACDWidth(MACDWidth);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置K颜色
|
||||
*/
|
||||
public void setKColor(int color) {
|
||||
mKDJDraw.setKColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置D颜色
|
||||
*/
|
||||
public void setDColor(int color) {
|
||||
mKDJDraw.setDColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置J颜色
|
||||
*/
|
||||
public void setJColor(int color) {
|
||||
mKDJDraw.setJColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置R颜色
|
||||
*/
|
||||
public void setRColor(int color) {
|
||||
mWRDraw.setRColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置ma5颜色
|
||||
*
|
||||
* @param color
|
||||
*/
|
||||
public void setMa5Color(int color) {
|
||||
mMainDraw.setMa5Color(color);
|
||||
mVolumeDraw.setMa5Color(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置ma10颜色
|
||||
*
|
||||
* @param color
|
||||
*/
|
||||
public void setMa10Color(int color) {
|
||||
mMainDraw.setMa10Color(color);
|
||||
mVolumeDraw.setMa10Color(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置ma20颜色
|
||||
*
|
||||
* @param color
|
||||
*/
|
||||
public void setMa30Color(int color) {
|
||||
mMainDraw.setMa30Color(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置选择器文字大小
|
||||
*
|
||||
* @param textSize
|
||||
*/
|
||||
public void setSelectorTextSize(float textSize) {
|
||||
mMainDraw.setSelectorTextSize(textSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置选择器背景
|
||||
*
|
||||
* @param color
|
||||
*/
|
||||
public void setSelectorBackgroundColor(int color) {
|
||||
mMainDraw.setSelectorBackgroundColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置蜡烛宽度
|
||||
*
|
||||
* @param candleWidth
|
||||
*/
|
||||
public void setCandleWidth(float candleWidth) {
|
||||
mMainDraw.setCandleWidth(candleWidth);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置蜡烛线宽度
|
||||
*
|
||||
* @param candleLineWidth
|
||||
*/
|
||||
public void setCandleLineWidth(float candleLineWidth) {
|
||||
mMainDraw.setCandleLineWidth(candleLineWidth);
|
||||
}
|
||||
|
||||
/**
|
||||
* 蜡烛是否空心
|
||||
*/
|
||||
public void setCandleSolid(boolean candleSolid) {
|
||||
mMainDraw.setCandleSolid(candleSolid);
|
||||
}
|
||||
|
||||
public void setRSI1Color(int color) {
|
||||
mRSIDraw.setRSI1Color(color);
|
||||
}
|
||||
|
||||
public void setRSI2Color(int color) {
|
||||
mRSIDraw.setRSI2Color(color);
|
||||
}
|
||||
|
||||
public void setRSI3Color(int color) {
|
||||
mRSIDraw.setRSI3Color(color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTextSize(float textSize) {
|
||||
super.setTextSize(textSize);
|
||||
mMainDraw.setTextSize(textSize);
|
||||
mRSIDraw.setTextSize(textSize);
|
||||
mMACDDraw.setTextSize(textSize);
|
||||
mKDJDraw.setTextSize(textSize);
|
||||
mWRDraw.setTextSize(textSize);
|
||||
mVolumeDraw.setTextSize(textSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLineWidth(float lineWidth) {
|
||||
super.setLineWidth(lineWidth);
|
||||
mMainDraw.setLineWidth(lineWidth);
|
||||
mRSIDraw.setLineWidth(lineWidth);
|
||||
mMACDDraw.setLineWidth(lineWidth);
|
||||
mKDJDraw.setLineWidth(lineWidth);
|
||||
mWRDraw.setLineWidth(lineWidth);
|
||||
mVolumeDraw.setLineWidth(lineWidth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTextColor(int color) {
|
||||
super.setTextColor(color);
|
||||
mMainDraw.setSelectorTextColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置刷新监听
|
||||
*/
|
||||
public void setRefreshListener(KChartRefreshListener refreshListener) {
|
||||
mRefreshListener = refreshListener;
|
||||
}
|
||||
|
||||
public void setMainDrawLine(boolean isLine) {
|
||||
mMainDraw.setLine(isLine);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private int startX;
|
||||
private int startY;
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
switch (ev.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
startX = (int) ev.getX();
|
||||
startY = (int) ev.getY();
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
int dX = (int) (ev.getX() - startX);
|
||||
int dY = (int) (ev.getY() - startX);
|
||||
if (Math.abs(dX) > Math.abs(dY)) {
|
||||
//左右滑动
|
||||
return true;
|
||||
} else {
|
||||
//上下滑动
|
||||
return false;
|
||||
}
|
||||
case MotionEvent.ACTION_UP:
|
||||
break;
|
||||
default:
|
||||
}
|
||||
return super.onInterceptTouchEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLongPress(MotionEvent e) {
|
||||
if (!isRefreshing) {
|
||||
super.onLongPress(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
package com.github.fujianlian.klinechart;
|
||||
|
||||
import com.github.fujianlian.klinechart.entity.IKLine;
|
||||
|
||||
/**
|
||||
* K线实体
|
||||
* Created by tifezh on 2016/5/16.
|
||||
*/
|
||||
public class KLineEntity implements IKLine {
|
||||
|
||||
public String getDate() {
|
||||
return Date;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getOpenPrice() {
|
||||
return Open;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getHighPrice() {
|
||||
return High;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getLowPrice() {
|
||||
return Low;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getClosePrice() {
|
||||
return Close;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMA5Price() {
|
||||
return MA5Price;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMA10Price() {
|
||||
return MA10Price;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMA20Price() {
|
||||
return MA20Price;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMA30Price() {
|
||||
return MA30Price;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMA60Price() {
|
||||
return MA60Price;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDea() {
|
||||
return dea;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDif() {
|
||||
return dif;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMacd() {
|
||||
return macd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getK() {
|
||||
return k;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getD() {
|
||||
return d;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getJ() {
|
||||
return j;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getR() {
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getRsi() {
|
||||
return rsi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getUp() {
|
||||
return up;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMb() {
|
||||
return mb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDn() {
|
||||
return dn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getVolume() {
|
||||
return Volume;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMA5Volume() {
|
||||
return MA5Volume;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMA10Volume() {
|
||||
return MA10Volume;
|
||||
}
|
||||
|
||||
public String Date;
|
||||
public float Open;
|
||||
public float High;
|
||||
public float Low;
|
||||
public float Close;
|
||||
public float Volume;
|
||||
|
||||
public float MA5Price;
|
||||
|
||||
public float MA10Price;
|
||||
|
||||
public float MA20Price;
|
||||
|
||||
public float MA30Price;
|
||||
|
||||
public float MA60Price;
|
||||
|
||||
public float dea;
|
||||
|
||||
public float dif;
|
||||
|
||||
public float macd;
|
||||
|
||||
public float k;
|
||||
|
||||
public float d;
|
||||
|
||||
public float j;
|
||||
|
||||
public float r;
|
||||
|
||||
public float rsi;
|
||||
|
||||
public float up;
|
||||
|
||||
public float mb;
|
||||
|
||||
public float dn;
|
||||
|
||||
public float MA5Volume;
|
||||
|
||||
public float MA10Volume;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
package com.github.fujianlian.klinechart;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v4.view.GestureDetectorCompat;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ScaleGestureDetector;
|
||||
import android.widget.OverScroller;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
/**
|
||||
* 可以滑动和放大的view
|
||||
* Created by tian on 2016/5/3.
|
||||
*/
|
||||
public abstract class ScrollAndScaleView extends RelativeLayout implements
|
||||
GestureDetector.OnGestureListener,
|
||||
ScaleGestureDetector.OnScaleGestureListener {
|
||||
protected int mScrollX = 0;
|
||||
protected GestureDetectorCompat mDetector;
|
||||
protected ScaleGestureDetector mScaleDetector;
|
||||
|
||||
protected boolean isLongPress = false;
|
||||
|
||||
private OverScroller mScroller;
|
||||
|
||||
protected boolean touch = false;
|
||||
|
||||
protected float mScaleX = 1;
|
||||
|
||||
protected float mScaleXMax = 2f;
|
||||
|
||||
protected float mScaleXMin = 0.5f;
|
||||
|
||||
private boolean mMultipleTouch = false;
|
||||
|
||||
private boolean mScrollEnable = true;
|
||||
|
||||
private boolean mScaleEnable = true;
|
||||
|
||||
public ScrollAndScaleView(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public ScrollAndScaleView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public ScrollAndScaleView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
setWillNotDraw(false);
|
||||
mDetector = new GestureDetectorCompat(getContext(), this);
|
||||
mScaleDetector = new ScaleGestureDetector(getContext(), this);
|
||||
mScroller = new OverScroller(getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDown(MotionEvent e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShowPress(MotionEvent e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSingleTapUp(MotionEvent e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
|
||||
if (!isLongPress && !isMultipleTouch()) {
|
||||
scrollBy(Math.round(distanceX), 0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLongPress(MotionEvent e) {
|
||||
isLongPress = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||
if (!isTouch() && isScrollEnable()) {
|
||||
mScroller.fling(mScrollX, 0
|
||||
, Math.round(velocityX / mScaleX), 0,
|
||||
Integer.MIN_VALUE, Integer.MAX_VALUE,
|
||||
0, 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void computeScroll() {
|
||||
if (mScroller.computeScrollOffset()) {
|
||||
if (!isTouch()) {
|
||||
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
|
||||
} else {
|
||||
mScroller.forceFinished(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scrollBy(int x, int y) {
|
||||
scrollTo(mScrollX - Math.round(x / mScaleX), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scrollTo(int x, int y) {
|
||||
if (!isScrollEnable()) {
|
||||
mScroller.forceFinished(true);
|
||||
return;
|
||||
}
|
||||
int oldX = mScrollX;
|
||||
mScrollX = x;
|
||||
if (mScrollX < getMinScrollX()) {
|
||||
mScrollX = getMinScrollX();
|
||||
onRightSide();
|
||||
mScroller.forceFinished(true);
|
||||
} else if (mScrollX > getMaxScrollX()) {
|
||||
mScrollX = getMaxScrollX();
|
||||
onLeftSide();
|
||||
mScroller.forceFinished(true);
|
||||
}
|
||||
onScrollChanged(mScrollX, 0, oldX, 0);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScale(ScaleGestureDetector detector) {
|
||||
if (!isScaleEnable()) {
|
||||
return false;
|
||||
}
|
||||
float oldScale = mScaleX;
|
||||
mScaleX *= detector.getScaleFactor();
|
||||
if (mScaleX < mScaleXMin) {
|
||||
mScaleX = mScaleXMin;
|
||||
} else if (mScaleX > mScaleXMax) {
|
||||
mScaleX = mScaleXMax;
|
||||
} else {
|
||||
onScaleChanged(mScaleX, oldScale);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void onScaleChanged(float scale, float oldScale) {
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScaleBegin(ScaleGestureDetector detector) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScaleEnd(ScaleGestureDetector detector) {
|
||||
|
||||
}
|
||||
|
||||
float x;
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
// 按压手指超过1个
|
||||
if (event.getPointerCount() > 1) {
|
||||
isLongPress = false;
|
||||
}
|
||||
switch (event.getAction() & MotionEvent.ACTION_MASK) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
touch = true;
|
||||
x = event.getX();
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
//长按之后移动
|
||||
if (isLongPress) {
|
||||
onLongPress(event);
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
invalidate();
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
if (x == event.getX()) {
|
||||
if (isLongPress) {
|
||||
isLongPress = false;
|
||||
}
|
||||
}
|
||||
touch = false;
|
||||
invalidate();
|
||||
break;
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
isLongPress = false;
|
||||
touch = false;
|
||||
invalidate();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
mMultipleTouch = event.getPointerCount() > 1;
|
||||
this.mDetector.onTouchEvent(event);
|
||||
this.mScaleDetector.onTouchEvent(event);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 滑到了最左边
|
||||
*/
|
||||
abstract public void onLeftSide();
|
||||
|
||||
/**
|
||||
* 滑到了最右边
|
||||
*/
|
||||
abstract public void onRightSide();
|
||||
|
||||
/**
|
||||
* 是否在触摸中
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isTouch() {
|
||||
return touch;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取位移的最小值
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public abstract int getMinScrollX();
|
||||
|
||||
/**
|
||||
* 获取位移的最大值
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public abstract int getMaxScrollX();
|
||||
|
||||
/**
|
||||
* 设置ScrollX
|
||||
*
|
||||
* @param scrollX
|
||||
*/
|
||||
public void setScrollX(int scrollX) {
|
||||
this.mScrollX = scrollX;
|
||||
scrollTo(scrollX, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是多指触控
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isMultipleTouch() {
|
||||
return mMultipleTouch;
|
||||
}
|
||||
|
||||
protected void checkAndFixScrollX() {
|
||||
if (mScrollX < getMinScrollX()) {
|
||||
mScrollX = getMinScrollX();
|
||||
mScroller.forceFinished(true);
|
||||
} else if (mScrollX > getMaxScrollX()) {
|
||||
mScrollX = getMaxScrollX();
|
||||
mScroller.forceFinished(true);
|
||||
}
|
||||
}
|
||||
|
||||
public float getScaleXMax() {
|
||||
return mScaleXMax;
|
||||
}
|
||||
|
||||
public float getScaleXMin() {
|
||||
return mScaleXMin;
|
||||
}
|
||||
|
||||
public boolean isScrollEnable() {
|
||||
return mScrollEnable;
|
||||
}
|
||||
|
||||
public boolean isScaleEnable() {
|
||||
return mScaleEnable;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置缩放的最大值
|
||||
*/
|
||||
public void setScaleXMax(float scaleXMax) {
|
||||
mScaleXMax = scaleXMax;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置缩放的最小值
|
||||
*/
|
||||
public void setScaleXMin(float scaleXMin) {
|
||||
mScaleXMin = scaleXMin;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否可以滑动
|
||||
*/
|
||||
public void setScrollEnable(boolean scrollEnable) {
|
||||
mScrollEnable = scrollEnable;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否可以缩放
|
||||
*/
|
||||
public void setScaleEnable(boolean scaleEnable) {
|
||||
mScaleEnable = scaleEnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getScaleX() {
|
||||
return mScaleX;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.github.fujianlian.klinechart.base;
|
||||
|
||||
import android.database.DataSetObserver;
|
||||
|
||||
/**
|
||||
* 数据适配器
|
||||
* Created by tifezh on 2016/6/14.
|
||||
*/
|
||||
|
||||
public interface IAdapter {
|
||||
/**
|
||||
* 获取点的数目
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
int getCount();
|
||||
|
||||
/**
|
||||
* 通过序号获取item
|
||||
*
|
||||
* @param position 对应的序号
|
||||
* @return 数据实体
|
||||
*/
|
||||
Object getItem(int position);
|
||||
|
||||
/**
|
||||
* 通过序号获取时间
|
||||
*
|
||||
* @param position
|
||||
* @return
|
||||
*/
|
||||
String getDate(int position);
|
||||
|
||||
/**
|
||||
* 注册一个数据观察者
|
||||
*
|
||||
* @param observer 数据观察者
|
||||
*/
|
||||
void registerDataSetObserver(DataSetObserver observer);
|
||||
|
||||
/**
|
||||
* 移除一个数据观察者
|
||||
*
|
||||
* @param observer 数据观察者
|
||||
*/
|
||||
void unregisterDataSetObserver(DataSetObserver observer);
|
||||
|
||||
/**
|
||||
* 当数据发生变化时调用
|
||||
*/
|
||||
void notifyDataSetChanged();
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.github.fujianlian.klinechart.base;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.github.fujianlian.klinechart.BaseKLineChartView;
|
||||
|
||||
|
||||
/**
|
||||
* 画图的基类 根据实体来画图形
|
||||
* Created by tifezh on 2016/6/14.
|
||||
*/
|
||||
public interface IChartDraw<T> {
|
||||
|
||||
/**
|
||||
* 需要滑动 物体draw方法
|
||||
*
|
||||
* @param canvas canvas
|
||||
* @param view k线图View
|
||||
* @param position 当前点的位置
|
||||
* @param lastPoint 上一个点
|
||||
* @param curPoint 当前点
|
||||
* @param lastX 上一个点的x坐标
|
||||
* @param curX 当前点的X坐标
|
||||
*/
|
||||
void drawTranslated(@Nullable T lastPoint, @NonNull T curPoint, float lastX, float curX, @NonNull Canvas canvas, @NonNull BaseKLineChartView view, int position);
|
||||
|
||||
/**
|
||||
* @param canvas
|
||||
* @param view
|
||||
* @param position 该点的位置
|
||||
* @param x x的起始坐标
|
||||
* @param y y的起始坐标
|
||||
*/
|
||||
void drawText(@NonNull Canvas canvas, @NonNull BaseKLineChartView view, int position, float x, float y);
|
||||
|
||||
/**
|
||||
* 获取当前实体中最大的值
|
||||
*
|
||||
* @param point
|
||||
* @return
|
||||
*/
|
||||
float getMaxValue(T point);
|
||||
|
||||
/**
|
||||
* 获取当前实体中最小的值
|
||||
*
|
||||
* @param point
|
||||
* @return
|
||||
*/
|
||||
float getMinValue(T point);
|
||||
|
||||
/**
|
||||
* 获取value格式化器
|
||||
*/
|
||||
IValueFormatter getValueFormatter();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.github.fujianlian.klinechart.base;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 时间格式化接口
|
||||
* Created by tifezh on 2016/6/21.
|
||||
*/
|
||||
|
||||
public interface IDateTimeFormatter {
|
||||
String format(Date date);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.github.fujianlian.klinechart.base;
|
||||
|
||||
/**
|
||||
* Value格式化接口
|
||||
* Created by tifezh on 2016/6/21.
|
||||
*/
|
||||
|
||||
public interface IValueFormatter {
|
||||
/**
|
||||
* 格式化value
|
||||
*
|
||||
* @param value 传入的value值
|
||||
* @return 返回字符串
|
||||
*/
|
||||
String format(float value);
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package com.github.fujianlian.klinechart.draw;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.github.fujianlian.klinechart.BaseKLineChartView;
|
||||
import com.github.fujianlian.klinechart.base.IChartDraw;
|
||||
import com.github.fujianlian.klinechart.base.IValueFormatter;
|
||||
import com.github.fujianlian.klinechart.entity.IKDJ;
|
||||
import com.github.fujianlian.klinechart.formatter.ValueFormatter;
|
||||
|
||||
/**
|
||||
* KDJ实现类
|
||||
* Created by tifezh on 2016/6/19.
|
||||
*/
|
||||
|
||||
public class KDJDraw implements IChartDraw<IKDJ> {
|
||||
|
||||
private Paint mKPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private Paint mDPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private Paint mJPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
|
||||
public KDJDraw(BaseKLineChartView view) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawTranslated(@Nullable IKDJ lastPoint, @NonNull IKDJ curPoint, float lastX, float curX, @NonNull Canvas canvas, @NonNull BaseKLineChartView view, int position) {
|
||||
if (lastPoint.getK() != 0) {
|
||||
view.drawChildLine(canvas, mKPaint, lastX, lastPoint.getK(), curX, curPoint.getK());
|
||||
}
|
||||
if (lastPoint.getD() != 0) {
|
||||
view.drawChildLine(canvas, mDPaint, lastX, lastPoint.getD(), curX, curPoint.getD());
|
||||
}
|
||||
if (lastPoint.getJ() != 0) {
|
||||
view.drawChildLine(canvas, mJPaint, lastX, lastPoint.getJ(), curX, curPoint.getJ());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawText(@NonNull Canvas canvas, @NonNull BaseKLineChartView view, int position, float x, float y) {
|
||||
IKDJ point = (IKDJ) view.getItem(position);
|
||||
if (point.getK() != 0) {
|
||||
String text = "KDJ(14,1,3) ";
|
||||
canvas.drawText(text, x, y, view.getTextPaint());
|
||||
x += view.getTextPaint().measureText(text);
|
||||
text = "K:" + view.formatValue(point.getK()) + " ";
|
||||
canvas.drawText(text, x, y, mKPaint);
|
||||
x += mKPaint.measureText(text);
|
||||
if (point.getD() != 0) {
|
||||
text = "D:" + view.formatValue(point.getD()) + " ";
|
||||
canvas.drawText(text, x, y, mDPaint);
|
||||
x += mDPaint.measureText(text);
|
||||
text = "J:" + view.formatValue(point.getJ()) + " ";
|
||||
canvas.drawText(text, x, y, mJPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMaxValue(IKDJ point) {
|
||||
return Math.max(point.getK(), Math.max(point.getD(), point.getJ()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMinValue(IKDJ point) {
|
||||
return Math.min(point.getK(), Math.min(point.getD(), point.getJ()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public IValueFormatter getValueFormatter() {
|
||||
return new ValueFormatter();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置K颜色
|
||||
*/
|
||||
public void setKColor(int color) {
|
||||
mKPaint.setColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置D颜色
|
||||
*/
|
||||
public void setDColor(int color) {
|
||||
mDPaint.setColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置J颜色
|
||||
*/
|
||||
public void setJColor(int color) {
|
||||
mJPaint.setColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置曲线宽度
|
||||
*/
|
||||
public void setLineWidth(float width) {
|
||||
mKPaint.setStrokeWidth(width);
|
||||
mDPaint.setStrokeWidth(width);
|
||||
mJPaint.setStrokeWidth(width);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置文字大小
|
||||
*/
|
||||
public void setTextSize(float textSize) {
|
||||
mKPaint.setTextSize(textSize);
|
||||
mDPaint.setTextSize(textSize);
|
||||
mJPaint.setTextSize(textSize);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.github.fujianlian.klinechart.draw;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
|
||||
import com.github.fujianlian.klinechart.R;
|
||||
import com.github.fujianlian.klinechart.BaseKLineChartView;
|
||||
import com.github.fujianlian.klinechart.base.IChartDraw;
|
||||
import com.github.fujianlian.klinechart.base.IValueFormatter;
|
||||
import com.github.fujianlian.klinechart.entity.IMACD;
|
||||
import com.github.fujianlian.klinechart.formatter.ValueFormatter;
|
||||
|
||||
/**
|
||||
* macd实现类
|
||||
* Created by tifezh on 2016/6/19.
|
||||
*/
|
||||
|
||||
public class MACDDraw implements IChartDraw<IMACD> {
|
||||
|
||||
private Paint mRedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private Paint mGreenPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private Paint mDIFPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private Paint mDEAPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private Paint mMACDPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
/**
|
||||
* macd 中柱子的宽度
|
||||
*/
|
||||
private float mMACDWidth = 0;
|
||||
|
||||
public MACDDraw(BaseKLineChartView view) {
|
||||
Context context = view.getContext();
|
||||
mRedPaint.setColor(ContextCompat.getColor(context, R.color.chart_red));
|
||||
mGreenPaint.setColor(ContextCompat.getColor(context, R.color.chart_green));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawTranslated(@Nullable IMACD lastPoint, @NonNull IMACD curPoint, float lastX, float curX, @NonNull Canvas canvas, @NonNull BaseKLineChartView view, int position) {
|
||||
drawMACD(canvas, view, curX, curPoint.getMacd());
|
||||
view.drawChildLine(canvas, mDIFPaint, lastX, lastPoint.getDea(), curX, curPoint.getDea());
|
||||
view.drawChildLine(canvas, mDEAPaint, lastX, lastPoint.getDif(), curX, curPoint.getDif());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawText(@NonNull Canvas canvas, @NonNull BaseKLineChartView view, int position, float x, float y) {
|
||||
IMACD point = (IMACD) view.getItem(position);
|
||||
String text = "MACD(12,26,9) ";
|
||||
canvas.drawText(text, x, y, view.getTextPaint());
|
||||
x += view.getTextPaint().measureText(text);
|
||||
text = "MACD:" + view.formatValue(point.getMacd()) + " ";
|
||||
canvas.drawText(text, x, y, mMACDPaint);
|
||||
x += mMACDPaint.measureText(text);
|
||||
text = "DIF:" + view.formatValue(point.getDif()) + " ";
|
||||
canvas.drawText(text, x, y, mDEAPaint);
|
||||
x += mDIFPaint.measureText(text);
|
||||
text = "DEA:" + view.formatValue(point.getDea());
|
||||
canvas.drawText(text, x, y, mDIFPaint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMaxValue(IMACD point) {
|
||||
return Math.max(point.getMacd(), Math.max(point.getDea(), point.getDif()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMinValue(IMACD point) {
|
||||
return Math.min(point.getMacd(), Math.min(point.getDea(), point.getDif()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public IValueFormatter getValueFormatter() {
|
||||
return new ValueFormatter();
|
||||
}
|
||||
|
||||
/**
|
||||
* 画macd
|
||||
*
|
||||
* @param canvas
|
||||
* @param x
|
||||
* @param macd
|
||||
*/
|
||||
private void drawMACD(Canvas canvas, BaseKLineChartView view, float x, float macd) {
|
||||
float macdy = view.getChildY(macd);
|
||||
float r = mMACDWidth / 2;
|
||||
float zeroy = view.getChildY(0);
|
||||
if (macd > 0) {
|
||||
// left top right bottom
|
||||
canvas.drawRect(x - r, macdy, x + r, zeroy, mRedPaint);
|
||||
} else {
|
||||
canvas.drawRect(x - r, zeroy, x + r, macdy, mGreenPaint);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置DIF颜色
|
||||
*/
|
||||
public void setDIFColor(int color) {
|
||||
this.mDIFPaint.setColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置DEA颜色
|
||||
*/
|
||||
public void setDEAColor(int color) {
|
||||
this.mDEAPaint.setColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置MACD颜色
|
||||
*/
|
||||
public void setMACDColor(int color) {
|
||||
this.mMACDPaint.setColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置MACD的宽度
|
||||
*
|
||||
* @param MACDWidth
|
||||
*/
|
||||
public void setMACDWidth(float MACDWidth) {
|
||||
mMACDWidth = MACDWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置曲线宽度
|
||||
*/
|
||||
public void setLineWidth(float width) {
|
||||
mDEAPaint.setStrokeWidth(width);
|
||||
mDIFPaint.setStrokeWidth(width);
|
||||
mMACDPaint.setStrokeWidth(width);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置文字大小
|
||||
*/
|
||||
public void setTextSize(float textSize) {
|
||||
mDEAPaint.setTextSize(textSize);
|
||||
mDIFPaint.setTextSize(textSize);
|
||||
mMACDPaint.setTextSize(textSize);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,410 @@
|
||||
package com.github.fujianlian.klinechart.draw;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
|
||||
import com.github.fujianlian.klinechart.R;
|
||||
import com.github.fujianlian.klinechart.BaseKLineChartView;
|
||||
import com.github.fujianlian.klinechart.KLineChartView;
|
||||
import com.github.fujianlian.klinechart.base.IChartDraw;
|
||||
import com.github.fujianlian.klinechart.base.IValueFormatter;
|
||||
import com.github.fujianlian.klinechart.entity.ICandle;
|
||||
import com.github.fujianlian.klinechart.entity.IKLine;
|
||||
import com.github.fujianlian.klinechart.formatter.ValueFormatter;
|
||||
import com.github.fujianlian.klinechart.utils.ViewUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 主图的实现类
|
||||
* Created by tifezh on 2016/6/14.
|
||||
*/
|
||||
public class MainDraw implements IChartDraw<ICandle> {
|
||||
|
||||
private float mCandleWidth = 0;
|
||||
private float mCandleLineWidth = 0;
|
||||
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private Paint mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private Paint mRedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private Paint mGreenPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private Paint ma5Paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private Paint ma10Paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private Paint ma30Paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
|
||||
private Paint mSelectorTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private Paint mSelectorBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private Context mContext;
|
||||
|
||||
private boolean mCandleSolid = true;
|
||||
// 是否分时
|
||||
private boolean isLine = false;
|
||||
private Status status = Status.MA;
|
||||
private KLineChartView kChartView;
|
||||
|
||||
public MainDraw(BaseKLineChartView view) {
|
||||
Context context = view.getContext();
|
||||
kChartView = (KLineChartView) view;
|
||||
mContext = context;
|
||||
mRedPaint.setColor(ContextCompat.getColor(context, R.color.chart_red));
|
||||
mGreenPaint.setColor(ContextCompat.getColor(context, R.color.chart_green));
|
||||
mLinePaint.setColor(ContextCompat.getColor(context, R.color.chart_line));
|
||||
paint.setColor(ContextCompat.getColor(context, R.color.chart_line_background));
|
||||
}
|
||||
|
||||
public void setStatus(Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawTranslated(@Nullable ICandle lastPoint, @NonNull ICandle curPoint, float lastX, float curX, @NonNull Canvas canvas, @NonNull BaseKLineChartView view, int position) {
|
||||
if (isLine) {
|
||||
view.drawMainLine(canvas, mLinePaint, lastX, lastPoint.getClosePrice(), curX, curPoint.getClosePrice());
|
||||
view.drawMainMinuteLine(canvas, paint, lastX, lastPoint.getClosePrice(), curX, curPoint.getClosePrice());
|
||||
if (status == Status.MA) {
|
||||
//画ma60
|
||||
if (lastPoint.getMA60Price() != 0) {
|
||||
view.drawMainLine(canvas, ma10Paint, lastX, lastPoint.getMA60Price(), curX, curPoint.getMA60Price());
|
||||
}
|
||||
} else if (status == Status.BOLL) {
|
||||
//画boll
|
||||
if (lastPoint.getMb() != 0) {
|
||||
view.drawMainLine(canvas, ma10Paint, lastX, lastPoint.getMb(), curX, curPoint.getMb());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
drawCandle(view, canvas, curX, curPoint.getHighPrice(), curPoint.getLowPrice(), curPoint.getOpenPrice(), curPoint.getClosePrice());
|
||||
if (status == Status.MA) {
|
||||
//画ma5
|
||||
if (lastPoint.getMA5Price() != 0) {
|
||||
view.drawMainLine(canvas, ma5Paint, lastX, lastPoint.getMA5Price(), curX, curPoint.getMA5Price());
|
||||
}
|
||||
//画ma10
|
||||
if (lastPoint.getMA10Price() != 0) {
|
||||
view.drawMainLine(canvas, ma10Paint, lastX, lastPoint.getMA10Price(), curX, curPoint.getMA10Price());
|
||||
}
|
||||
//画ma30
|
||||
if (lastPoint.getMA30Price() != 0) {
|
||||
view.drawMainLine(canvas, ma30Paint, lastX, lastPoint.getMA30Price(), curX, curPoint.getMA30Price());
|
||||
}
|
||||
} else if (status == Status.BOLL) {
|
||||
//画boll
|
||||
if (lastPoint.getUp() != 0) {
|
||||
view.drawMainLine(canvas, ma5Paint, lastX, lastPoint.getUp(), curX, curPoint.getUp());
|
||||
}
|
||||
if (lastPoint.getMb() != 0) {
|
||||
view.drawMainLine(canvas, ma10Paint, lastX, lastPoint.getMb(), curX, curPoint.getMb());
|
||||
}
|
||||
if (lastPoint.getDn() != 0) {
|
||||
view.drawMainLine(canvas, ma30Paint, lastX, lastPoint.getDn(), curX, curPoint.getDn());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawText(@NonNull Canvas canvas, @NonNull BaseKLineChartView view, int position, float x, float y) {
|
||||
ICandle point = (IKLine) view.getItem(position);
|
||||
y = y - 5;
|
||||
if (isLine) {
|
||||
if (status == Status.MA) {
|
||||
if (point.getMA60Price() != 0) {
|
||||
String text = "MA60:" + view.formatValue(point.getMA60Price()) + " ";
|
||||
canvas.drawText(text, x, y, ma10Paint);
|
||||
}
|
||||
} else if (status == Status.BOLL) {
|
||||
if (point.getMb() != 0) {
|
||||
String text = "BOLL:" + view.formatValue(point.getMb()) + " ";
|
||||
canvas.drawText(text, x, y, ma10Paint);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (status == Status.MA) {
|
||||
String text;
|
||||
if (point.getMA5Price() != 0) {
|
||||
text = "MA5:" + view.formatValue(point.getMA5Price()) + " ";
|
||||
canvas.drawText(text, x, y, ma5Paint);
|
||||
x += ma5Paint.measureText(text);
|
||||
}
|
||||
if (point.getMA10Price() != 0) {
|
||||
text = "MA10:" + view.formatValue(point.getMA10Price()) + " ";
|
||||
canvas.drawText(text, x, y, ma10Paint);
|
||||
x += ma10Paint.measureText(text);
|
||||
}
|
||||
if (point.getMA20Price() != 0) {
|
||||
text = "MA30:" + view.formatValue(point.getMA30Price());
|
||||
canvas.drawText(text, x, y, ma30Paint);
|
||||
}
|
||||
} else if (status == Status.BOLL) {
|
||||
if (point.getMb() != 0) {
|
||||
String text = "BOLL:" + view.formatValue(point.getMb()) + " ";
|
||||
canvas.drawText(text, x, y, ma10Paint);
|
||||
x += ma5Paint.measureText(text);
|
||||
text = "UB:" + view.formatValue(point.getUp()) + " ";
|
||||
canvas.drawText(text, x, y, ma5Paint);
|
||||
x += ma10Paint.measureText(text);
|
||||
text = "LB:" + view.formatValue(point.getDn());
|
||||
canvas.drawText(text, x, y, ma30Paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (view.isLongPress()) {
|
||||
drawSelector(view, canvas);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMaxValue(ICandle point) {
|
||||
if (status == Status.BOLL) {
|
||||
if (Float.isNaN(point.getUp())) {
|
||||
if (point.getMb() == 0) {
|
||||
return point.getHighPrice();
|
||||
} else {
|
||||
return point.getMb();
|
||||
}
|
||||
} else if (point.getUp() == 0) {
|
||||
return point.getHighPrice();
|
||||
} else {
|
||||
return point.getUp();
|
||||
}
|
||||
} else {
|
||||
return Math.max(point.getHighPrice(), point.getMA30Price());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMinValue(ICandle point) {
|
||||
if (status == Status.BOLL) {
|
||||
if (point.getDn() == 0) {
|
||||
return point.getLowPrice();
|
||||
} else {
|
||||
return point.getDn();
|
||||
}
|
||||
} else {
|
||||
if (point.getMA30Price() == 0f) {
|
||||
return point.getLowPrice();
|
||||
} else {
|
||||
return Math.min(point.getMA30Price(), point.getLowPrice());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IValueFormatter getValueFormatter() {
|
||||
return new ValueFormatter();
|
||||
}
|
||||
|
||||
/**
|
||||
* 画Candle
|
||||
*
|
||||
* @param canvas
|
||||
* @param x x轴坐标
|
||||
* @param high 最高价
|
||||
* @param low 最低价
|
||||
* @param open 开盘价
|
||||
* @param close 收盘价
|
||||
*/
|
||||
private void drawCandle(BaseKLineChartView view, Canvas canvas, float x, float high, float low, float open, float close) {
|
||||
high = view.getMainY(high);
|
||||
low = view.getMainY(low);
|
||||
open = view.getMainY(open);
|
||||
close = view.getMainY(close);
|
||||
float r = mCandleWidth / 2;
|
||||
float lineR = mCandleLineWidth / 2;
|
||||
if (open > close) {
|
||||
//实心
|
||||
if (mCandleSolid) {
|
||||
canvas.drawRect(x - r, close, x + r, open, mRedPaint);
|
||||
canvas.drawRect(x - lineR, high, x + lineR, low, mRedPaint);
|
||||
} else {
|
||||
mRedPaint.setStrokeWidth(mCandleLineWidth);
|
||||
canvas.drawLine(x, high, x, close, mRedPaint);
|
||||
canvas.drawLine(x, open, x, low, mRedPaint);
|
||||
canvas.drawLine(x - r + lineR, open, x - r + lineR, close, mRedPaint);
|
||||
canvas.drawLine(x + r - lineR, open, x + r - lineR, close, mRedPaint);
|
||||
mRedPaint.setStrokeWidth(mCandleLineWidth * view.getScaleX());
|
||||
canvas.drawLine(x - r, open, x + r, open, mRedPaint);
|
||||
canvas.drawLine(x - r, close, x + r, close, mRedPaint);
|
||||
}
|
||||
|
||||
} else if (open < close) {
|
||||
canvas.drawRect(x - r, open, x + r, close, mGreenPaint);
|
||||
canvas.drawRect(x - lineR, high, x + lineR, low, mGreenPaint);
|
||||
} else {
|
||||
canvas.drawRect(x - r, open, x + r, close + 1, mRedPaint);
|
||||
canvas.drawRect(x - lineR, high, x + lineR, low, mRedPaint);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* draw选择器
|
||||
*
|
||||
* @param view
|
||||
* @param canvas
|
||||
*/
|
||||
private void drawSelector(BaseKLineChartView view, Canvas canvas) {
|
||||
Paint.FontMetrics metrics = mSelectorTextPaint.getFontMetrics();
|
||||
float textHeight = metrics.descent - metrics.ascent;
|
||||
|
||||
int index = view.getSelectedIndex();
|
||||
float padding = ViewUtil.Dp2Px(mContext, 5);
|
||||
float margin = ViewUtil.Dp2Px(mContext, 5);
|
||||
float width = 0;
|
||||
float left;
|
||||
float top = margin + view.getTopPadding();
|
||||
float height = padding * 8 + textHeight * 5;
|
||||
|
||||
ICandle point = (ICandle) view.getItem(index);
|
||||
List<String> strings = new ArrayList<>();
|
||||
strings.add(view.getAdapter().getDate(index));
|
||||
strings.add("高:" + point.getHighPrice());
|
||||
strings.add("低:" + point.getLowPrice());
|
||||
strings.add("开:" + point.getOpenPrice());
|
||||
strings.add("收:" + point.getClosePrice());
|
||||
|
||||
for (String s : strings) {
|
||||
width = Math.max(width, mSelectorTextPaint.measureText(s));
|
||||
}
|
||||
width += padding * 2;
|
||||
|
||||
float x = view.translateXtoX(view.getX(index));
|
||||
if (x > view.getChartWidth() / 2) {
|
||||
left = margin;
|
||||
} else {
|
||||
left = view.getChartWidth() - width - margin;
|
||||
}
|
||||
|
||||
RectF r = new RectF(left, top, left + width, top + height);
|
||||
canvas.drawRoundRect(r, padding, padding, mSelectorBackgroundPaint);
|
||||
float y = top + padding * 2 + (textHeight - metrics.bottom - metrics.top) / 2;
|
||||
|
||||
for (String s : strings) {
|
||||
canvas.drawText(s, left + padding, y, mSelectorTextPaint);
|
||||
y += textHeight + padding;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置蜡烛宽度
|
||||
*
|
||||
* @param candleWidth
|
||||
*/
|
||||
public void setCandleWidth(float candleWidth) {
|
||||
mCandleWidth = candleWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置蜡烛线宽度
|
||||
*
|
||||
* @param candleLineWidth
|
||||
*/
|
||||
public void setCandleLineWidth(float candleLineWidth) {
|
||||
mCandleLineWidth = candleLineWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置ma5颜色
|
||||
*
|
||||
* @param color
|
||||
*/
|
||||
public void setMa5Color(int color) {
|
||||
this.ma5Paint.setColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置ma10颜色
|
||||
*
|
||||
* @param color
|
||||
*/
|
||||
public void setMa10Color(int color) {
|
||||
this.ma10Paint.setColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置ma30颜色
|
||||
*
|
||||
* @param color
|
||||
*/
|
||||
public void setMa30Color(int color) {
|
||||
this.ma30Paint.setColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置选择器文字颜色
|
||||
*
|
||||
* @param color
|
||||
*/
|
||||
public void setSelectorTextColor(int color) {
|
||||
mSelectorTextPaint.setColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置选择器文字大小
|
||||
*
|
||||
* @param textSize
|
||||
*/
|
||||
public void setSelectorTextSize(float textSize) {
|
||||
mSelectorTextPaint.setTextSize(textSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置选择器背景
|
||||
*
|
||||
* @param color
|
||||
*/
|
||||
public void setSelectorBackgroundColor(int color) {
|
||||
mSelectorBackgroundPaint.setColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置曲线宽度
|
||||
*/
|
||||
public void setLineWidth(float width) {
|
||||
ma30Paint.setStrokeWidth(width);
|
||||
ma10Paint.setStrokeWidth(width);
|
||||
ma5Paint.setStrokeWidth(width);
|
||||
mLinePaint.setStrokeWidth(width);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置文字大小
|
||||
*/
|
||||
public void setTextSize(float textSize) {
|
||||
ma30Paint.setTextSize(textSize);
|
||||
ma10Paint.setTextSize(textSize);
|
||||
ma5Paint.setTextSize(textSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* 蜡烛是否实心
|
||||
*/
|
||||
public void setCandleSolid(boolean candleSolid) {
|
||||
mCandleSolid = candleSolid;
|
||||
}
|
||||
|
||||
public void setLine(boolean line) {
|
||||
if (isLine != line) {
|
||||
isLine = line;
|
||||
if (isLine) {
|
||||
kChartView.setCandleWidth(kChartView.dp2px(7f));
|
||||
} else {
|
||||
kChartView.setCandleWidth(kChartView.dp2px(6f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isLine() {
|
||||
return isLine;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.github.fujianlian.klinechart.draw;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.github.fujianlian.klinechart.BaseKLineChartView;
|
||||
import com.github.fujianlian.klinechart.base.IChartDraw;
|
||||
import com.github.fujianlian.klinechart.base.IValueFormatter;
|
||||
import com.github.fujianlian.klinechart.entity.IRSI;
|
||||
import com.github.fujianlian.klinechart.formatter.ValueFormatter;
|
||||
|
||||
/**
|
||||
* RSI实现类
|
||||
* Created by tifezh on 2016/6/19.
|
||||
*/
|
||||
public class RSIDraw implements IChartDraw<IRSI> {
|
||||
|
||||
private Paint mRSI1Paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private Paint mRSI2Paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private Paint mRSI3Paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
|
||||
public RSIDraw(BaseKLineChartView view) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawTranslated(@Nullable IRSI lastPoint, @NonNull IRSI curPoint, float lastX, float curX, @NonNull Canvas canvas, @NonNull BaseKLineChartView view, int position) {
|
||||
if (lastPoint.getRsi() != 0) {
|
||||
view.drawChildLine(canvas, mRSI1Paint, lastX, lastPoint.getRsi(), curX, curPoint.getRsi());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawText(@NonNull Canvas canvas, @NonNull BaseKLineChartView view, int position, float x, float y) {
|
||||
IRSI point = (IRSI) view.getItem(position);
|
||||
if (point.getRsi() != 0) {
|
||||
String text = "RSI(14) ";
|
||||
canvas.drawText(text, x, y, view.getTextPaint());
|
||||
x += view.getTextPaint().measureText(text);
|
||||
text = view.formatValue(point.getRsi());
|
||||
canvas.drawText(text, x, y, mRSI1Paint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMaxValue(IRSI point) {
|
||||
return point.getRsi();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMinValue(IRSI point) {
|
||||
return point.getRsi();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IValueFormatter getValueFormatter() {
|
||||
return new ValueFormatter();
|
||||
}
|
||||
|
||||
public void setRSI1Color(int color) {
|
||||
mRSI1Paint.setColor(color);
|
||||
}
|
||||
|
||||
public void setRSI2Color(int color) {
|
||||
mRSI2Paint.setColor(color);
|
||||
}
|
||||
|
||||
public void setRSI3Color(int color) {
|
||||
mRSI3Paint.setColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置曲线宽度
|
||||
*/
|
||||
public void setLineWidth(float width) {
|
||||
mRSI1Paint.setStrokeWidth(width);
|
||||
mRSI2Paint.setStrokeWidth(width);
|
||||
mRSI3Paint.setStrokeWidth(width);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置文字大小
|
||||
*/
|
||||
public void setTextSize(float textSize) {
|
||||
mRSI2Paint.setTextSize(textSize);
|
||||
mRSI3Paint.setTextSize(textSize);
|
||||
mRSI1Paint.setTextSize(textSize);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.github.fujianlian.klinechart.draw;
|
||||
|
||||
/**
|
||||
* @author fujianlian Created on 2018/8/20 11:09
|
||||
* @descripe MainDraw当前子视图
|
||||
*/
|
||||
public enum Status {
|
||||
MA, BOLL, NONE
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.github.fujianlian.klinechart.draw;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
|
||||
import com.github.fujianlian.klinechart.R;
|
||||
import com.github.fujianlian.klinechart.BaseKLineChartView;
|
||||
import com.github.fujianlian.klinechart.base.IChartDraw;
|
||||
import com.github.fujianlian.klinechart.base.IValueFormatter;
|
||||
import com.github.fujianlian.klinechart.entity.IVolume;
|
||||
import com.github.fujianlian.klinechart.formatter.BigValueFormatter;
|
||||
import com.github.fujianlian.klinechart.utils.ViewUtil;
|
||||
|
||||
/**
|
||||
* Created by hjm on 2017/11/14 17:49.
|
||||
*/
|
||||
public class VolumeDraw implements IChartDraw<IVolume> {
|
||||
|
||||
private Paint mRedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private Paint mGreenPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private Paint ma5Paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private Paint ma10Paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private int pillarWidth = 0;
|
||||
|
||||
public VolumeDraw(BaseKLineChartView view) {
|
||||
Context context = view.getContext();
|
||||
mRedPaint.setColor(ContextCompat.getColor(context, R.color.chart_red));
|
||||
mGreenPaint.setColor(ContextCompat.getColor(context, R.color.chart_green));
|
||||
pillarWidth = ViewUtil.Dp2Px(context, 4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawTranslated(
|
||||
@Nullable IVolume lastPoint, @NonNull IVolume curPoint, float lastX, float curX,
|
||||
@NonNull Canvas canvas, @NonNull BaseKLineChartView view, int position) {
|
||||
|
||||
drawHistogram(canvas, curPoint, lastPoint, curX, view, position);
|
||||
if (lastPoint.getMA5Volume() != 0f) {
|
||||
view.drawVolLine(canvas, ma5Paint, lastX, lastPoint.getMA5Volume(), curX, curPoint.getMA5Volume());
|
||||
}
|
||||
if (lastPoint.getMA10Volume() != 0f) {
|
||||
view.drawVolLine(canvas, ma10Paint, lastX, lastPoint.getMA10Volume(), curX, curPoint.getMA10Volume());
|
||||
}
|
||||
}
|
||||
|
||||
private void drawHistogram(
|
||||
Canvas canvas, IVolume curPoint, IVolume lastPoint, float curX,
|
||||
BaseKLineChartView view, int position) {
|
||||
|
||||
float r = pillarWidth / 2;
|
||||
float top = view.getVolY(curPoint.getVolume());
|
||||
int bottom = view.getVolRect().bottom;
|
||||
if (curPoint.getClosePrice() >= curPoint.getOpenPrice()) {//涨
|
||||
canvas.drawRect(curX - r, top, curX + r, bottom, mRedPaint);
|
||||
} else {
|
||||
canvas.drawRect(curX - r, top, curX + r, bottom, mGreenPaint);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawText(
|
||||
@NonNull Canvas canvas, @NonNull BaseKLineChartView view, int position, float x, float y) {
|
||||
IVolume point = (IVolume) view.getItem(position);
|
||||
String text = "VOL:" + getValueFormatter().format(point.getVolume()) + " ";
|
||||
canvas.drawText(text, x, y, view.getTextPaint());
|
||||
x += view.getTextPaint().measureText(text);
|
||||
text = "MA5:" + getValueFormatter().format(point.getMA5Volume()) + " ";
|
||||
canvas.drawText(text, x, y, ma5Paint);
|
||||
x += ma5Paint.measureText(text);
|
||||
text = "MA10:" + getValueFormatter().format(point.getMA10Volume());
|
||||
canvas.drawText(text, x, y, ma10Paint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMaxValue(IVolume point) {
|
||||
return Math.max(point.getVolume(), Math.max(point.getMA5Volume(), point.getMA10Volume()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMinValue(IVolume point) {
|
||||
return Math.min(point.getVolume(), Math.min(point.getMA5Volume(), point.getMA10Volume()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public IValueFormatter getValueFormatter() {
|
||||
return new BigValueFormatter();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 MA5 线的颜色
|
||||
*
|
||||
* @param color
|
||||
*/
|
||||
public void setMa5Color(int color) {
|
||||
this.ma5Paint.setColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 MA10 线的颜色
|
||||
*
|
||||
* @param color
|
||||
*/
|
||||
public void setMa10Color(int color) {
|
||||
this.ma10Paint.setColor(color);
|
||||
}
|
||||
|
||||
public void setLineWidth(float width) {
|
||||
this.ma5Paint.setStrokeWidth(width);
|
||||
this.ma10Paint.setStrokeWidth(width);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置文字大小
|
||||
*
|
||||
* @param textSize
|
||||
*/
|
||||
public void setTextSize(float textSize) {
|
||||
this.ma5Paint.setTextSize(textSize);
|
||||
this.ma10Paint.setTextSize(textSize);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.github.fujianlian.klinechart.draw;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.github.fujianlian.klinechart.BaseKLineChartView;
|
||||
import com.github.fujianlian.klinechart.base.IChartDraw;
|
||||
import com.github.fujianlian.klinechart.base.IValueFormatter;
|
||||
import com.github.fujianlian.klinechart.entity.IWR;
|
||||
import com.github.fujianlian.klinechart.formatter.ValueFormatter;
|
||||
|
||||
/**
|
||||
* KDJ实现类
|
||||
* Created by tifezh on 2016/6/19.
|
||||
*/
|
||||
public class WRDraw implements IChartDraw<IWR> {
|
||||
|
||||
private Paint mRPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
|
||||
public WRDraw(BaseKLineChartView view) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawTranslated(@Nullable IWR lastPoint, @NonNull IWR curPoint, float lastX, float curX, @NonNull Canvas canvas, @NonNull BaseKLineChartView view, int position) {
|
||||
if (lastPoint.getR() != -10) {
|
||||
view.drawChildLine(canvas, mRPaint, lastX, lastPoint.getR(), curX, curPoint.getR());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawText(@NonNull Canvas canvas, @NonNull BaseKLineChartView view, int position, float x, float y) {
|
||||
IWR point = (IWR) view.getItem(position);
|
||||
if (point.getR() != -10) {
|
||||
String text = "WR(14):";
|
||||
canvas.drawText(text, x, y, view.getTextPaint());
|
||||
x += view.getTextPaint().measureText(text);
|
||||
text = view.formatValue(point.getR()) + " ";
|
||||
canvas.drawText(text, x, y, mRPaint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMaxValue(IWR point) {
|
||||
return point.getR();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMinValue(IWR point) {
|
||||
return point.getR();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IValueFormatter getValueFormatter() {
|
||||
return new ValueFormatter();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置%R颜色
|
||||
*/
|
||||
public void setRColor(int color) {
|
||||
mRPaint.setColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置曲线宽度
|
||||
*/
|
||||
public void setLineWidth(float width) {
|
||||
mRPaint.setStrokeWidth(width);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置文字大小
|
||||
*/
|
||||
public void setTextSize(float textSize) {
|
||||
mRPaint.setTextSize(textSize);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.github.fujianlian.klinechart.entity;
|
||||
|
||||
/**
|
||||
* 蜡烛图实体接口
|
||||
* Created by tifezh on 2016/6/9.
|
||||
*/
|
||||
public interface ICandle {
|
||||
|
||||
/**
|
||||
* 开盘价
|
||||
*/
|
||||
float getOpenPrice();
|
||||
|
||||
/**
|
||||
* 最高价
|
||||
*/
|
||||
float getHighPrice();
|
||||
|
||||
/**
|
||||
* 最低价
|
||||
*/
|
||||
float getLowPrice();
|
||||
|
||||
/**
|
||||
* 收盘价
|
||||
*/
|
||||
float getClosePrice();
|
||||
|
||||
|
||||
// 以下为MA数据
|
||||
/**
|
||||
* 五(月,日,时,分,5分等)均价
|
||||
*/
|
||||
float getMA5Price();
|
||||
|
||||
/**
|
||||
* 十(月,日,时,分,5分等)均价
|
||||
*/
|
||||
float getMA10Price();
|
||||
|
||||
/**
|
||||
* 二十(月,日,时,分,5分等)均价
|
||||
*/
|
||||
float getMA20Price();
|
||||
|
||||
/**
|
||||
* 三十(月,日,时,分,5分等)均价
|
||||
*/
|
||||
float getMA30Price();
|
||||
|
||||
/**
|
||||
* 六十(月,日,时,分,5分等)均价
|
||||
*/
|
||||
float getMA60Price();
|
||||
|
||||
// 以下为BOLL数据
|
||||
/**
|
||||
* 上轨线
|
||||
*/
|
||||
float getUp();
|
||||
|
||||
/**
|
||||
* 中轨线
|
||||
*/
|
||||
float getMb();
|
||||
|
||||
/**
|
||||
* 下轨线
|
||||
*/
|
||||
float getDn();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.github.fujianlian.klinechart.entity;
|
||||
|
||||
/**
|
||||
* @author fujianlian Created on 2018/8/20 16:08
|
||||
* @descripe KDJ指标(随机指标)接口
|
||||
* 相关说明:https://baike.baidu.com/item/KDJ%E6%8C%87%E6%A0%87/6328421?fr=aladdin&fromid=3423560&fromtitle=kdj
|
||||
*/
|
||||
public interface IKDJ {
|
||||
|
||||
/**
|
||||
* K值
|
||||
*/
|
||||
float getK();
|
||||
|
||||
/**
|
||||
* D值
|
||||
*/
|
||||
float getD();
|
||||
|
||||
/**
|
||||
* J值
|
||||
*/
|
||||
float getJ();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.github.fujianlian.klinechart.entity;
|
||||
|
||||
/**
|
||||
* KDJ指标(随机指标)接口
|
||||
* Created by tifezh on 2016/6/9.
|
||||
*/
|
||||
public interface IKLine extends ICandle, IMACD, IKDJ, IRSI, IVolume, IWR {
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.github.fujianlian.klinechart.entity;
|
||||
|
||||
/**
|
||||
* MACD指标(指数平滑移动平均线)接口
|
||||
* @see <a href="https://baike.baidu.com/item/MACD指标"/>相关说明</a>
|
||||
* Created by tifezh on 2016/6/10.
|
||||
*/
|
||||
|
||||
public interface IMACD {
|
||||
|
||||
|
||||
/**
|
||||
* DEA值
|
||||
*/
|
||||
float getDea();
|
||||
|
||||
/**
|
||||
* DIF值
|
||||
*/
|
||||
float getDif();
|
||||
|
||||
/**
|
||||
* MACD值
|
||||
*/
|
||||
float getMacd();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.github.fujianlian.klinechart.entity;
|
||||
|
||||
/**
|
||||
* RSI指标接口
|
||||
* @see <a href="https://baike.baidu.com/item/RSI%E6%8C%87%E6%A0%87"/>相关说明</a>
|
||||
* Created by tifezh on 2016/6/10.
|
||||
*/
|
||||
|
||||
public interface IRSI {
|
||||
|
||||
/**
|
||||
* RSI值
|
||||
*/
|
||||
float getRsi();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.github.fujianlian.klinechart.entity;
|
||||
|
||||
/**
|
||||
* 成交量接口
|
||||
* Created by hjm on 2017/11/14 17:46.
|
||||
*/
|
||||
|
||||
public interface IVolume {
|
||||
|
||||
/**
|
||||
* 开盘价
|
||||
*/
|
||||
float getOpenPrice();
|
||||
|
||||
/**
|
||||
* 收盘价
|
||||
*/
|
||||
float getClosePrice();
|
||||
|
||||
/**
|
||||
* 成交量
|
||||
*/
|
||||
float getVolume();
|
||||
|
||||
/**
|
||||
* 五(月,日,时,分,5分等)均量
|
||||
*/
|
||||
float getMA5Volume();
|
||||
|
||||
/**
|
||||
* 十(月,日,时,分,5分等)均量
|
||||
*/
|
||||
float getMA10Volume();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.github.fujianlian.klinechart.entity;
|
||||
|
||||
/**
|
||||
* @author fujianlian Created on 2018/8/20 15:16
|
||||
* @descripe WR指标(随机指标)接口
|
||||
*/
|
||||
public interface IWR {
|
||||
|
||||
/**
|
||||
* %R值
|
||||
*/
|
||||
float getR();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.github.fujianlian.klinechart.formatter;
|
||||
|
||||
import com.github.fujianlian.klinechart.base.IValueFormatter;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* 对较大数据进行格式化
|
||||
* Created by tifezh on 2017/12/13.
|
||||
*/
|
||||
|
||||
public class BigValueFormatter implements IValueFormatter{
|
||||
|
||||
//必须是排好序的
|
||||
private int[] values={10000,1000000,100000000};
|
||||
private String[] units={"万","百万","亿"};
|
||||
|
||||
@Override
|
||||
public String format(float value) {
|
||||
String unit="";
|
||||
int i=values.length-1;
|
||||
while (i>=0)
|
||||
{
|
||||
if(value>values[i]) {
|
||||
value /= values[i];
|
||||
unit = units[i];
|
||||
break;
|
||||
}
|
||||
i--;
|
||||
}
|
||||
return String.format(Locale.getDefault(),"%.2f", value)+unit;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.github.fujianlian.klinechart.formatter;
|
||||
|
||||
import com.github.fujianlian.klinechart.base.IDateTimeFormatter;
|
||||
import com.github.fujianlian.klinechart.utils.DateUtil;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 时间格式化器
|
||||
* Created by tifezh on 2016/6/21.
|
||||
*/
|
||||
|
||||
public class DateFormatter implements IDateTimeFormatter {
|
||||
@Override
|
||||
public String format(Date date) {
|
||||
if (date != null) {
|
||||
return DateUtil.DateFormat.format(date);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.github.fujianlian.klinechart.formatter;
|
||||
|
||||
import com.github.fujianlian.klinechart.base.IDateTimeFormatter;
|
||||
import com.github.fujianlian.klinechart.utils.DateUtil;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 时间格式化器
|
||||
* Created by tifezh on 2016/6/21.
|
||||
*/
|
||||
|
||||
public class TimeFormatter implements IDateTimeFormatter {
|
||||
@Override
|
||||
public String format(Date date) {
|
||||
if (date == null) {
|
||||
return "";
|
||||
}
|
||||
return DateUtil.shortTimeFormat.format(date);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.github.fujianlian.klinechart.formatter;
|
||||
|
||||
import com.github.fujianlian.klinechart.base.IValueFormatter;
|
||||
|
||||
/**
|
||||
* Value格式化类
|
||||
* Created by tifezh on 2016/6/21.
|
||||
*/
|
||||
|
||||
public class ValueFormatter implements IValueFormatter {
|
||||
@Override
|
||||
public String format(float value) {
|
||||
return String.format("%.2f", value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.github.fujianlian.klinechart.utils;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
/**
|
||||
* 时间工具类
|
||||
* Created by tifezh on 2016/4/27.
|
||||
*/
|
||||
public class DateUtil {
|
||||
public static SimpleDateFormat longTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
||||
public static SimpleDateFormat shortTimeFormat = new SimpleDateFormat("HH:mm");
|
||||
public static SimpleDateFormat DateFormat = new SimpleDateFormat("yyyy/MM/dd");
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.github.fujianlian.klinechart.utils;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* Created by tian on 2016/4/11.
|
||||
*/
|
||||
public class ViewUtil {
|
||||
static public int Dp2Px(Context context, float dp) {
|
||||
final float scale = context.getResources().getDisplayMetrics().density;
|
||||
return (int) (dp * scale + 0.5f);
|
||||
}
|
||||
|
||||
static public int Px2Dp(Context context, float px) {
|
||||
final float scale = context.getResources().getDisplayMetrics().density;
|
||||
return (int) (px / scale + 0.5f);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<declare-styleable name="KLineChartView">
|
||||
<!--公共的-->
|
||||
<attr name="kc_text_size" format="dimension"/>
|
||||
<attr name="kc_text_color" format="color"/>
|
||||
<attr name="kc_line_width" format="dimension"/>
|
||||
<attr name="kc_background_color" format="color"/>
|
||||
<attr name="kc_selected_line_color" format="color"/>
|
||||
<attr name="kc_selected_line_width" format="dimension"/>
|
||||
<attr name="kc_grid_line_width" format="dimension"/>
|
||||
<attr name="kc_grid_line_color" format="color"/>
|
||||
<attr name="kc_point_width" format="dimension"/>
|
||||
<!--macd-->
|
||||
<attr name="kc_macd_width" format="dimension"/>
|
||||
<attr name="kc_dif_color" format="color"/>
|
||||
<attr name="kc_dea_color" format="color"/>
|
||||
<attr name="kc_macd_color" format="color"/>
|
||||
<!--kdj-->
|
||||
<attr name="kc_k_color" format="color"/>
|
||||
<attr name="kc_d_color" format="color"/>
|
||||
<attr name="kc_j_color" format="color"/>
|
||||
<!--rsi-->
|
||||
<attr name="kc_rsi1_color" format="color"/>
|
||||
<attr name="kc_rsi2_color" format="color"/>
|
||||
<attr name="kc_ris3_color" format="color"/>
|
||||
<!--boll-->
|
||||
<attr name="kc_up_color" format="color"/>
|
||||
<attr name="kc_mb_color" format="color"/>
|
||||
<attr name="kc_dn_color" format="color"/>
|
||||
<!--main-->
|
||||
<attr name="kc_ma5_color" format="color"/>
|
||||
<attr name="kc_ma10_color" format="color"/>
|
||||
<attr name="kc_ma20_color" format="color"/>
|
||||
<attr name="kc_candle_width" format="dimension"/>
|
||||
<attr name="kc_candle_line_width" format="dimension"/>
|
||||
<attr name="kc_selector_background_color" format="color"/>
|
||||
<attr name="kc_selector_text_size" format="dimension"/>
|
||||
<attr name="kc_candle_solid" format="boolean"/>
|
||||
<!--tab-->
|
||||
<attr name="kc_tab_background_color" format="color"/>
|
||||
<attr name="kc_tab_text_color" format="color"/>
|
||||
<attr name="kc_tab_indicator_color" format="color"/>
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#3F51B5</color>
|
||||
<color name="colorPrimaryDark">#303F9F</color>
|
||||
<color name="colorAccent">#FF4081</color>
|
||||
|
||||
<color name="chart_red">#26BF66</color>
|
||||
<color name="chart_green">#FD6433</color>
|
||||
<color name="chart_line">#C9933E</color>
|
||||
<color name="chart_line_background">#1aC9933E</color>
|
||||
|
||||
<color name="chart_ma5">#DA8AE5</color>
|
||||
<color name="chart_ma10">#39B0E8</color>
|
||||
<color name="chart_ma30">#FFC76D</color>
|
||||
<color name="chart_white">#ffffff</color>
|
||||
|
||||
<color name="chart_background">#202326</color>
|
||||
<color name="chart_bac">#00FFFFFF</color>
|
||||
<color name="chart_point_bac">#202326</color>
|
||||
<color name="chart_grid_line">#1AFFFFFF</color>
|
||||
<color name="chart_text">#818596</color>
|
||||
<color name="chart_selector">#202326</color>
|
||||
<color name="chart_tab_background">#30343C</color>
|
||||
<color name="chart_tab_indicator">#FF6601</color>
|
||||
</resources>
|
||||
@@ -0,0 +1,13 @@
|
||||
<resources>
|
||||
<dimen name="chart_text_size">10sp</dimen>
|
||||
<dimen name="chart_selector_text_size">12sp</dimen>
|
||||
<dimen name="chart_candle_line_width">1dp</dimen>
|
||||
<dimen name="chart_candle_width">6dp</dimen>
|
||||
<dimen name="chart_line_width">1dp</dimen>
|
||||
<dimen name="chart_grid_line_width">0.8dp</dimen>
|
||||
<dimen name="chart_0.5dp">0.5dp</dimen>
|
||||
<dimen name="chart_point_width">7dp</dimen>
|
||||
<dimen name="chart_top_padding">15dp</dimen>
|
||||
<dimen name="chart_bottom_padding">15dp</dimen>
|
||||
<dimen name="child_top_padding">10dp</dimen>
|
||||
</resources>
|
||||
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">KLineChartLib</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.github.fujianlian.klinechart;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
# KLineChart(不再维护)
|
||||
|
||||
Android仿火币K线图实现(包含MA,BOLL,MACD,KDJ,RSI,WR指标)
|
||||
|
||||
本项目是在 [tifezh的KChartView](https://github.com/tifezh/KChartView) 基础上进行修改的,新增了KDJ、WR指标、最大值和最小值展示,对UI展示进行了修改。
|
||||
|
||||
建议使用之前先查看[常见问题](./problem.md)
|
||||
|
||||
## 项目运行效果
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## app下载
|
||||
|
||||

|
||||
|
||||
## 配置使用
|
||||
|
||||
```xml
|
||||
<com.github.fujianlian.klinechart.KLineChartView
|
||||
android:id="@+id/kLineChartView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
```
|
||||
|
||||
主图和附图初始化
|
||||
```java
|
||||
// KLineChartView
|
||||
private void initView() {
|
||||
...
|
||||
// 依次添加副图子视图
|
||||
addChildDraw(mMACDDraw);
|
||||
addChildDraw(mKDJDraw);
|
||||
addChildDraw(mRSIDraw);
|
||||
addChildDraw(mWRDraw);
|
||||
// 设置成交量视图
|
||||
setVolDraw(mVolumeDraw);
|
||||
// 设置主视图
|
||||
setMainDraw(mMainDraw);
|
||||
}
|
||||
```
|
||||
BaseKLineChartView
|
||||
```java
|
||||
// 主图显示隐藏调用
|
||||
public void changeMainDrawType(Status status) {
|
||||
if (mainDraw != null && mainDraw.getStatus() != status) {
|
||||
mainDraw.setStatus(status);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
// 主视图当前子视图
|
||||
public enum Status {
|
||||
MA, BOLL, NONE
|
||||
}
|
||||
|
||||
// 设置子视图,position依据初始化添加先后顺序下标
|
||||
public void setChildDraw(int position) {
|
||||
if (mChildDrawPosition != position) {
|
||||
if (!isShowChild) {
|
||||
isShowChild = true;
|
||||
initRect();
|
||||
}
|
||||
mChildDraw = mChildDraws.get(position);
|
||||
mChildDrawPosition = position;
|
||||
isWR = position == 5;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
// 子视图隐藏
|
||||
public void hideChildDraw() {
|
||||
mChildDrawPosition = -1;
|
||||
isShowChild = false;
|
||||
mChildDraw = null;
|
||||
initRect();
|
||||
invalidate();
|
||||
}
|
||||
```
|
||||
|
||||
KLineChartView
|
||||
```java
|
||||
// 是否显示为分时图
|
||||
public void setMainDrawLine(boolean isLine) {
|
||||
mMainDraw.setLine(isLine);
|
||||
}
|
||||
```
|
||||
|
||||
## 自定义颜色
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#3F51B5</color>
|
||||
<color name="colorPrimaryDark">#303F9F</color>
|
||||
<color name="colorAccent">#FF4081</color>
|
||||
|
||||
<color name="chart_red">#26BF66</color>
|
||||
<color name="chart_green">#FD6433</color>
|
||||
<color name="chart_line">#C9933E</color>
|
||||
<color name="chart_line_background">#1aC9933E</color>
|
||||
|
||||
<color name="chart_ma5">#DA8AE5</color>
|
||||
<color name="chart_ma10">#39B0E8</color>
|
||||
<color name="chart_ma30">#FFC76D</color>
|
||||
<color name="chart_white">#ffffff</color>
|
||||
|
||||
<color name="chart_background">#202326</color>
|
||||
<color name="chart_bac">#00FFFFFF</color>
|
||||
<color name="chart_point_bac">#202326</color>
|
||||
<color name="chart_grid_line">#1AFFFFFF</color>
|
||||
<color name="chart_text">#818596</color>
|
||||
<color name="chart_selector">#202326</color>
|
||||
<color name="chart_tab_background">#30343C</color>
|
||||
<color name="chart_tab_indicator">#FF6601</color>
|
||||
</resources>
|
||||
```
|
||||
布局时修改KLineChartView的相关颜色即可
|
||||
|
||||
|
||||
|
||||
## 具体使用参照KLineChartDemo
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.3.20'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.2.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx1536m
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
android.enableAapt2=true
|
||||
@@ -0,0 +1,6 @@
|
||||
#Fri Jul 26 21:09:55 CST 2024
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
@@ -0,0 +1,84 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 815 KiB |
|
After Width: | Height: | Size: 12 KiB |
@@ -0,0 +1,56 @@
|
||||
## 展示5分钟、10分钟等分时图
|
||||
|
||||
https://github.com/fujianlian/KLineChart/issues/5
|
||||
|
||||
|
||||
## 在NestedScrollView或ScrollView左右滑动冲突问题
|
||||
|
||||
```kotlin
|
||||
// layout为与kChartView引起滑动冲突的view
|
||||
kChartView.setOnTouchListener { _, event ->
|
||||
if (event.pointerCount == 2) {
|
||||
layout.requestDisallowInterceptTouchEvent(true)
|
||||
} else {
|
||||
layout.requestDisallowInterceptTouchEvent(false)
|
||||
}
|
||||
false
|
||||
}
|
||||
```
|
||||
|
||||
或者参考以下
|
||||
|
||||
https://github.com/fujianlian/KLineChart/issues/13
|
||||
|
||||
由于滑动冲突的情况比较多,若无法解决,请自查相关滑动冲突解决文章
|
||||
|
||||
|
||||
## 向尾部添加数据时,尾部MA线形成一条竖线?
|
||||
|
||||
https://github.com/fujianlian/KLineChart/issues/12
|
||||
|
||||
|
||||
## 关于新增一条数据addHeaderData后指标线不见了
|
||||
|
||||
https://github.com/fujianlian/KLineChart/issues/1
|
||||
|
||||
|
||||
## 当数值过小时,macd图 只显示一条横线
|
||||
|
||||
https://github.com/fujianlian/KLineChart/issues/6
|
||||
|
||||
|
||||
## 单击显示详情
|
||||
|
||||
https://github.com/fujianlian/KLineChart/issues/10
|
||||
|
||||
https://github.com/fujianlian/KLineChart/issues/19
|
||||
|
||||
|
||||
## 数据很少从左边开始显示
|
||||
|
||||
https://github.com/fujianlian/KLineChart/issues/9
|
||||
|
||||
|
||||
## 时间轴挤在一起了
|
||||
|
||||
https://github.com/fujianlian/KLineChart/issues/14
|
||||
@@ -0,0 +1 @@
|
||||
include ':KLineChartDemo', ':KLineChartLib'
|
||||