├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── drawable-xxhdpi
│ │ │ │ ├── ic_back.png
│ │ │ │ ├── ic_close.png
│ │ │ │ ├── ic_eye_on.png
│ │ │ │ ├── ic_land.png
│ │ │ │ ├── ic_eye_off.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── kline_water_logo.png
│ │ │ │ ├── ic_k_line_tab_more.png
│ │ │ │ └── ic_k_line_tab_setting.png
│ │ │ ├── drawable
│ │ │ │ ├── shape_corners_radius2.xml
│ │ │ │ ├── selector_tab_text.xml
│ │ │ │ ├── selector_stock_setting_eye.xml
│ │ │ │ └── shape_pop_bg.xml
│ │ │ ├── values
│ │ │ │ ├── colors.xml
│ │ │ │ ├── styles.xml
│ │ │ │ └── strings.xml
│ │ │ └── layout
│ │ │ │ ├── item_tab.xml
│ │ │ │ ├── item_tab_k_line_setting.xml
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── item_tab_k_line_more.xml
│ │ │ │ ├── popup_setting.xml
│ │ │ │ ├── activity_k_line.xml
│ │ │ │ └── activity_k_line_land.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── github
│ │ │ │ └── wyang
│ │ │ │ └── klinechartdemo
│ │ │ │ ├── global
│ │ │ │ └── App.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ ├── utils
│ │ │ │ └── AssetUtil.java
│ │ │ │ ├── widget
│ │ │ │ ├── ObservableScrollView.java
│ │ │ │ ├── PopupController.java
│ │ │ │ └── CommonPopupWindow.java
│ │ │ │ ├── KLineChartLandActivity.java
│ │ │ │ └── KLineChartActivity.java
│ │ ├── AndroidManifest.xml
│ │ └── assets
│ │ │ ├── 石头杂诗
│ │ │ ├── little.json
│ │ │ └── little2.json
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── github
│ │ │ └── wyang
│ │ │ └── klinechartdemo
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── github
│ │ └── wyang
│ │ └── klinechartdemo
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── klinechartlib
├── .gitignore
├── src
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── github
│ │ │ │ └── wyang
│ │ │ │ └── klinechartlib
│ │ │ │ ├── data
│ │ │ │ ├── IMacd.java
│ │ │ │ ├── IVolume.java
│ │ │ │ ├── ISar.java
│ │ │ │ └── ICandle.java
│ │ │ │ ├── huobi
│ │ │ │ ├── interfaces
│ │ │ │ │ ├── IData.java
│ │ │ │ │ ├── IDataLineSetProvider.java
│ │ │ │ │ ├── KLineConstant.java
│ │ │ │ │ ├── IDataLineSet.java
│ │ │ │ │ └── IKLineChartAdapter.java
│ │ │ │ ├── data
│ │ │ │ │ ├── Volume.java
│ │ │ │ │ ├── Macd.java
│ │ │ │ │ ├── Candle.java
│ │ │ │ │ ├── KLineEntity.java
│ │ │ │ │ ├── DataLineSet.java
│ │ │ │ │ └── DataLineSetProvider.java
│ │ │ │ ├── draw
│ │ │ │ │ ├── IChartDraw.java
│ │ │ │ │ ├── ChartDraw.java
│ │ │ │ │ ├── ChildDraw.java
│ │ │ │ │ └── MainDraw.java
│ │ │ │ ├── KLineChartAdapter.java
│ │ │ │ └── helper
│ │ │ │ │ ├── TextDrawHelper.java
│ │ │ │ │ └── LinePathHelper.java
│ │ │ │ ├── base
│ │ │ │ ├── IValueFormatter.java
│ │ │ │ ├── IDateFormatter.java
│ │ │ │ ├── ChartAdapter.java
│ │ │ │ └── BaseChartView.java
│ │ │ │ ├── formatter
│ │ │ │ ├── PercentValueFormatter.java
│ │ │ │ ├── PriceFormatter.java
│ │ │ │ ├── VolumeFormatter.java
│ │ │ │ └── DateFormatter.java
│ │ │ │ └── utils
│ │ │ │ ├── IntegerPool.java
│ │ │ │ ├── PointFPool.java
│ │ │ │ └── ObjectPool.java
│ │ └── res
│ │ │ └── values
│ │ │ ├── strings.xml
│ │ │ ├── dimens.xml
│ │ │ ├── colors.xml
│ │ │ └── attrs.xml
│ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── github
│ │ │ └── wyang
│ │ │ └── klinechartlib
│ │ │ └── ExampleInstrumentedTest.java
│ └── test
│ │ └── java
│ │ └── com
│ │ └── github
│ │ └── wyang
│ │ └── klinechartlib
│ │ └── ObjectPoolTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties
├── gradlew.bat
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/klinechartlib/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':klinechartlib'
2 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/weiyang-6539/KLineChart-master/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/weiyang-6539/KLineChart-master/HEAD/app/src/main/res/drawable-xxhdpi/ic_back.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/weiyang-6539/KLineChart-master/HEAD/app/src/main/res/drawable-xxhdpi/ic_close.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_eye_on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/weiyang-6539/KLineChart-master/HEAD/app/src/main/res/drawable-xxhdpi/ic_eye_on.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_land.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/weiyang-6539/KLineChart-master/HEAD/app/src/main/res/drawable-xxhdpi/ic_land.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_eye_off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/weiyang-6539/KLineChart-master/HEAD/app/src/main/res/drawable-xxhdpi/ic_eye_off.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/weiyang-6539/KLineChart-master/HEAD/app/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/klinechartlib/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/kline_water_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/weiyang-6539/KLineChart-master/HEAD/app/src/main/res/drawable-xxhdpi/kline_water_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_k_line_tab_more.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/weiyang-6539/KLineChart-master/HEAD/app/src/main/res/drawable-xxhdpi/ic_k_line_tab_more.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_k_line_tab_setting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/weiyang-6539/KLineChart-master/HEAD/app/src/main/res/drawable-xxhdpi/ic_k_line_tab_setting.png
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/data/IMacd.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.data;
2 |
3 | /**
4 | * Created by fxb on 2019-12-25.
5 | */
6 | public interface IMacd {
7 | float getMacd();
8 | }
9 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/data/IVolume.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.data;
2 |
3 | /**
4 | * Created by fxb on 2019-12-25.
5 | * 成交量对应图形:矩形
6 | */
7 | public interface IVolume {
8 | float getVolume();
9 | }
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_corners_radius2.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Mar 12 10:53:56 CST 2025
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/huobi/interfaces/IData.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.huobi.interfaces;
2 |
3 | /**
4 | * Created by fxb on 2019-12-25.
5 | */
6 | public interface IData {
7 |
8 | float getMax();
9 |
10 | float getMin();
11 | }
12 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/base/IValueFormatter.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.base;
2 |
3 | /**
4 | * Created by fxb on 2019-11-08.
5 | * 数值格式化接口,价格精确 涨跌幅计算 成交量转换
6 | */
7 | public interface IValueFormatter {
8 | String format(float value);
9 | }
10 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/data/ISar.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.data;
2 |
3 | /**
4 | * Created by fxb on 2019-12-25.
5 | * SAR指标对应图:空心圆(算法复杂,后续拓展)
6 | */
7 | public interface ISar {
8 | float getSar();
9 |
10 | boolean isIncrease();
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/selector_tab_text.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/selector_stock_setting_eye.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_pop_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/base/IDateFormatter.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.base;
2 |
3 | /**
4 | * Created by fxb on 2019-11-06.
5 | * 时间转换器接口,参数毫秒值
6 | * 数据时间不推荐使用字符串(类似 yyyy-MM-dd HH:mm:ss.SSS)
7 | * 分时线 K线(分钟,日K,周K,月K)格式化后的String不相同
8 | */
9 | public interface IDateFormatter {
10 | String format(long time);
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 | #FF2E7DF6
8 | #AA2E7DF6
9 | #999999
10 |
11 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/data/ICandle.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.data;
2 |
3 | /**
4 | * Created by fxb on 2019-11-11.
5 | * 蜡烛图对应图形:上影线 + 矩形 + 下影线
6 | */
7 | public interface ICandle {
8 |
9 | float getOpen();//开盘价
10 |
11 | float getHigh();//最高价
12 |
13 | float getLow();//最低价
14 |
15 | float getClose();//收盘价
16 |
17 | long getTime();//时间
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/wyang/klinechartdemo/global/App.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartdemo.global;
2 |
3 | import android.app.Application;
4 |
5 | import com.zhouyou.http.EasyHttp;
6 |
7 | /**
8 | * Created by fxb on 2019-12-13.
9 | */
10 | public class App extends Application {
11 |
12 | @Override
13 | public void onCreate() {
14 | super.onCreate();
15 |
16 | EasyHttp.init(this);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/test/java/com/github/wyang/klinechartdemo/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartdemo;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_tab.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | KLineChartLib
3 |
4 | 时间
5 | 开
6 | 高
7 | 低
8 | 收
9 | 涨跌额
10 | 涨跌幅
11 | 成交量
12 |
13 |
14 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/huobi/interfaces/IDataLineSetProvider.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.huobi.interfaces;
2 |
3 | import com.github.wyang.klinechartlib.huobi.data.KLineEntity;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * Created by fxb on 2019-12-29.
9 | */
10 | public interface IDataLineSetProvider {
11 |
12 | IDataLineSet get(String name);
13 |
14 | void calculateAll(List data);
15 |
16 | void calculateLast(List data, boolean replace);
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/wyang/klinechartdemo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartdemo;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.view.View;
7 |
8 | public class MainActivity extends AppCompatActivity {
9 |
10 | @Override
11 | protected void onCreate(Bundle savedInstanceState) {
12 | super.onCreate(savedInstanceState);
13 | setContentView(R.layout.activity_main);
14 |
15 | }
16 |
17 | public void start(View view) {
18 | startActivity(new Intent(this, KLineChartActivity.class));
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/formatter/PercentValueFormatter.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.formatter;
2 |
3 |
4 | import com.github.wyang.klinechartlib.base.IValueFormatter;
5 |
6 | import java.text.DecimalFormat;
7 |
8 | /**
9 | * Created by *** on 2019-08-24.
10 | */
11 | public class PercentValueFormatter implements IValueFormatter {
12 | private DecimalFormat format = new DecimalFormat("0.00%");
13 |
14 | @Override
15 | public String format(float value) {
16 | String format = this.format.format(value);
17 | return format.startsWith("-") ? format : "+" + format;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/huobi/data/Volume.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.huobi.data;
2 |
3 | import com.github.wyang.klinechartlib.data.IVolume;
4 | import com.github.wyang.klinechartlib.huobi.interfaces.IData;
5 |
6 | /**
7 | * Created by fxb on 2019-12-25.
8 | */
9 | public class Volume implements IVolume, IData {
10 | public float volume;
11 |
12 | @Override
13 | public float getVolume() {
14 | return volume;
15 | }
16 |
17 | @Override
18 | public float getMax() {
19 | return volume;
20 | }
21 |
22 | @Override
23 | public float getMin() {
24 | return 0;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/utils/IntegerPool.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.utils;
2 |
3 | /**
4 | * Created by fxb on 2019-11-29.
5 | */
6 | public class IntegerPool extends ObjectPool {
7 | private static int i;
8 | private static IntegerPool pool;
9 |
10 | static {
11 | pool = new IntegerPool(32, 16);
12 | }
13 |
14 | public static IntegerPool getPool() {
15 | return pool;
16 | }
17 |
18 | private IntegerPool(int initialCapacity, int expansionCapacity) {
19 | super(initialCapacity, expansionCapacity);
20 | }
21 |
22 | @Override
23 | public Integer createObject() {
24 | return i++;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/huobi/data/Macd.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.huobi.data;
2 |
3 | import com.github.wyang.klinechartlib.data.IMacd;
4 | import com.github.wyang.klinechartlib.huobi.interfaces.IData;
5 |
6 | /**
7 | * Created by fxb on 2019-12-25.
8 | */
9 | public class Macd implements IMacd, IData {
10 | public float macd;
11 |
12 | public Macd(float macd) {
13 | this.macd = macd;
14 | }
15 |
16 | @Override
17 | public float getMacd() {
18 | return macd;
19 | }
20 |
21 | @Override
22 | public float getMax() {
23 | return macd;
24 | }
25 |
26 | @Override
27 | public float getMin() {
28 | return macd;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 |
15 |
16 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/formatter/PriceFormatter.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.formatter;
2 |
3 | import android.support.annotation.NonNull;
4 |
5 | import com.github.wyang.klinechartlib.base.IValueFormatter;
6 |
7 | import java.text.DecimalFormat;
8 |
9 | /**
10 | * Created by fxb on 2019-11-08.
11 | */
12 | public class PriceFormatter implements IValueFormatter {
13 | private DecimalFormat format;
14 |
15 | public PriceFormatter() {
16 | format = new DecimalFormat("0.00");
17 | }
18 |
19 | public PriceFormatter(@NonNull DecimalFormat format) {
20 | this.format = format;
21 | }
22 |
23 | @Override
24 | public String format(float value) {
25 | return format.format(value);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/formatter/VolumeFormatter.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.formatter;
2 |
3 | import android.support.annotation.NonNull;
4 |
5 | import com.github.wyang.klinechartlib.base.IValueFormatter;
6 |
7 | import java.text.DecimalFormat;
8 |
9 | /**
10 | * Created by fxb on 2019-11-25.
11 | */
12 | public class VolumeFormatter implements IValueFormatter {
13 | private DecimalFormat format;
14 |
15 | public VolumeFormatter() {
16 | format = new DecimalFormat("0");
17 | }
18 |
19 | public VolumeFormatter(@NonNull DecimalFormat format) {
20 | this.format = format;
21 | }
22 |
23 | @Override
24 | public String format(float value) {
25 | return format.format(value);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_tab_k_line_setting.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
16 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 10sp
4 | 11sp
5 | 1dp
6 | 6dp
7 | 8dp
8 | 1dp
9 | 0.5dp
10 | 0.5dp
11 | 15dp
12 | 17dp
13 | 10dp
14 | 0.8dp
15 | 2.5dp
16 | 3dp
17 |
--------------------------------------------------------------------------------
/klinechartlib/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/huobi/interfaces/KLineConstant.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.huobi.interfaces;
2 |
3 | /**
4 | * Created by fxb on 2019-12-27.
5 | */
6 | public interface KLineConstant {
7 | //最多有6条均线,对应也就6个色号
8 | Integer[] COLORS = new Integer[]{0xfff2dc9c, 0xff7ecfc0, 0xffc197f7, 0xffec4d46, 0xff89ce40, 0xff6437f5};
9 |
10 | /**
11 | * 数组中存在 Integer.MAX_VALUE 时,表示当前指标线不可用
12 | */
13 | int[] MA_N = new int[]{5, 20, 30, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE};
14 | int[] EMA_N = new int[]{12, 26};
15 | int[] BOLL_N = new int[]{20, 2};
16 |
17 | int[] VOL_N = new int[]{5, 10};
18 | int[] MACD_N = new int[]{12, 26, 9};
19 | int[] KDJ_N = new int[]{14, 1, 3};
20 | int[] RSI_N = new int[]{14, 12, 6};
21 | int[] WR_N = new int[]{14, Integer.MAX_VALUE, Integer.MAX_VALUE};
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/github/wyang/klinechartdemo/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartdemo;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.github.wyang.klinechartdemo", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/klinechartlib/src/androidTest/java/com/github/wyang/klinechartlib/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.github.wyang.klinechartlib.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/utils/PointFPool.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.utils;
2 |
3 | import android.graphics.PointF;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * Created by fxb on 2019-11-29.
9 | */
10 | public class PointFPool {
11 | private static ObjectPool pool;
12 |
13 | static {
14 | pool = new ObjectPool(4, 2) {
15 | @Override
16 | public PointF createObject() {
17 | return new PointF();
18 | }
19 | };
20 | }
21 |
22 | public static PointF get(float x, float y) {
23 | PointF pointF = pool.get();
24 | pointF.set(x, y);
25 | return pointF;
26 | }
27 |
28 | public static void recycle(PointF pointF) {
29 | pool.recycle(pointF);
30 | }
31 |
32 | public static void recycle(List list) {
33 | pool.recycle(list);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/huobi/interfaces/IDataLineSet.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.huobi.interfaces;
2 |
3 | /**
4 | * Created by fxb on 2019-11-18.
5 | */
6 | public interface IDataLineSet {
7 |
8 | String getName();
9 |
10 | boolean isShowName();
11 |
12 | String getDataLabel();
13 |
14 | int getCount();
15 |
16 | void addData(boolean replace, int position, IData o);
17 |
18 | T getData(int position);
19 |
20 | int getDataCount();
21 |
22 | void addLine(int color, String label);
23 |
24 | int getLineColor(int index);
25 |
26 | String getLabel(int index);
27 |
28 | void addLinePoint(boolean replace, int position, Float... floats);
29 |
30 | Float getLinePoint(int index, int position);
31 |
32 | int getLineSize();
33 |
34 | float getMax(int index);
35 |
36 | float getMin(int index);
37 |
38 | void clear();
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/wyang/klinechartdemo/utils/AssetUtil.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartdemo.utils;
2 |
3 | import android.content.Context;
4 |
5 | import java.io.BufferedReader;
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 | import java.io.InputStreamReader;
9 |
10 | /**
11 | * Created by fxb on 2019-11-04.
12 | */
13 | public class AssetUtil {
14 |
15 | public static String readAsset(Context context, String filename) {
16 | StringBuilder sb = new StringBuilder();
17 | try {
18 | InputStream is = context.getAssets().open(filename);
19 |
20 | BufferedReader br = new BufferedReader(new InputStreamReader(is));
21 | String line;
22 | while ((line = br.readLine()) != null) {
23 | sb.append(line);
24 | }
25 | is.close();
26 | br.close();
27 | } catch (IOException e) {
28 | e.printStackTrace();
29 | }
30 | return sb.toString();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/klinechartlib/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion rootProject.ext.compileSdkVersion
5 | buildToolsVersion rootProject.ext.buildToolsVersion
6 | namespace "com.github.wyang.klinechartlib"
7 | defaultConfig {
8 | minSdkVersion rootProject.ext.minSdkVersion
9 | targetSdkVersion rootProject.ext.targetSdkVersion
10 | versionCode 1
11 | versionName "1.0"
12 |
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 | }
15 |
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 | }
23 |
24 | dependencies {
25 | api fileTree(dir: 'libs', include: ['*.jar'])
26 | def supportVersion = rootProject.ext.supportLibraryVersion
27 | api "com.android.support:appcompat-v7:$supportVersion"
28 | testImplementation 'junit:junit:4.12'
29 | }
30 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/huobi/draw/IChartDraw.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.huobi.draw;
2 |
3 | /**
4 | * Created by fxb on 2019-11-12.
5 | */
6 | public interface IChartDraw {
7 |
8 | /**
9 | * 设置当前指标名称,名称用于从适配器中取对应DataLineSet
10 | */
11 | void setName(String name);
12 |
13 | /**
14 | * 实际最大值
15 | */
16 | float getMaxValue();
17 |
18 | /**
19 | * 实际最小值
20 | */
21 | float getMinValue();
22 |
23 | /**
24 | * 最大值对应View中的y值
25 | */
26 | float getMaxAxisY();
27 |
28 | /**
29 | * 最小值对应View中的y值
30 | */
31 | float getMinAxisY();
32 |
33 | /**
34 | * 计算当前索引在矩形区域内的X坐标值
35 | */
36 | float getAxisX(int position);
37 |
38 | /**
39 | * 计算某一值在矩形区域内的Y坐标值
40 | */
41 | float getAxisY(float value);
42 |
43 | /**
44 | * 在矩形中,最大值上方预留的间距,用来绘制指标值String
45 | */
46 | float getTopSpacing();
47 |
48 | float getTop();
49 |
50 | float getBottom();
51 | }
52 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/formatter/DateFormatter.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.formatter;
2 |
3 | import android.support.annotation.NonNull;
4 |
5 | import com.github.wyang.klinechartlib.base.IDateFormatter;
6 |
7 | import java.text.SimpleDateFormat;
8 | import java.util.Date;
9 | import java.util.Locale;
10 | import java.util.TimeZone;
11 |
12 | /**
13 | * Created by fxb on 2019-11-06.
14 | */
15 | public class DateFormatter implements IDateFormatter {
16 | private SimpleDateFormat format;
17 | private Date date = new Date();
18 |
19 | public DateFormatter(String pattern) {
20 | format = new SimpleDateFormat(pattern, Locale.SIMPLIFIED_CHINESE);
21 | format.setTimeZone(TimeZone.getTimeZone("GMT+8"));
22 | }
23 |
24 | public DateFormatter(@NonNull SimpleDateFormat format) {
25 | this.format = format;
26 | }
27 |
28 | @Override
29 | public String format(long time) {
30 | date.setTime(time);
31 | return format.format(date);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/base/ChartAdapter.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.base;
2 |
3 | import android.database.DataSetObservable;
4 | import android.database.DataSetObserver;
5 | import android.support.annotation.NonNull;
6 |
7 | import com.github.wyang.klinechartlib.huobi.KLineChartView;
8 |
9 | /**
10 | * Created by fxb on 2019-11-04.
11 | */
12 | public abstract class ChartAdapter {
13 | protected final DataSetObservable mDataSetObservable = new DataSetObservable();
14 |
15 | /**
16 | * 获取点的数目
17 | *
18 | * @return
19 | */
20 | public abstract int getCount();
21 |
22 | /**
23 | * 注册一个数据观察者
24 | *
25 | * @param observer 数据观察者
26 | */
27 | void registerDataSetObserver(DataSetObserver observer) {
28 | mDataSetObservable.registerObserver(observer);
29 | }
30 |
31 | /**
32 | * 移除一个数据观察者
33 | *
34 | * @param observer 数据观察者
35 | */
36 | void unregisterDataSetObserver(DataSetObserver observer) {
37 | mDataSetObservable.unregisterObserver(observer);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | KLineChart-master
3 |
4 | 分时
5 | 15分钟
6 | 1小时
7 | 4小时
8 | 1天
9 | 更多
10 |
11 | - 1分钟
12 | - 5分钟
13 | - 30分钟
14 | - 1周
15 | - 1月
16 |
17 |
18 | 分时
19 | 1分钟
20 | 5分钟
21 | 15分钟
22 | 30分钟
23 | 1小时
24 | 4小时
25 | 1天
26 | 1周
27 | 1月
28 |
29 | 主图
30 | 副图
31 |
32 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/huobi/data/Candle.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.huobi.data;
2 |
3 | import com.github.wyang.klinechartlib.data.ICandle;
4 | import com.github.wyang.klinechartlib.huobi.interfaces.IData;
5 |
6 | /**
7 | * Created by fxb on 2019-11-11.
8 | */
9 | public class Candle implements ICandle, IData {
10 | public float open;
11 | public float high;
12 | public float low;
13 | public float close;
14 | public long time;
15 |
16 | @Override
17 | public float getOpen() {
18 | return open;
19 | }
20 |
21 | @Override
22 | public float getHigh() {
23 | return high;
24 | }
25 |
26 | @Override
27 | public float getLow() {
28 | return low;
29 | }
30 |
31 | @Override
32 | public float getClose() {
33 | return close;
34 | }
35 |
36 | @Override
37 | public long getTime() {
38 | return time;
39 | }
40 |
41 | @Override
42 | public float getMax() {
43 | return high;
44 | }
45 |
46 | @Override
47 | public float getMin() {
48 | return low;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #202326
4 | #202326
5 | #1AFFFFFF
6 | #738495
7 | #e2e2e2
8 | #081928
9 | #ffffff
10 |
11 | #C75266
12 | #4BA692
13 | #909EAE
14 | #516CFF
15 |
16 | #AAAAAA
17 | #5187EC
18 |
19 | #F2DC9C
20 | #7ECFC0
21 | #C197F7
22 | #EC4D46
23 | #89CE40
24 | #6437F5
25 |
26 |
27 | #ffffff
28 |
29 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/huobi/data/KLineEntity.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.huobi.data;
2 |
3 | /**
4 | * Created by fxb on 2019-12-28.
5 | */
6 | public class KLineEntity {
7 | public float open;//开盘价
8 | public float high;//最高价
9 | public float low;//最低价
10 | public float close;//收盘价
11 | public float vol;//成交量
12 | public float total;//成交额
13 | public long time;//时间戳 单位毫秒
14 |
15 | private Candle candle;
16 | private Volume volume;
17 |
18 | public Candle getCandle() {
19 | if (candle == null)
20 | candle = new Candle();
21 |
22 | candle.open = open;
23 | candle.high = high;
24 | candle.close = close;
25 | candle.low = low;
26 | candle.time = time;
27 | return candle;
28 | }
29 |
30 | public Volume getVolume() {
31 | if (volume == null)
32 | volume = new Volume();
33 |
34 | volume.volume = vol;
35 | return volume;
36 | }
37 |
38 | public void update(float newClose) {
39 | this.close = newClose;
40 |
41 | high = Math.max(high, close);
42 | low = Math.min(low, close);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/huobi/interfaces/IKLineChartAdapter.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.huobi.interfaces;
2 |
3 | import com.github.wyang.klinechartlib.huobi.KLineChartView;
4 | import com.github.wyang.klinechartlib.huobi.data.DataLineSetProvider;
5 | import com.github.wyang.klinechartlib.huobi.data.KLineEntity;
6 |
7 | import java.util.List;
8 |
9 | /**
10 | * Created by fxb on 2019-12-23.
11 | */
12 | public interface IKLineChartAdapter {
13 |
14 | void bindToKLineChartView(KLineChartView kLineChartView);
15 |
16 | void bindToDataLineSetProvider(DataLineSetProvider provider);
17 |
18 | /**
19 | * 第一次添加数据
20 | */
21 | void setNewData(List list);
22 |
23 | /**
24 | * 分页加载时添加数据
25 | */
26 | void addData(List list);
27 |
28 | /**
29 | * 接收报价时添加柱子
30 | */
31 | void addData(KLineEntity data, boolean replace);
32 |
33 | KLineEntity getData(int position);
34 |
35 | IDataLineSet getDataLineSet(String name);
36 |
37 | boolean isIncrease(int position);
38 |
39 | float getLatestPrice();
40 |
41 | void notifyDataSetChanged();
42 |
43 | void notifyDataSetInvalidated();
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_tab_k_line_more.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
18 |
19 |
26 |
--------------------------------------------------------------------------------
/app/src/main/assets/石头杂诗:
--------------------------------------------------------------------------------
1 | 空山新雨后,石头单身狗
2 | 欲穷千里目,石头单身狗
3 | 爹娘闻女来,石头单身狗
4 | 洞房花烛夜,石头单身狗
5 | 少壮不努力,石头单身狗
6 | 问君何所知,石头单身狗
7 | 危楼高百尺,石头单身狗
8 | 道路阻且长,石头单身狗
9 | 人生不相见,石头单身狗
10 | 驱车登古原,石头单身狗
11 | 晓看红湿处,石头单身狗
12 | 海内存知己,石头单身狗
13 | 风疏雨骤,石头单身狗
14 | 知否知否,石头单身狗
15 | 双兔傍地走,安知石头单身狗
16 | 海客谈石头,还是单身狗
17 | 越人语石头,还是单身狗
18 | 蓦然回首,石头却是单身狗
19 | 石头虽是单身狗,却道天凉好个秋
20 | 石头虽是单身狗,与尔同消万古愁
21 | 谁知石头单身狗,而月春风似增高
22 | 遥知石头单身狗,遍插朱萸少一人
23 | 满园石头单身狗,一枝红杏出墙来
24 | 只因石头单身狗,从此君王不早朝
25 | 天生我材必有用,不如石头单身狗
26 | 人生在世不如意,就像石头单身狗
27 | 问君能有几多愁,恰是石头单身狗
28 | 横眉冷对千夫指,唯有石头单身狗
29 | 东风不与周郎便,咋看石头单身狗
30 | 夜深忽梦少年事,瞧那石头单身狗
31 | 石头垂死惊坐起,笑问谁是单身狗
32 | 无端嫁得金龟婿,石头还是单身狗
33 | 停车做爱枫林晚,石头无奈单身狗
34 | 何当共剪西窗烛,却话石头单身狗
35 | 报君黄金台上意,从此石头单身狗
36 | 此情可待成追忆,只是石头单身狗
37 | 何当共剪西窗烛,却话石头单身狗
38 | 两只黄鹂鸣翠柳,一行石头单身狗
39 | 少小离家老大回,石头仍是单身狗
40 | 清明时节雨纷纷,路上石头单身狗
41 | 乘风破浪会有时,直挂石头单身狗
42 | 李白乘舟将欲行,忽闻石头单身狗
43 | 飞流直下三千尺,不及石头单身狗
44 | 蓬山此去无多路,只原石头单身狗
45 | 人生自古谁无死,可惜石头单身狗
46 | 春蝉到死丝方尽,蜡炬石头单身狗
47 | 不识庐山真面目,只缘石头单身狗
48 | 天长地久有时尽,此恨石头单身狗
49 | 借问酒家何处有,遥指石头单身狗
50 | 花泽香菜兵库北,艺颜石头单身狗
51 | 雏菊花开绽放时,石头却是单身狗
52 | 人面不知何处去,石头依旧单身狗
53 | 桃花潭水深千尺,不及石头单身狗
54 | 尔来四万八千岁,不知石头单身狗
55 | 我自横刀向天笑,石头只有单身狗
56 | 两岸猿声啼不住,泛舟石头单身狗
57 | 春色满园关不住,一支石头单身狗
58 | 落红不是无情物,化作石头单身狗
59 | 后宫佳丽三千人,石头磨成单身狗
60 | 但使龙城飞将在,不及石头单身狗
61 | 山穷水复疑无路,却见石头单身狗
62 | 出师未捷身先死,白白石头单身狗
63 | 王师北定中原日,石头还是单身狗
64 | 石头不是无情物,无奈还是单身狗
--------------------------------------------------------------------------------
/klinechartlib/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 、
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/klinechartlib/src/test/java/com/github/wyang/klinechartlib/ObjectPoolTest.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib;
2 |
3 | import com.github.wyang.klinechartlib.utils.IntegerPool;
4 |
5 | import org.junit.Test;
6 |
7 | import java.util.ArrayList;
8 | import java.util.HashMap;
9 | import java.util.List;
10 |
11 | /**
12 | * Created by fxb on 2019-11-29.
13 | */
14 | public class ObjectPoolTest {
15 |
16 | @Test
17 | public void test() {
18 | IntegerPool pool = IntegerPool.getPool();
19 |
20 | pool.printTestInfo();
21 |
22 | List list = new ArrayList<>();
23 | for (int i = 0; i < 48; i++) {
24 | list.add(pool.get());
25 | }
26 |
27 | pool.printTestInfo();
28 | System.out.println(list.toString());
29 |
30 | pool.recycle(list);
31 | pool.printTestInfo();
32 |
33 | }
34 |
35 | @Test
36 | public void testSqrt() {
37 | System.out.println(sqrt(2));
38 |
39 | System.out.println(~0b101);
40 | }
41 |
42 | private static float sqrt(int number) {
43 | if (number == 0) {
44 | return 0;
45 | }
46 |
47 | float i = 0;
48 | float x1, x2 = 0;
49 | while ((i * i) <= number) {
50 | i += 0.1;
51 | }
52 | x1 = i;
53 | for (int j = 0; j < 10; j++) {
54 | x2 = number;
55 | x2 /= x1;
56 | x2 += x1;
57 | x2 /= 2;
58 | x1 = x2;
59 | }
60 | return x2;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion rootProject.ext.compileSdkVersion
5 | buildToolsVersion rootProject.ext.buildToolsVersion
6 | namespace "com.github.wyang.klinechartdemo"
7 | defaultConfig {
8 | applicationId "com.github.wyang.klinechartdemo"
9 | minSdkVersion rootProject.ext.minSdkVersion
10 | targetSdkVersion rootProject.ext.targetSdkVersion
11 | versionCode 1
12 | versionName "1.0"
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 | }
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 | compileOptions {
22 | sourceCompatibility = '1.8'
23 | targetCompatibility = '1.8'
24 | }
25 | }
26 |
27 | dependencies {
28 | implementation fileTree(dir: 'libs', include: ['*.jar'])
29 |
30 | testImplementation 'junit:junit:4.12'
31 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
32 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
33 |
34 | def supportVersion = rootProject.ext.supportLibraryVersion
35 | api "com.android.support:appcompat-v7:$supportVersion"
36 | api "com.android.support:design:$supportVersion"
37 | api 'com.android.support.constraint:constraint-layout:1.1.3'
38 |
39 | implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.44'
40 |
41 | implementation 'com.zhouyou:rxeasyhttp:2.1.5'
42 |
43 | api project(path: ':klinechartlib')
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/wyang/klinechartdemo/widget/ObservableScrollView.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartdemo.widget;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.MotionEvent;
6 | import android.widget.ScrollView;
7 |
8 | /**
9 | * Created by *** on 2019/7/30.
10 | */
11 | public class ObservableScrollView extends ScrollView {
12 | private float xDistance, yDistance, xLast, yLast;
13 |
14 | public ObservableScrollView(Context context) {
15 | super(context);
16 | }
17 |
18 | public ObservableScrollView(Context context, AttributeSet attrs) {
19 | super(context, attrs);
20 | }
21 |
22 | public ObservableScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
23 | super(context, attrs, defStyleAttr);
24 | }
25 |
26 | @Override
27 | public boolean onInterceptTouchEvent(MotionEvent ev) {
28 | switch (ev.getAction()) {
29 | case MotionEvent.ACTION_DOWN:
30 | xDistance = yDistance = 0f;
31 | xLast = ev.getX();
32 | yLast = ev.getY();
33 | break;
34 | case MotionEvent.ACTION_MOVE:
35 | final float curX = ev.getX();
36 | final float curY = ev.getY();
37 |
38 | xDistance += Math.abs(curX - xLast);
39 | yDistance += Math.abs(curY - yLast);
40 | xLast = curX;
41 | yLast = curY;
42 |
43 | /**
44 | * X轴滑动距离大于Y轴滑动距离,也就是用户横向滑动时,返回false,ScrollView不处理这次事件,
45 | * 让子控件中的TouchEvent去处理,所以横向滑动的事件交由子控件处理, ScrollView只处理纵向滑动事件
46 | */
47 | if (xDistance > yDistance) {
48 | return false;
49 | }
50 | }
51 |
52 | return super.onInterceptTouchEvent(ev);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/utils/ObjectPool.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.utils;
2 |
3 | import java.util.HashSet;
4 | import java.util.List;
5 | import java.util.Set;
6 | import java.util.Stack;
7 |
8 | /**
9 | * Created by fxb on 2019-11-27.
10 | * 对象池,参考-超市购物车,后进先出(理想情况,每一位顾客借车都会归还)
11 | */
12 | public abstract class ObjectPool {
13 | /**
14 | * 初始预期容量,建议为2的指数倍
15 | */
16 | private int desiredCapacity;
17 | /**
18 | * 扩充量
19 | */
20 | private int expansionCapacity;
21 |
22 | private Stack objects;
23 | private Set recordSet;
24 |
25 | public ObjectPool(int initialCapacity, int expansionCapacity) {
26 | if (initialCapacity <= 0) {
27 | throw new IllegalArgumentException("Object Pool must be instantiated with a capacity greater than 0!");
28 | }
29 | this.desiredCapacity = initialCapacity;
30 | this.objects = new Stack<>();
31 | this.recordSet = new HashSet<>();
32 | setExpansionCapacity(expansionCapacity);
33 |
34 | this.refillPool(desiredCapacity);
35 | }
36 |
37 | public abstract T createObject();
38 |
39 | private void setExpansionCapacity(int expansionCapacity) {
40 | if (expansionCapacity < 0 || expansionCapacity > desiredCapacity) {
41 | expansionCapacity = desiredCapacity / 2;
42 | }
43 | this.expansionCapacity = expansionCapacity;
44 | }
45 |
46 | private void refillPool(int count) {
47 | for (int i = 0; i < count; i++) {
48 | T t = createObject();
49 | objects.add(t);
50 |
51 | recordSet.add(t);
52 | }
53 | }
54 |
55 | public synchronized T get() {
56 | if (objects.isEmpty()) {
57 | if (expansionCapacity == 0) {
58 | expansionCapacity = desiredCapacity / 2;
59 | }
60 |
61 | desiredCapacity += expansionCapacity;
62 | this.refillPool(expansionCapacity);
63 | }
64 | return objects.pop();
65 | }
66 |
67 | public synchronized void recycle(T t) {
68 | //当前对象池借出而且未归还才执行归还操作
69 | if (recordSet.contains(t) && !objects.contains(t)) {
70 | objects.add(t);
71 | }
72 | }
73 |
74 | public synchronized void recycle(List list) {
75 | for (T t : list) {
76 | recycle(t);
77 | }
78 | }
79 |
80 | public void printTestInfo() {
81 | System.out.println("当前容量=" + desiredCapacity + ",可用数量=" + objects.size() + ",实际数量=" + recordSet.size());
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/huobi/KLineChartAdapter.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.huobi;
2 |
3 | import com.github.wyang.klinechartlib.base.ChartAdapter;
4 | import com.github.wyang.klinechartlib.huobi.data.DataLineSetProvider;
5 | import com.github.wyang.klinechartlib.huobi.data.KLineEntity;
6 | import com.github.wyang.klinechartlib.huobi.interfaces.IDataLineSet;
7 | import com.github.wyang.klinechartlib.huobi.interfaces.IDataLineSetProvider;
8 | import com.github.wyang.klinechartlib.huobi.interfaces.IKLineChartAdapter;
9 |
10 | import java.util.ArrayList;
11 | import java.util.List;
12 |
13 | /**
14 | * Created by fxb on 2019-11-04.
15 | */
16 | public class KLineChartAdapter extends ChartAdapter implements IKLineChartAdapter {
17 | private List mData = new ArrayList<>();
18 | private IDataLineSetProvider provider;
19 |
20 | @Override
21 | public int getCount() {
22 | return mData.size();
23 | }
24 |
25 | @Override
26 | public void bindToKLineChartView(KLineChartView kLineChartView) {
27 | kLineChartView.setAdapter(this);
28 | }
29 |
30 | @Override
31 | public void bindToDataLineSetProvider(DataLineSetProvider provider) {
32 | this.provider = provider;
33 | }
34 |
35 | @Override
36 | public void setNewData(List list) {
37 | mData.clear();
38 | mData.addAll(list);
39 |
40 | provider.calculateAll(mData);
41 |
42 | notifyDataSetChanged();
43 | }
44 |
45 | @Override
46 | public void addData(List list) {
47 | mData.addAll(0, list);
48 |
49 | provider.calculateAll(mData);
50 |
51 | notifyDataSetChanged();
52 | }
53 |
54 | @Override
55 | public void addData(KLineEntity data, boolean replace) {
56 | if (replace)
57 | mData.set(mData.size() - 1, data);
58 | else
59 | mData.add(data);
60 |
61 | provider.calculateLast(mData, replace);
62 |
63 | notifyDataSetChanged();
64 | }
65 |
66 | @Override
67 | public KLineEntity getData(int position) {
68 | return mData.get(position);
69 | }
70 |
71 | @Override
72 | public boolean isIncrease(int position) {
73 | KLineEntity data = getData(position);
74 | return data.close - data.open >= 0;
75 | }
76 |
77 | @Override
78 | public float getLatestPrice() {
79 | return getCount() == 0 ? 0 : getData(getCount() - 1).close;
80 | }
81 |
82 | @Override
83 | public void notifyDataSetChanged() {
84 | mDataSetObservable.notifyChanged();
85 | }
86 |
87 | @Override
88 | public void notifyDataSetInvalidated() {
89 | mDataSetObservable.notifyInvalidated();
90 | }
91 |
92 | @Override
93 | public IDataLineSet getDataLineSet(String name) {
94 | return provider.get(name.toLowerCase());
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/huobi/draw/ChartDraw.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.huobi.draw;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.RectF;
5 | import android.support.annotation.NonNull;
6 |
7 | import com.github.wyang.klinechartlib.huobi.KLineChartView;
8 | import com.github.wyang.klinechartlib.huobi.helper.LinePathHelper;
9 |
10 | /**
11 | * Created by fxb on 2019-11-04.
12 | * 图表拆分部分,绘制流程如下:
13 | * 1,绘制蜡烛图(K线蜡烛,成交量,成交额)
14 | * 2,绘制指标线(同区域存在多条指标线,不分先后顺序)
15 | * 3,绘制坐标值(x轴日期,y轴价格值,指标值,成交量值..)
16 | * 4,绘制高亮值(最大最小值在前,长按高亮线,该点对应各值等)
17 | */
18 | public abstract class ChartDraw implements IChartDraw {
19 | /**
20 | * 表格一部分在View所占矩形区域
21 | */
22 | private RectF mBounds = new RectF();
23 |
24 | private float mTopSpacing;
25 |
26 | /**
27 | * 矩形最上方y轴对应值(最大)
28 | */
29 | float maxValue;
30 | /**
31 | * 矩形最下方y轴对应值(最小)
32 | */
33 | float minValue;
34 | /**
35 | * 指标名称
36 | */
37 | protected String name = "";
38 |
39 | KLineChartView mChart;
40 | LinePathHelper mHelper;
41 |
42 | ChartDraw(KLineChartView chart, LinePathHelper helper) {
43 | this.mChart = chart;
44 | this.mHelper = helper;
45 | }
46 |
47 | public abstract void draw(@NonNull Canvas canvas);
48 |
49 | public abstract void calcMinMax(int position, boolean isReset);
50 |
51 | public abstract void fixMaxMin(float diff);
52 |
53 | @Override
54 | public void setName(String name) {
55 | this.name = name;
56 | }
57 |
58 | public void setBounds(float left, float top, float right, float bottom) {
59 | mBounds.set(left, top, right, bottom);
60 | }
61 |
62 | public void setTopSpacing(float mTopSpacing) {
63 | this.mTopSpacing = mTopSpacing;
64 | }
65 |
66 | @Override
67 | public float getMaxValue() {
68 | return maxValue;
69 | }
70 |
71 | @Override
72 | public float getMinValue() {
73 | return minValue;
74 | }
75 |
76 | @Override
77 | public float getMaxAxisY() {
78 | return mBounds.top + mTopSpacing;
79 | }
80 |
81 | @Override
82 | public float getMinAxisY() {
83 | return mBounds.bottom;
84 | }
85 |
86 | @Override
87 | public float getAxisX(int position) {
88 | return mChart.getDrawX(position);
89 | }
90 |
91 | @Override
92 | public float getAxisY(float value) {
93 | float scaleY = (getMinAxisY() - getMaxAxisY()) / (maxValue - minValue);
94 |
95 | return (maxValue - value) * scaleY + getMaxAxisY();
96 | }
97 |
98 | @Override
99 | public float getTopSpacing() {
100 | return mTopSpacing;
101 | }
102 |
103 | @Override
104 | public float getTop() {
105 | return mBounds.top;
106 | }
107 |
108 | @Override
109 | public float getBottom() {
110 | return mBounds.bottom;
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/huobi/helper/TextDrawHelper.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.huobi.helper;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Paint;
5 | import android.graphics.PointF;
6 |
7 | /**
8 | * Created by fxb on 2019-12-04.
9 | * 画布上绘制文本辅助类
10 | */
11 | public class TextDrawHelper {
12 | /**
13 | * 默认绘制文本,点在文本矩形的正左方
14 | */
15 | private void draw(Canvas c, String text, float x, float y, Paint paint) {
16 | Paint.FontMetrics fontMetrics = paint.getFontMetrics();
17 | y -= (fontMetrics.descent + fontMetrics.ascent) / 2;
18 |
19 | c.drawText(text, x, y, paint);
20 | }
21 |
22 | /**
23 | * 绘制文本于点的正中心
24 | */
25 | public void drawPointCenter(Canvas c, String text, PointF p, Paint paint) {
26 | float x = p.x - paint.measureText(text) / 2;
27 | float y = p.y;
28 |
29 | draw(c, text, x, y, paint);
30 | }
31 |
32 | /**
33 | * 绘制文本于点的正左方
34 | */
35 | public void drawPointLeft(Canvas c, String text, PointF p, Paint paint) {
36 | float x = p.x - paint.measureText(text);
37 | float y = p.y;
38 |
39 | draw(c, text, x, y, paint);
40 | }
41 |
42 | /**
43 | * 绘制文本于点的正上方
44 | */
45 | public void drawPointTop(Canvas c, String text, PointF p, Paint paint) {
46 | float x = p.x - paint.measureText(text) / 2;
47 | float y = p.y - getTextHeight(paint) / 2;
48 |
49 | draw(c, text, x, y, paint);
50 | }
51 |
52 | /**
53 | * 绘制文本于点的正右方
54 | */
55 | public void drawPointRight(Canvas c, String text, PointF p, Paint paint) {
56 | float x = p.x;
57 | float y = p.y;
58 |
59 | draw(c, text, x, y, paint);
60 | }
61 |
62 | /**
63 | * 绘制文本于点的正下方
64 | */
65 | public void drawPointBot(Canvas c, String text, PointF p, Paint paint) {
66 | float x = p.x - paint.measureText(text) / 2;
67 | float y = p.y + getTextHeight(paint) / 2;
68 |
69 | draw(c, text, x, y, paint);
70 | }
71 |
72 | /**
73 | * 绘制文本于点的左上方
74 | */
75 | public void drawPointLeftTop(Canvas c, String text, PointF p, Paint paint) {
76 | float x = p.x - paint.measureText(text);
77 | float y = p.y - getTextHeight(paint) / 2;
78 |
79 | draw(c, text, x, y, paint);
80 | }
81 |
82 | /**
83 | * 绘制文本于点的左下方
84 | */
85 | public void drawPointLeftBot(Canvas c, String text, PointF p, Paint paint) {
86 | float x = p.x - paint.measureText(text);
87 | float y = p.y + getTextHeight(paint) / 2;
88 |
89 | draw(c, text, x, y, paint);
90 | }
91 |
92 | /**
93 | * 绘制文本于点的右下方
94 | */
95 | public void drawPointRightBot(Canvas c, String text, PointF p, Paint paint) {
96 | float x = p.x;
97 | float y = p.y + getTextHeight(paint) / 2;
98 |
99 | draw(c, text, x, y, paint);
100 | }
101 |
102 | public float getTextHeight(Paint paint) {
103 | Paint.FontMetrics fontMetrics = paint.getFontMetrics();
104 | return fontMetrics.bottom - fontMetrics.top;
105 | }
106 |
107 | public float getTextHeight2(Paint paint) {
108 | return paint.ascent() + paint.descent();
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/popup_setting.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
21 |
22 |
32 |
33 |
44 |
45 |
56 |
57 |
67 |
68 |
79 |
80 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/wyang/klinechartdemo/widget/PopupController.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartdemo.widget;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.graphics.drawable.ColorDrawable;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.view.Window;
10 | import android.view.WindowManager;
11 | import android.widget.PopupWindow;
12 |
13 | /**
14 | * Created by fxb on 2017/6/22.
15 | */
16 | public class PopupController {
17 | private int layoutResId;//布局id
18 | private Context context;
19 | private PopupWindow popupWindow;
20 | View mPopupView;//弹窗布局View
21 | private View mView;
22 | private Window mWindow;
23 |
24 | PopupController(Context context, PopupWindow popupWindow) {
25 | this.context = context;
26 | this.popupWindow = popupWindow;
27 | }
28 |
29 | public void setView(int layoutResId) {
30 | mView = null;
31 | this.layoutResId = layoutResId;
32 | installContent();
33 | }
34 |
35 | public void setView(View view) {
36 | mView = view;
37 | this.layoutResId = 0;
38 | installContent();
39 | }
40 |
41 | private void installContent() {
42 | if (layoutResId != 0) {
43 | mPopupView = LayoutInflater.from(context).inflate(layoutResId, null);
44 | } else if (mView != null) {
45 | mPopupView = mView;
46 | }
47 | popupWindow.setContentView(mPopupView);
48 | }
49 |
50 | /**
51 | * 设置宽度
52 | *
53 | * @param width 宽
54 | * @param height 高
55 | */
56 | private void setWidthAndHeight(int width, int height) {
57 | if (width == 0 || height == 0) {
58 | //如果没设置宽高,默认是WRAP_CONTENT
59 | popupWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
60 | popupWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
61 | } else {
62 | popupWindow.setWidth(width);
63 | popupWindow.setHeight(height);
64 | }
65 | }
66 |
67 |
68 | /**
69 | * 设置背景灰色程度
70 | *
71 | * @param level 0.0f-1.0f
72 | */
73 | void setBackGroundLevel(float level) {
74 | mWindow = ((Activity) context).getWindow();
75 | WindowManager.LayoutParams params = mWindow.getAttributes();
76 | params.alpha = level;
77 | mWindow.setAttributes(params);
78 | }
79 |
80 |
81 | /**
82 | * 设置动画
83 | */
84 | private void setAnimationStyle(int animationStyle) {
85 | popupWindow.setAnimationStyle(animationStyle);
86 | }
87 |
88 | /**
89 | * 设置Outside是否可点击
90 | *
91 | * @param touchable 是否可点击
92 | */
93 | private void setOutsideTouchable(boolean touchable) {
94 | popupWindow.setBackgroundDrawable(new ColorDrawable(0x00000000));//设置透明背景
95 | popupWindow.setOutsideTouchable(touchable);//设置outside可点击
96 | popupWindow.setFocusable(touchable);
97 | }
98 |
99 |
100 | static class PopupParams {
101 | public int layoutResId;//布局id
102 | public Context mContext;
103 | public int mWidth, mHeight;//弹窗的宽和高
104 | public boolean isShowBg, isShowAnim;
105 | public float bg_level;//屏幕背景灰色程度
106 | public int animationStyle;//动画Id
107 | public View mView;
108 | public boolean isTouchable = true;
109 |
110 | public PopupParams(Context mContext) {
111 | this.mContext = mContext;
112 | }
113 |
114 | public void apply(PopupController controller) {
115 | if (mView != null) {
116 | controller.setView(mView);
117 | } else if (layoutResId != 0) {
118 | controller.setView(layoutResId);
119 | } else {
120 | throw new IllegalArgumentException("PopupView's contentView is null");
121 | }
122 | controller.setWidthAndHeight(mWidth, mHeight);
123 | controller.setOutsideTouchable(isTouchable);//设置outside可点击
124 | if (isShowBg) {
125 | //设置背景
126 | controller.setBackGroundLevel(bg_level);
127 | }
128 | if (isShowAnim) {
129 | controller.setAnimationStyle(animationStyle);
130 | }
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/wyang/klinechartdemo/widget/CommonPopupWindow.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartdemo.widget;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 | import android.widget.PopupWindow;
6 |
7 | /**
8 | * Created by fxb on 2017/6/22.
9 | */
10 | public class CommonPopupWindow extends PopupWindow {
11 | final PopupController controller;
12 |
13 | @Override
14 | public int getWidth() {
15 | return controller.mPopupView.getMeasuredWidth();
16 | }
17 |
18 | @Override
19 | public int getHeight() {
20 | return controller.mPopupView.getMeasuredHeight();
21 | }
22 |
23 | public interface ViewInterface {
24 | void getChildView(View view, int layoutResId);
25 | }
26 |
27 | private CommonPopupWindow(Context context) {
28 | controller = new PopupController(context, this);
29 | }
30 |
31 | @Override
32 | public void showAtLocation(View parent, int gravity, int x, int y) {
33 | super.showAtLocation(parent, gravity, x, y);
34 | controller.setBackGroundLevel(0.5f);
35 | }
36 |
37 | @Override
38 | public void dismiss() {
39 | super.dismiss();
40 | controller.setBackGroundLevel(1.0f);
41 | }
42 |
43 | public static class Builder {
44 | private final PopupController.PopupParams params;
45 | private ViewInterface listener;
46 |
47 | public Builder(Context context) {
48 | params = new PopupController.PopupParams(context);
49 | }
50 |
51 | /**
52 | * @param layoutResId 设置PopupWindow 布局ID
53 | * @return Builder
54 | */
55 | public Builder setView(int layoutResId) {
56 | params.mView = null;
57 | params.layoutResId = layoutResId;
58 | return this;
59 | }
60 |
61 | /**
62 | * @param view 设置PopupWindow布局
63 | * @return Builder
64 | */
65 | public Builder setView(View view) {
66 | params.mView = view;
67 | params.layoutResId = 0;
68 | return this;
69 | }
70 |
71 | /**
72 | * 设置子View
73 | *
74 | * @param listener ViewInterface
75 | * @return Builder
76 | */
77 | public Builder setViewOnclickListener(ViewInterface listener) {
78 | this.listener = listener;
79 | return this;
80 | }
81 |
82 | /**
83 | * 设置宽度和高度 如果不设置 默认是wrap_content
84 | *
85 | * @param width 宽
86 | * @return Builder
87 | */
88 | public Builder setWidthAndHeight(int width, int height) {
89 | params.mWidth = width;
90 | params.mHeight = height;
91 | return this;
92 | }
93 |
94 | /**
95 | * 设置背景灰色程度
96 | *
97 | * @param level 0.0f-1.0f
98 | * @return Builder
99 | */
100 | public Builder setBackGroundLevel(float level) {
101 | params.isShowBg = true;
102 | params.bg_level = level;
103 | return this;
104 | }
105 |
106 | /**
107 | * 是否可点击Outside消失
108 | *
109 | * @param touchable 是否可点击
110 | * @return Builder
111 | */
112 | public Builder setOutsideTouchable(boolean touchable) {
113 | params.isTouchable = touchable;
114 | return this;
115 | }
116 |
117 | /**
118 | * 设置动画
119 | *
120 | * @return Builder
121 | */
122 | public Builder setAnimationStyle(int animationStyle) {
123 | params.isShowAnim = true;
124 | params.animationStyle = animationStyle;
125 | return this;
126 | }
127 |
128 | public CommonPopupWindow create() {
129 | final CommonPopupWindow popupWindow = new CommonPopupWindow(params.mContext);
130 | params.apply(popupWindow.controller);
131 | if (listener != null && params.layoutResId != 0) {
132 | listener.getChildView(popupWindow.controller.mPopupView, params.layoutResId);
133 | }
134 |
135 | int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
136 | int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
137 | popupWindow.controller.mPopupView.measure(w, h);
138 | return popupWindow;
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/app/src/main/assets/little.json:
--------------------------------------------------------------------------------
1 | [
2 | [
3 | 7302.78,
4 | 7297.45,
5 | 7304.97,
6 | 7287.73,
7 | 1575303000000,
8 | 121.542
9 | ],
10 | [
11 | 7298.66,
12 | 7297.72,
13 | 7307.24,
14 | 7294.99,
15 | 1575303060000,
16 | 22.8154
17 | ],
18 | [
19 | 7297.71,
20 | 7293.79,
21 | 7297.71,
22 | 7286.80,
23 | 1575303120000,
24 | 18.5118
25 | ],
26 | [
27 | 7293.78,
28 | 7303.27,
29 | 7304.31,
30 | 7293.12,
31 | 1575303180000,
32 | 10.6001
33 | ],
34 | [
35 | 7303.94,
36 | 7315.14,
37 | 7318.23,
38 | 7303.94,
39 | 1575303240000,
40 | 18.2516
41 | ],
42 | [
43 | 7314.40,
44 | 7321.74,
45 | 7326.82,
46 | 7313.20,
47 | 1575303300000,
48 | 17.6498
49 | ],
50 | [
51 | 7324.57,
52 | 7312.64,
53 | 7325.98,
54 | 7311.63,
55 | 1575303360000,
56 | 4.71672
57 | ],
58 | [
59 | 7311.82,
60 | 7308.40,
61 | 7311.82,
62 | 7302.73,
63 | 1575303420000,
64 | 3.14229
65 | ],
66 | [
67 | 7308.09,
68 | 7303.16,
69 | 7311.73,
70 | 7303.08,
71 | 1575303480000,
72 | 4.42419
73 | ],
74 | [
75 | 7303.14,
76 | 7307.00,
77 | 7307.04,
78 | 7299.97,
79 | 1575303540000,
80 | 4.54998
81 | ],
82 | [
83 | 7307.04,
84 | 7307.95,
85 | 7311.55,
86 | 7305.47,
87 | 1575303600000,
88 | 5.30718
89 | ],
90 | [
91 | 7307.22,
92 | 7308.24,
93 | 7309.64,
94 | 7304.68,
95 | 1575303660000,
96 | 1.47813
97 | ],
98 | [
99 | 7306.80,
100 | 7304.81,
101 | 7311.79,
102 | 7304.80,
103 | 1575303720000,
104 | 3.9934
105 | ],
106 | [
107 | 7304.80,
108 | 7294.09,
109 | 7304.81,
110 | 7292.01,
111 | 1575303780000,
112 | 19.334
113 | ],
114 | [
115 | 7295.78,
116 | 7291.94,
117 | 7301.80,
118 | 7290.87,
119 | 1575303840000,
120 | 7.08857
121 | ],
122 | [
123 | 7291.49,
124 | 7303.33,
125 | 7303.42,
126 | 7288.63,
127 | 1575303900000,
128 | 4.6531
129 | ],
130 | [
131 | 7302.97,
132 | 7298.92,
133 | 7302.97,
134 | 7296.80,
135 | 1575303960000,
136 | 4.59234
137 | ],
138 | [
139 | 7297.84,
140 | 7300.48,
141 | 7300.48,
142 | 7293.26,
143 | 1575304020000,
144 | 4.85822
145 | ],
146 | [
147 | 7301.40,
148 | 7300.78,
149 | 7304.80,
150 | 7299.30,
151 | 1575304080000,
152 | 3.77003
153 | ],
154 | [
155 | 7299.99,
156 | 7297.91,
157 | 7301.80,
158 | 7296.80,
159 | 1575304140000,
160 | 1.70624
161 | ],
162 | [
163 | 7296.74,
164 | 7295.78,
165 | 7297.53,
166 | 7293.67,
167 | 1575304200000,
168 | 1.24033
169 | ],
170 | [
171 | 7295.52,
172 | 7285.05,
173 | 7295.52,
174 | 7281.96,
175 | 1575304260000,
176 | 10.5466
177 | ],
178 | [
179 | 7284.02,
180 | 7284.89,
181 | 7291.72,
182 | 7281.80,
183 | 1575304320000,
184 | 13.5388
185 | ],
186 | [
187 | 7283.64,
188 | 7284.53,
189 | 7285.22,
190 | 7277.58,
191 | 1575304380000,
192 | 8.14867
193 | ],
194 | [
195 | 7285.22,
196 | 7293.15,
197 | 7293.15,
198 | 7282.31,
199 | 1575304440000,
200 | 6.44772
201 | ],
202 | [
203 | 7292.51,
204 | 7299.97,
205 | 7301.72,
206 | 7292.38,
207 | 1575304500000,
208 | 6.08902
209 | ],
210 | [
211 | 7300.04,
212 | 7291.78,
213 | 7301.72,
214 | 7290.13,
215 | 1575304560000,
216 | 6.22454
217 | ],
218 | [
219 | 7290.12,
220 | 7286.47,
221 | 7291.80,
222 | 7284.09,
223 | 1575304620000,
224 | 6.18076
225 | ],
226 | [
227 | 7286.42,
228 | 7292.70,
229 | 7293.54,
230 | 7284.52,
231 | 1575304680000,
232 | 14.0331
233 | ],
234 | [
235 | 7292.34,
236 | 7288.74,
237 | 7292.68,
238 | 7280.10,
239 | 1575304740000,
240 | 16.5069
241 | ],
242 | [
243 | 7287.83,
244 | 7285.23,
245 | 7291.46,
246 | 7285.20,
247 | 1575304800000,
248 | 4.95303
249 | ],
250 | [
251 | 7285.30,
252 | 7292.17,
253 | 7292.36,
254 | 7281.80,
255 | 1575304860000,
256 | 24.0774
257 | ],
258 | [
259 | 7292.38,
260 | 7294.45,
261 | 7296.80,
262 | 7292.25,
263 | 1575304920000,
264 | 6.12603
265 | ],
266 | [
267 | 7292.58,
268 | 7293.26,
269 | 7293.28,
270 | 7285.85,
271 | 1575304980000,
272 | 4.67842
273 | ],
274 | [
275 | 7291.78,
276 | 7294.50,
277 | 7295.71,
278 | 7290.99,
279 | 1575305040000,
280 | 2.29064
281 | ],
282 | [
283 | 7294.51,
284 | 7291.82,
285 | 7295.77,
286 | 7291.80,
287 | 1575305100000,
288 | 3.6707
289 | ]
290 | ]
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/huobi/data/DataLineSet.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.huobi.data;
2 |
3 | import android.util.SparseArray;
4 | import android.util.SparseIntArray;
5 |
6 | import com.github.wyang.klinechartlib.huobi.interfaces.IData;
7 | import com.github.wyang.klinechartlib.huobi.interfaces.IDataLineSet;
8 |
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | /**
13 | * Created by fxb on 2019-11-18.
14 | */
15 | public class DataLineSet implements IDataLineSet {
16 | /**
17 | * 指标名称(ma,rsi,sar,macd,boll...)
18 | */
19 | private String name;
20 | /**
21 | * 是否显示名称
22 | */
23 | private boolean isShowName;
24 | /**
25 | * data数据对应标签
26 | */
27 | private String dataLabel;
28 | /**
29 | * 用于绘制矩形的值
30 | */
31 | private List data = new ArrayList<>();
32 | /**
33 | * 线的颜色
34 | */
35 | private SparseIntArray colors = new SparseIntArray();
36 | /**
37 | * 指标标签
38 | */
39 | private SparseArray labels = new SparseArray<>();
40 | /**
41 | * 绘制线的点
42 | */
43 | private SparseArray> lines = new SparseArray<>();
44 |
45 | public DataLineSet() {
46 | }
47 |
48 | public void setName(String name) {
49 | this.name = name;
50 | }
51 |
52 | public void setShowName(boolean showName) {
53 | isShowName = showName;
54 | }
55 |
56 | public void setDataLabel(String dataLabel) {
57 | this.dataLabel = dataLabel;
58 | }
59 |
60 | @Override
61 | public String getName() {
62 | return name;
63 | }
64 |
65 | @Override
66 | public boolean isShowName() {
67 | return isShowName;
68 | }
69 |
70 | @Override
71 | public String getDataLabel() {
72 | return dataLabel;
73 | }
74 |
75 | @Override
76 | public int getCount() {
77 | if (!data.isEmpty())
78 | return getDataCount();
79 | if (lines.size() != 0)
80 | return lines.get(0).size();
81 | return 0;
82 | }
83 |
84 | @Override
85 | public void addData(boolean replace, int position, IData o) {
86 | if (replace && position < data.size())
87 | data.set(position, o);
88 | else
89 | data.add(position, o);
90 | }
91 |
92 | @Override
93 | public T getData(int position) {
94 | return (T) data.get(position);
95 | }
96 |
97 | @Override
98 | public int getDataCount() {
99 | return data.size();
100 | }
101 |
102 | @Override
103 | public void addLine(int color, String label) {
104 | int size = getLineSize();
105 | colors.put(size, color);
106 | labels.put(size, label);
107 | lines.put(size, new ArrayList());
108 | }
109 |
110 | @Override
111 | public int getLineColor(int index) {
112 | return colors.get(index);
113 | }
114 |
115 | @Override
116 | public String getLabel(int index) {
117 | return labels.get(index);
118 | }
119 |
120 | @Override
121 | public void addLinePoint(boolean replace, int position, Float... floats) {
122 | if (floats.length != lines.size()) {
123 | throw new IllegalArgumentException("更新线的元素点时,点与线的数量必须相等");
124 | }
125 |
126 | for (int i = 0; i < floats.length; i++) {
127 | List list = lines.get(i);
128 | if (replace && position < list.size())
129 | list.set(position, floats[i]);
130 | else
131 | list.add(position, floats[i]);
132 | }
133 | }
134 |
135 | @Override
136 | public Float getLinePoint(int index, int position) {
137 | return lines.get(index).get(position);
138 | }
139 |
140 | @Override
141 | public int getLineSize() {
142 | return colors.size();
143 | }
144 |
145 | @Override
146 | public float getMax(int index) {
147 | float rst = Float.MIN_VALUE;
148 | for (int i = 0; i < getLineSize(); i++) {
149 | Float f = getLinePoint(i, index);
150 | if (f != null)
151 | rst = Math.max(rst, f);
152 | }
153 | return data.isEmpty() ? rst : Math.max(rst, data.get(index).getMax());
154 | }
155 |
156 | @Override
157 | public float getMin(int index) {
158 | float rst = Float.MAX_VALUE;
159 | for (int i = 0; i < getLineSize(); i++) {
160 | Float f = getLinePoint(i, index);
161 | if (f != null)
162 | rst = Math.min(rst, f);
163 | }
164 | return data.isEmpty() ? rst : Math.min(rst, data.get(index).getMin());
165 | }
166 |
167 | @Override
168 | public void clear() {
169 | data.clear();
170 | colors.clear();
171 | labels.clear();
172 | lines.clear();
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/huobi/draw/ChildDraw.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.huobi.draw;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Path;
5 | import android.graphics.PointF;
6 | import android.support.annotation.NonNull;
7 | import android.text.TextUtils;
8 |
9 | import com.github.wyang.klinechartlib.R;
10 | import com.github.wyang.klinechartlib.data.ICandle;
11 | import com.github.wyang.klinechartlib.data.IMacd;
12 | import com.github.wyang.klinechartlib.data.IVolume;
13 | import com.github.wyang.klinechartlib.huobi.KLineChartAdapter;
14 | import com.github.wyang.klinechartlib.huobi.KLineChartView;
15 | import com.github.wyang.klinechartlib.huobi.helper.LinePathHelper;
16 | import com.github.wyang.klinechartlib.huobi.interfaces.IData;
17 | import com.github.wyang.klinechartlib.huobi.interfaces.IDataLineSet;
18 | import com.github.wyang.klinechartlib.utils.PointFPool;
19 |
20 | import java.util.List;
21 |
22 | /**
23 | * Created by fxb on 2019-11-04.
24 | */
25 | public class ChildDraw extends ChartDraw {
26 |
27 | public ChildDraw(KLineChartView chart, LinePathHelper helper) {
28 | super(chart, helper);
29 | }
30 |
31 | @Override
32 | public void draw(@NonNull Canvas canvas) {
33 | KLineChartAdapter mAdapter = mChart.getAdapter();
34 | IDataLineSet dataLineSet = mAdapter.getDataLineSet(name);
35 | if (dataLineSet == null)
36 | return;
37 | mHelper.save(this, dataLineSet);
38 |
39 | int startIndex = mChart.getStartIndex();
40 | int endIndex = mChart.getEndIndex();
41 |
42 | //生成绘制线的Path并绘制特殊图形(蜡烛图、Sar圆、成交量、macd……)
43 | for (int i = startIndex; i <= endIndex; i++) {
44 | mHelper.move(i);
45 |
46 | if (dataLineSet.getDataCount() == 0)
47 | continue;
48 |
49 | IData data = dataLineSet.getData(i);
50 |
51 | float x = getAxisX(i);
52 | if (data instanceof ICandle) {
53 | ICandle candle = (ICandle) data;
54 | float[] arrY = new float[4];
55 | arrY[0] = getAxisY(candle.getOpen());
56 | arrY[1] = getAxisY(candle.getClose());
57 | arrY[2] = getAxisY(candle.getHigh());
58 | arrY[3] = getAxisY(candle.getLow());
59 | mChart.drawAmericanLine(canvas, x, arrY);
60 | }
61 | if (data instanceof IVolume) {
62 | IVolume volume = (IVolume) data;
63 | float y = getAxisY(volume.getVolume());
64 | mChart.drawCandle(canvas, x, y, getAxisY(0), mAdapter.isIncrease(i));
65 | }
66 | if (data instanceof IMacd) {
67 | IMacd macd = (IMacd) data;
68 | float y = getAxisY(macd.getMacd());
69 | mChart.drawCandle(canvas, x, y, getAxisY(0), mAdapter.isIncrease(i));
70 | }
71 | }
72 |
73 | //绘制指标线
74 | List paths = mHelper.getPaths();
75 | for (int i = 0; i < paths.size(); i++) {
76 | mChart.drawLinePath(canvas, paths.get(i), dataLineSet.getLineColor(i));
77 | }
78 | mHelper.restore();
79 |
80 | int selectedIndex = mChart.getSelectedIndex();
81 | String text;
82 | PointF p = PointFPool.get(mChart.getAxisTextPadding(), getTop());
83 | //绘制指标名称
84 | if (dataLineSet.isShowName()) {
85 | text = dataLineSet.getName();
86 |
87 | mChart.drawText(canvas, text, p, mChart.getColor(R.color.chart_text));
88 |
89 | p.x += mChart.getTextPaint().measureText(text + " ");
90 | p.y = getTop();
91 | }
92 |
93 | //绘制各项指标值
94 | if (dataLineSet.getDataCount() != 0 && !TextUtils.isEmpty(dataLineSet.getDataLabel())) {
95 | IData data = dataLineSet.getData(selectedIndex == -1 ? dataLineSet.getDataCount() - 1 : selectedIndex);
96 |
97 | text = dataLineSet.getDataLabel() + mChart.getPriceFormatter().format(data.getMax());
98 |
99 | mChart.drawText(canvas, text, p, mChart.getColor(R.color.chart_text));
100 |
101 | p.x += mChart.getTextPaint().measureText(text + " ");
102 | p.y = getTop();
103 | }
104 | for (int i = 0; i < dataLineSet.getLineSize(); i++) {
105 | Float rst = dataLineSet.getLinePoint(i, selectedIndex == -1 ? mAdapter.getCount() - 1 : selectedIndex);
106 | if (rst == null)
107 | continue;
108 |
109 | text = dataLineSet.getLabel(i) + mChart.getPriceFormatter().format(rst);
110 | mChart.drawText(canvas, text, p, dataLineSet.getLineColor(i));
111 |
112 | p.x += mChart.getTextPaint().measureText(text + " ");
113 | p.y = getTop();
114 | }
115 | PointFPool.recycle(p);
116 | }
117 |
118 | @Override
119 | public void calcMinMax(int position, boolean isReset) {
120 | if (isReset) {
121 | maxValue = Float.MIN_VALUE;
122 | minValue = Float.MAX_VALUE;
123 | }
124 | IDataLineSet dataLineSet = mChart.getAdapter().getDataLineSet(name);
125 | if (dataLineSet != null) {
126 | maxValue = Math.max(dataLineSet.getMax(position), maxValue);
127 | minValue = Math.min(dataLineSet.getMin(position), minValue);
128 | } else {
129 | maxValue = Float.MIN_VALUE;
130 | minValue = Float.MAX_VALUE;
131 | }
132 | }
133 |
134 | @Override
135 | public void fixMaxMin(float diff) {
136 | float minimum = (maxValue - minValue) / (getMinAxisY() - getMaxAxisY() - diff);
137 | minValue -= minimum * diff;
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/huobi/helper/LinePathHelper.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.huobi.helper;
2 |
3 | import android.graphics.Path;
4 | import android.graphics.PointF;
5 | import android.support.annotation.NonNull;
6 |
7 | import com.github.wyang.klinechartlib.data.ICandle;
8 | import com.github.wyang.klinechartlib.huobi.interfaces.IDataLineSet;
9 | import com.github.wyang.klinechartlib.huobi.draw.IChartDraw;
10 | import com.github.wyang.klinechartlib.utils.ObjectPool;
11 | import com.github.wyang.klinechartlib.utils.PointFPool;
12 |
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 | /**
17 | * Created by fxb on 2019-12-03.
18 | * 用于绘制指标线的辅助类
19 | */
20 | public class LinePathHelper {
21 | private static ObjectPool pathPool;
22 |
23 | static {
24 | pathPool = new ObjectPool(16, 8) {
25 | @Override
26 | public Path createObject() {
27 | return new Path();
28 | }
29 | };
30 | }
31 |
32 | private Path getPath() {
33 | return pathPool.get();
34 | }
35 |
36 | private void recycle() {
37 | pathPool.recycle(path);
38 | PointFPool.recycle(p1);
39 | PointFPool.recycle(p2);
40 |
41 | pathPool.recycle(paths);
42 | PointFPool.recycle(control1Points);
43 | PointFPool.recycle(control2Points);
44 |
45 | path = null;
46 | p1 = null;
47 | p2 = null;
48 | paths.clear();
49 | control1Points.clear();
50 | control2Points.clear();
51 | }
52 |
53 | private IChartDraw iChartDraw;
54 | private IDataLineSet iLineSet;
55 |
56 | /**
57 | * 主图收盘线
58 | */
59 | private Path path;
60 | private PointF p1;
61 | private PointF p2;
62 |
63 | /**
64 | * 主图or子图 指标线
65 | */
66 | private List paths = new ArrayList<>();
67 | private List control1Points = new ArrayList<>();
68 | private List control2Points = new ArrayList<>();
69 |
70 | public void save(@NonNull IChartDraw iChartDraw, IDataLineSet iLineSet) {
71 | this.iChartDraw = iChartDraw;
72 | this.iLineSet = iLineSet;
73 | if (iLineSet != null) {
74 | if (iLineSet.getDataCount() != 0) {
75 | Object o = iLineSet.getData(0);
76 | if (o instanceof ICandle) {
77 | path = getPath();
78 | path.reset();
79 |
80 | p1 = PointFPool.get(0, 0);
81 | p2 = PointFPool.get(0, 0);
82 | }
83 | }
84 |
85 | for (int i = 0; i < iLineSet.getLineSize(); i++) {
86 | Path path = getPath();
87 | path.reset();
88 | paths.add(path);
89 |
90 | control1Points.add(PointFPool.get(0, 0));
91 | control2Points.add(PointFPool.get(0, 0));
92 | }
93 | }
94 | }
95 |
96 | public void restore() {
97 | recycle();
98 | }
99 |
100 | public Path getLinePath() {
101 | return path;
102 | }
103 |
104 | public List getPaths() {
105 | return paths;
106 | }
107 |
108 | /**
109 | * 绘制曲线的平滑度
110 | */
111 | protected float SMOOTHNESS = 0.2f;
112 |
113 | public void move(int position) {
114 | if (iLineSet == null)
115 | return;
116 | if (iLineSet.getDataCount() != 0) {
117 | Object o = iLineSet.getData(position);
118 | if (o instanceof ICandle) {
119 | ICandle candle = (ICandle) o;
120 |
121 | float x = iChartDraw.getAxisX(position);
122 | float y = iChartDraw.getAxisY(candle.getClose());
123 |
124 | if (path.isEmpty()) {
125 | path.moveTo(x, y);
126 | //更新控制点1
127 | p1.set(x, y);
128 | } else if (position == iLineSet.getDataCount() - 1) {
129 | float lastX = iChartDraw.getAxisX(position - 1);
130 | p2.x = x - (x - lastX) * SMOOTHNESS;
131 | p2.y = y;
132 |
133 | path.cubicTo(p1.x, p1.y, p2.x, p2.y, x, y);
134 | } else {
135 | ICandle last = iLineSet.getData(position - 1);
136 | ICandle next = iLineSet.getData(position + 1);
137 | float lastX = iChartDraw.getAxisX(position - 1);
138 | float lastY = iChartDraw.getAxisY(last.getClose());
139 |
140 | float nextX = iChartDraw.getAxisX(position + 1);
141 | float nextY = iChartDraw.getAxisY(next.getClose());
142 |
143 | float k = (nextY - lastY) / (nextX - lastX);
144 | float b = y - k * x;
145 |
146 | //更新控制点2
147 | p2.x = x - (x - lastX) * SMOOTHNESS;
148 | p2.y = k * p2.x + b;
149 |
150 | path.cubicTo(p1.x, p1.y, p2.x, p2.y, x, y);
151 |
152 | //更新控制点1
153 | p1.x = x + (nextX - x) * SMOOTHNESS;
154 | p1.y = k * p1.x + b;
155 | }
156 | }
157 | }
158 | for (int i = 0; i < iLineSet.getLineSize(); i++) {
159 | Float rst = iLineSet.getLinePoint(i, position);
160 | Path path = paths.get(i);
161 |
162 | if (rst == null)
163 | continue;
164 |
165 | PointF f1 = control1Points.get(i);
166 | PointF f2 = control2Points.get(i);
167 |
168 | float x = iChartDraw.getAxisX(position);
169 | float y = iChartDraw.getAxisY(rst);
170 |
171 | if (path.isEmpty()) {
172 | path.moveTo(x, y);
173 | //更新控制点1
174 | f1.set(x, y);
175 | } else if (position == iLineSet.getCount() - 1) {
176 | float lastX = iChartDraw.getAxisX(position - 1);
177 | f2.x = x - (x - lastX) * SMOOTHNESS;
178 | f2.y = y;
179 |
180 | path.cubicTo(f1.x, f1.y, f2.x, f2.y, x, y);
181 | } else {
182 | float lastX = iChartDraw.getAxisX(position - 1);
183 | float lastY = iChartDraw.getAxisY(iLineSet.getLinePoint(i, position - 1));
184 |
185 | float nextX = iChartDraw.getAxisX(position + 1);
186 | float nextY = iChartDraw.getAxisY(iLineSet.getLinePoint(i, position + 1));
187 |
188 | float k = (nextY - lastY) / (nextX - lastX);
189 | float b = y - k * x;
190 |
191 | //更新控制点2
192 | f2.x = x - (x - lastX) * SMOOTHNESS;
193 | f2.y = k * f2.x + b;
194 |
195 | path.cubicTo(f1.x, f1.y, f2.x, f2.y, x, y);
196 |
197 | //更新控制点1
198 | f1.x = x + (nextX - x) * SMOOTHNESS;
199 | f1.y = k * f1.x + b;
200 | }
201 | }
202 | }
203 | }
204 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/wyang/klinechartdemo/KLineChartLandActivity.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartdemo;
2 |
3 | import android.content.Context;
4 | import android.content.pm.ActivityInfo;
5 | import android.os.Bundle;
6 | import android.support.annotation.Nullable;
7 | import android.support.design.widget.TabLayout;
8 | import android.support.v7.app.AppCompatActivity;
9 | import android.view.View;
10 | import android.view.Window;
11 | import android.view.WindowManager;
12 | import android.widget.ImageButton;
13 | import android.widget.RadioGroup;
14 | import android.widget.TextView;
15 |
16 | import com.github.wyang.klinechartdemo.utils.AssetUtil;
17 | import com.github.wyang.klinechartlib.huobi.KLineChartAdapter;
18 | import com.github.wyang.klinechartlib.huobi.KLineChartView;
19 | import com.github.wyang.klinechartlib.huobi.data.KLineEntity;
20 | import com.github.wyang.klinechartlib.huobi.data.DataLineSetProvider;
21 | import com.github.wyang.klinechartlib.huobi.draw.MainDraw;
22 |
23 | import org.json.JSONArray;
24 |
25 | import java.util.ArrayList;
26 | import java.util.List;
27 |
28 | /**
29 | * Created by fxb on 2019-12-05.
30 | */
31 | public class KLineChartLandActivity extends AppCompatActivity {
32 | private TextView tv_code;
33 |
34 | private TabLayout mTabLayout;
35 | private KLineChartView mKLineChartView;
36 | private KLineChartAdapter mAdapter;
37 |
38 | private RadioGroup rg_main;
39 | private RadioGroup rg_sub;
40 |
41 | private ImageButton btn_main_hide;
42 | private ImageButton btn_sub_hide;
43 |
44 | @Override
45 | protected void onCreate(@Nullable Bundle savedInstanceState) {
46 | super.onCreate(savedInstanceState);
47 | //设置竖屏Activity
48 | setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
49 |
50 | requestWindowFeature(Window.FEATURE_NO_TITLE);
51 |
52 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
53 | WindowManager.LayoutParams.FLAG_FULLSCREEN);
54 | setContentView(R.layout.activity_k_line_land);
55 |
56 | mTabLayout = findViewById(R.id.mTabLayout);
57 | mTabLayout.addTab(mTabLayout.newTab().setText(R.string.k_tab1));
58 | mTabLayout.addTab(mTabLayout.newTab().setText(R.string.k_tab2));
59 | mTabLayout.addTab(mTabLayout.newTab().setText(R.string.k_tab3));
60 | mTabLayout.addTab(mTabLayout.newTab().setText(R.string.k_tab4));
61 | mTabLayout.addTab(mTabLayout.newTab().setText(R.string.k_tab5));
62 | mTabLayout.addTab(mTabLayout.newTab().setText(R.string.k_tab6));
63 | mTabLayout.addTab(mTabLayout.newTab().setText(R.string.k_tab7));
64 | mTabLayout.addTab(mTabLayout.newTab().setText(R.string.k_tab8));
65 | mTabLayout.addTab(mTabLayout.newTab().setText(R.string.k_tab9));
66 | mTabLayout.addTab(mTabLayout.newTab().setText(R.string.k_tab10));
67 | mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
68 | @Override
69 | public void onTabSelected(TabLayout.Tab tab) {
70 | if (tab.getPosition() == 0)
71 | mKLineChartView.setMode(MainDraw.Mode.LINE);
72 | else
73 | mKLineChartView.setMode(MainDraw.Mode.CANDLE);
74 |
75 | }
76 |
77 | @Override
78 | public void onTabUnselected(TabLayout.Tab tab) {
79 |
80 | }
81 |
82 | @Override
83 | public void onTabReselected(TabLayout.Tab tab) {
84 |
85 | }
86 | });
87 |
88 | mKLineChartView = findViewById(R.id.mKLineChartView);
89 | mKLineChartView.setBottomAxisX(false);
90 | mKLineChartView.setDrawGridStartEnd(true);
91 |
92 | mAdapter = new KLineChartAdapter();
93 | mAdapter.bindToKLineChartView(mKLineChartView);
94 | mAdapter.bindToDataLineSetProvider(new DataLineSetProvider());
95 |
96 | initKLineData(this);
97 |
98 | rg_main = findViewById(R.id.rg_main);
99 | rg_sub = findViewById(R.id.rg_sub);
100 |
101 | btn_main_hide = findViewById(R.id.btn_main_hide);
102 | btn_sub_hide = findViewById(R.id.btn_sub_hide);
103 |
104 | rg_main.setOnCheckedChangeListener((group, checkedId) -> {
105 | btn_main_hide.setSelected(checkedId != View.NO_ID);
106 | switch (checkedId) {
107 | case R.id.rb_ma:
108 | mKLineChartView.setMainSelected("ma");
109 | break;
110 | case R.id.rb_ema:
111 | mKLineChartView.setMainSelected("ema");
112 | break;
113 | case R.id.rb_boll:
114 | mKLineChartView.setMainSelected("boll");
115 | break;
116 | default:
117 | mKLineChartView.setMainSelected("");
118 | break;
119 | }
120 | });
121 | rg_main.check(R.id.rb_ma);
122 |
123 | rg_sub.setOnCheckedChangeListener((group, checkedId) -> {
124 | btn_sub_hide.setSelected(checkedId != View.NO_ID);
125 | mKLineChartView.setShowChild2(checkedId != View.NO_ID);
126 |
127 | switch (checkedId) {
128 | case R.id.rb_macd:
129 | mKLineChartView.setChild2Selected("macd");
130 | break;
131 | case R.id.rb_kdj:
132 | mKLineChartView.setChild2Selected("kdj");
133 | break;
134 | case R.id.rb_rsi:
135 | mKLineChartView.setChild2Selected("rsi");
136 | break;
137 | case R.id.rb_wr:
138 | mKLineChartView.setChild2Selected("wr");
139 | break;
140 | case R.id.rb_boll_child:
141 | mKLineChartView.setChild2Selected("boll");
142 | break;
143 | default:
144 | break;
145 | }
146 | });
147 | rg_sub.check(R.id.rb_macd);
148 | }
149 |
150 | public void onClose(View v) {
151 | finish();
152 | }
153 |
154 | public void onHideMain(View view) {
155 | rg_main.check(View.NO_ID);
156 | }
157 |
158 | public void onHideSub(View view) {
159 | rg_sub.check(View.NO_ID);
160 | }
161 |
162 | public void onSetting(View view) {
163 | }
164 |
165 |
166 | private void initKLineData(Context context) {
167 | try {
168 | String json = AssetUtil.readAsset(context, "test.json");
169 | JSONArray jsonArray = new JSONArray(json);
170 |
171 | List list = new ArrayList<>();
172 | for (int i = 0; i < jsonArray.length(); i++) {
173 | JSONArray arr = jsonArray.getJSONArray(i);
174 |
175 | KLineEntity entity = new KLineEntity();
176 | entity.open = (float) arr.getDouble(0);
177 | entity.close = (float) arr.getDouble(1);
178 | entity.high = (float) arr.getDouble(2);
179 | entity.low = (float) arr.getDouble(3);
180 | entity.time = arr.getLong(4);
181 | entity.vol = (float) arr.getDouble(5);
182 |
183 | list.add(entity);
184 | }
185 |
186 | mAdapter.setNewData(list);
187 |
188 | mKLineChartView.setMainSelected("ma");
189 | mKLineChartView.setChild1Selected("volume");
190 | mKLineChartView.setChild2Selected("macd");
191 | } catch (Exception ignored) {
192 | }
193 |
194 | }
195 |
196 | }
197 |
--------------------------------------------------------------------------------
/app/src/main/assets/little2.json:
--------------------------------------------------------------------------------
1 | [
2 | [
3 | 7302.78,
4 | 7297.45,
5 | 7304.97,
6 | 7287.73,
7 | 1575303000000,
8 | 121.542
9 | ],
10 | [
11 | 7298.66,
12 | 7297.72,
13 | 7307.24,
14 | 7294.99,
15 | 1575303060000,
16 | 22.8154
17 | ],
18 | [
19 | 7297.71,
20 | 7293.79,
21 | 7297.71,
22 | 7286.80,
23 | 1575303120000,
24 | 18.5118
25 | ],
26 | [
27 | 7293.78,
28 | 7303.27,
29 | 7304.31,
30 | 7293.12,
31 | 1575303180000,
32 | 10.6001
33 | ],
34 | [
35 | 7303.94,
36 | 7315.14,
37 | 7318.23,
38 | 7303.94,
39 | 1575303240000,
40 | 18.2516
41 | ],
42 | [
43 | 7314.40,
44 | 7321.74,
45 | 7326.82,
46 | 7313.20,
47 | 1575303300000,
48 | 17.6498
49 | ],
50 | [
51 | 7324.57,
52 | 7312.64,
53 | 7325.98,
54 | 7311.63,
55 | 1575303360000,
56 | 4.71672
57 | ],
58 | [
59 | 7311.82,
60 | 7308.40,
61 | 7311.82,
62 | 7302.73,
63 | 1575303420000,
64 | 3.14229
65 | ],
66 | [
67 | 7308.09,
68 | 7303.16,
69 | 7311.73,
70 | 7303.08,
71 | 1575303480000,
72 | 4.42419
73 | ],
74 | [
75 | 7303.14,
76 | 7307.00,
77 | 7307.04,
78 | 7299.97,
79 | 1575303540000,
80 | 4.54998
81 | ],
82 | [
83 | 7307.04,
84 | 7307.95,
85 | 7311.55,
86 | 7305.47,
87 | 1575303600000,
88 | 5.30718
89 | ],
90 | [
91 | 7307.22,
92 | 7308.24,
93 | 7309.64,
94 | 7304.68,
95 | 1575303660000,
96 | 1.47813
97 | ],
98 | [
99 | 7306.80,
100 | 7304.81,
101 | 7311.79,
102 | 7304.80,
103 | 1575303720000,
104 | 3.9934
105 | ],
106 | [
107 | 7304.80,
108 | 7294.09,
109 | 7304.81,
110 | 7292.01,
111 | 1575303780000,
112 | 19.334
113 | ],
114 | [
115 | 7295.78,
116 | 7291.94,
117 | 7301.80,
118 | 7290.87,
119 | 1575303840000,
120 | 7.08857
121 | ],
122 | [
123 | 7291.49,
124 | 7303.33,
125 | 7303.42,
126 | 7288.63,
127 | 1575303900000,
128 | 4.6531
129 | ],
130 | [
131 | 7302.97,
132 | 7298.92,
133 | 7302.97,
134 | 7296.80,
135 | 1575303960000,
136 | 4.59234
137 | ],
138 | [
139 | 7297.84,
140 | 7300.48,
141 | 7300.48,
142 | 7293.26,
143 | 1575304020000,
144 | 4.85822
145 | ],
146 | [
147 | 7301.40,
148 | 7300.78,
149 | 7304.80,
150 | 7299.30,
151 | 1575304080000,
152 | 3.77003
153 | ],
154 | [
155 | 7299.99,
156 | 7297.91,
157 | 7301.80,
158 | 7296.80,
159 | 1575304140000,
160 | 1.70624
161 | ],
162 | [
163 | 7296.74,
164 | 7295.78,
165 | 7297.53,
166 | 7293.67,
167 | 1575304200000,
168 | 1.24033
169 | ],
170 | [
171 | 7295.52,
172 | 7285.05,
173 | 7295.52,
174 | 7281.96,
175 | 1575304260000,
176 | 10.5466
177 | ],
178 | [
179 | 7284.02,
180 | 7284.89,
181 | 7291.72,
182 | 7281.80,
183 | 1575304320000,
184 | 13.5388
185 | ],
186 | [
187 | 7283.64,
188 | 7284.53,
189 | 7285.22,
190 | 7277.58,
191 | 1575304380000,
192 | 8.14867
193 | ],
194 | [
195 | 7285.22,
196 | 7293.15,
197 | 7293.15,
198 | 7282.31,
199 | 1575304440000,
200 | 6.44772
201 | ],
202 | [
203 | 7292.51,
204 | 7299.97,
205 | 7301.72,
206 | 7292.38,
207 | 1575304500000,
208 | 6.08902
209 | ],
210 | [
211 | 7300.04,
212 | 7291.78,
213 | 7301.72,
214 | 7290.13,
215 | 1575304560000,
216 | 6.22454
217 | ],
218 | [
219 | 7290.12,
220 | 7286.47,
221 | 7291.80,
222 | 7284.09,
223 | 1575304620000,
224 | 6.18076
225 | ],
226 | [
227 | 7286.42,
228 | 7292.70,
229 | 7293.54,
230 | 7284.52,
231 | 1575304680000,
232 | 14.0331
233 | ],
234 | [
235 | 7292.34,
236 | 7288.74,
237 | 7292.68,
238 | 7280.10,
239 | 1575304740000,
240 | 16.5069
241 | ],
242 | [
243 | 7287.83,
244 | 7285.23,
245 | 7291.46,
246 | 7285.20,
247 | 1575304800000,
248 | 4.95303
249 | ],
250 | [
251 | 7285.30,
252 | 7292.17,
253 | 7292.36,
254 | 7281.80,
255 | 1575304860000,
256 | 24.0774
257 | ],
258 | [
259 | 7292.38,
260 | 7294.45,
261 | 7296.80,
262 | 7292.25,
263 | 1575304920000,
264 | 6.12603
265 | ],
266 | [
267 | 7292.58,
268 | 7293.26,
269 | 7293.28,
270 | 7285.85,
271 | 1575304980000,
272 | 4.67842
273 | ],
274 | [
275 | 7291.78,
276 | 7294.50,
277 | 7295.71,
278 | 7290.99,
279 | 1575305040000,
280 | 2.29064
281 | ],
282 | [
283 | 7294.51,
284 | 7291.82,
285 | 7295.77,
286 | 7291.80,
287 | 1575305100000,
288 | 3.6707
289 | ],
290 | [
291 | 7291.81,
292 | 7287.55,
293 | 7291.81,
294 | 7286.86,
295 | 1575305160000,
296 | 2.78885
297 | ],
298 | [
299 | 7287.56,
300 | 7287.90,
301 | 7288.78,
302 | 7282.46,
303 | 1575305220000,
304 | 3.65304
305 | ],
306 | [
307 | 7287.91,
308 | 7291.90,
309 | 7295.09,
310 | 7284.13,
311 | 1575305280000,
312 | 17.7612
313 | ],
314 | [
315 | 7292.07,
316 | 7290.59,
317 | 7295.77,
318 | 7289.40,
319 | 1575305340000,
320 | 2.91658
321 | ],
322 | [
323 | 7290.14,
324 | 7295.63,
325 | 7295.79,
326 | 7290.05,
327 | 1575305400000,
328 | 2.99363
329 | ],
330 | [
331 | 7295.78,
332 | 7294.53,
333 | 7295.80,
334 | 7288.70,
335 | 1575305460000,
336 | 2.87446
337 | ],
338 | [
339 | 7294.16,
340 | 7295.60,
341 | 7296.79,
342 | 7290.69,
343 | 1575305520000,
344 | 13.8412
345 | ],
346 | [
347 | 7295.61,
348 | 7296.35,
349 | 7296.80,
350 | 7294.51,
351 | 1575305580000,
352 | 2.33056
353 | ],
354 | [
355 | 7296.36,
356 | 7285.73,
357 | 7296.36,
358 | 7284.57,
359 | 1575305640000,
360 | 7.48369
361 | ],
362 | [
363 | 7285.08,
364 | 7261.92,
365 | 7285.40,
366 | 7261.15,
367 | 1575305700000,
368 | 65.8993
369 | ],
370 | [
371 | 7263.66,
372 | 7288.73,
373 | 7291.25,
374 | 7262.11,
375 | 1575305760000,
376 | 31.9917
377 | ],
378 | [
379 | 7288.39,
380 | 7280.59,
381 | 7294.40,
382 | 7279.21,
383 | 1575305820000,
384 | 10.0481
385 | ],
386 | [
387 | 7280.58,
388 | 7289.17,
389 | 7291.20,
390 | 7279.91,
391 | 1575305880000,
392 | 5.45214
393 | ],
394 | [
395 | 7289.09,
396 | 7286.23,
397 | 7290.60,
398 | 7282.87,
399 | 1575305940000,
400 | 6.33588
401 | ],
402 | [
403 | 7286.21,
404 | 7275.43,
405 | 7288.66,
406 | 7272.50,
407 | 1575306000000,
408 | 6.02968
409 | ],
410 | [
411 | 7278.52,
412 | 7282.48,
413 | 7284.82,
414 | 7272.89,
415 | 1575306060000,
416 | 15.4614
417 | ],
418 | [
419 | 7285.57,
420 | 7279.24,
421 | 7286.80,
422 | 7276.83,
423 | 1575306120000,
424 | 3.94772
425 | ],
426 | [
427 | 7277.49,
428 | 7278.57,
429 | 7280.50,
430 | 7272.13,
431 | 1575306180000,
432 | 2.05759
433 | ],
434 | [
435 | 7279.34,
436 | 7269.29,
437 | 7279.34,
438 | 7268.46,
439 | 1575306240000,
440 | 6.07933
441 | ],
442 | [
443 | 7268.71,
444 | 7279.54,
445 | 7280.73,
446 | 7268.27,
447 | 1575306300000,
448 | 6.0101
449 | ],
450 | [
451 | 7278.19,
452 | 7274.10,
453 | 7280.04,
454 | 7273.92,
455 | 1575306360000,
456 | 4.00904
457 | ],
458 | [
459 | 7274.40,
460 | 7268.77,
461 | 7274.40,
462 | 7268.29,
463 | 1575306420000,
464 | 11.2447
465 | ],
466 | [
467 | 7270.02,
468 | 7265.65,
469 | 7270.02,
470 | 7263.34,
471 | 1575306480000,
472 | 7.39563
473 | ],
474 | [
475 | 7264.83,
476 | 7266.46,
477 | 7266.50,
478 | 7255.80,
479 | 1575306540000,
480 | 46.6691
481 | ]
482 | ]
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/huobi/draw/MainDraw.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.huobi.draw;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.LinearGradient;
5 | import android.graphics.Paint;
6 | import android.graphics.Path;
7 | import android.graphics.PointF;
8 | import android.graphics.Shader;
9 | import android.support.annotation.IntDef;
10 | import android.support.annotation.NonNull;
11 |
12 | import com.github.wyang.klinechartlib.data.ICandle;
13 | import com.github.wyang.klinechartlib.huobi.KLineChartAdapter;
14 | import com.github.wyang.klinechartlib.huobi.KLineChartView;
15 | import com.github.wyang.klinechartlib.huobi.data.KLineEntity;
16 | import com.github.wyang.klinechartlib.huobi.helper.LinePathHelper;
17 | import com.github.wyang.klinechartlib.huobi.interfaces.IDataLineSet;
18 | import com.github.wyang.klinechartlib.utils.PointFPool;
19 |
20 | import java.lang.annotation.Retention;
21 | import java.lang.annotation.RetentionPolicy;
22 | import java.util.List;
23 |
24 | /**
25 | * Created by fxb on 2019-11-04.
26 | * 最下面为收盘线or蜡烛图,支持均线,boll线...
27 | */
28 | public class MainDraw extends ChartDraw {
29 |
30 | @IntDef({Mode.LINE, Mode.CANDLE})
31 | @Retention(RetentionPolicy.SOURCE)
32 | public @interface Mode {
33 | int LINE = 1;//收盘线
34 | int CANDLE = 2;//蜡烛图
35 | }
36 |
37 | @Mode
38 | private int mode = Mode.LINE;
39 |
40 | /**
41 | * 当前可见最高价
42 | */
43 | private float maxPrice;
44 | /**
45 | * 当前可见最低价
46 | */
47 | private float minPrice;
48 | private int mMaxValueIndex;
49 | private int mMinValueIndex;
50 |
51 | public MainDraw(KLineChartView chart, LinePathHelper helper) {
52 | super(chart, helper);
53 | }
54 |
55 | @Override
56 | public void draw(@NonNull Canvas canvas) {
57 | KLineChartAdapter mAdapter = mChart.getAdapter();
58 | IDataLineSet dataLineSet = mAdapter.getDataLineSet(name);
59 |
60 | mHelper.save(this, dataLineSet);
61 |
62 | int startIndex = mChart.getStartIndex();
63 | int endIndex = mChart.getEndIndex();
64 |
65 | for (int i = startIndex; i <= endIndex; i++) {
66 | ICandle data = mAdapter.getData(i).getCandle();
67 |
68 | if (isCandle()) {
69 | float x = getAxisX(i);
70 |
71 | float high = getAxisY(data.getHigh());
72 | float low = getAxisY(data.getLow());
73 | float open = getAxisY(data.getOpen());
74 | float close = getAxisY(data.getClose());
75 |
76 | if (open > close) {//涨(这里比较的y坐标值)
77 | mChart.drawCandle(canvas, x, open, close, true);
78 | mChart.drawCandleLine(canvas, x, high, close);
79 | mChart.drawCandleLine(canvas, x, open, low);
80 | } else if (open < close) {
81 | mChart.drawCandle(canvas, x, close, open, false);
82 | mChart.drawCandleLine(canvas, x, high, open);
83 | mChart.drawCandleLine(canvas, x, close, low);
84 | } else {
85 | mChart.drawCandle(canvas, x, close - 1, close, true);
86 | mChart.drawCandleLine(canvas, x, high, low);
87 | }
88 | }
89 | mHelper.move(i);
90 | }
91 |
92 | //先画线#5776AD
93 | if (isLine()) {
94 | Path linePath = mHelper.getLinePath();
95 | mChart.drawLinePath(canvas, linePath, 0xFF5776AD);
96 | LinearGradient shader = new LinearGradient(0, 0, 0, getMinAxisY(), 0x335776AD, 0x005776AD, Shader.TileMode.CLAMP);
97 |
98 | linePath.lineTo(getAxisX(endIndex), getMinAxisY());
99 | linePath.lineTo(getAxisX(startIndex), getMinAxisY());
100 | linePath.close();
101 | mChart.drawFillPath(canvas, linePath, 0xffff0828, shader);
102 | } else {
103 | List paths = mHelper.getPaths();
104 | for (int i = 0; i < paths.size(); i++) {
105 | mChart.drawLinePath(canvas, paths.get(i), dataLineSet.getLineColor(i));
106 | }
107 |
108 | if (dataLineSet != null) {
109 | int selectedIndex = mChart.getSelectedIndex();
110 | String text;
111 | PointF p = PointFPool.get(mChart.getAxisTextPadding(), mChart.getAxisTextPadding());
112 | for (int i = 0; i < dataLineSet.getLineSize(); i++) {
113 | Float rst = dataLineSet.getLinePoint(i, selectedIndex == -1 ? mAdapter.getCount() - 1 : selectedIndex);
114 | if (rst == null)
115 | continue;
116 |
117 | text = dataLineSet.getLabel(i) + mChart.getPriceFormatter().format(rst);
118 | float x = p.x + mChart.getTextPaint().measureText(text + " ");
119 | if (x > mChart.getWidth() - mChart.getAxisTextPadding()) {
120 | p.x = mChart.getAxisTextPadding();
121 | p.y += mChart.getDefaultTextHeight();
122 | }
123 | mChart.drawText(canvas, text, p, dataLineSet.getLineColor(i));
124 | if (p.x < mChart.getWidth() - mChart.getAxisTextPadding())
125 | p.x += mChart.getTextPaint().measureText(text + " ");
126 |
127 | }
128 | PointFPool.recycle(p);
129 | }
130 | }
131 | mHelper.restore();
132 |
133 | //绘制最大最小值
134 | if (!isLine()) {
135 | Paint mHighLightPaint = mChart.getHighLightPaint();
136 | //画最小值
137 | KLineEntity entity = mAdapter.getData(mMinValueIndex);
138 | String text;
139 |
140 | PointF p = PointFPool.get(0, 0);
141 | p.x = getAxisX(mMinValueIndex);
142 | p.y = getAxisY(entity.low);
143 | if (p.x < mChart.getWidth() / 2) {
144 | text = "─ " + mChart.getPriceFormatter().format(entity.low);
145 | //画右边
146 | mChart.getTextDrawHelper().drawPointRight(canvas, text, p, mHighLightPaint);
147 | } else {
148 | //画左边
149 | text = mChart.getPriceFormatter().format(entity.low) + " ─";
150 | mChart.getTextDrawHelper().drawPointLeft(canvas, text, p, mHighLightPaint);
151 | }
152 |
153 | //画最大值
154 | entity = mAdapter.getData(mMaxValueIndex);
155 | p.x = getAxisX(mMaxValueIndex);
156 | p.y = getAxisY(entity.high);
157 |
158 | if (p.x < mChart.getWidth() / 2) {
159 | text = "─ " + mChart.getPriceFormatter().format(entity.high);
160 | //画右边
161 | mChart.getTextDrawHelper().drawPointRight(canvas, text, p, mHighLightPaint);
162 | } else {
163 | //画左边
164 | text = mChart.getPriceFormatter().format(entity.high) + " ─";
165 | mChart.getTextDrawHelper().drawPointLeft(canvas, text, p, mHighLightPaint);
166 | }
167 |
168 | PointFPool.recycle(p);
169 | }
170 | }
171 |
172 | @Override
173 | public void calcMinMax(int position, boolean isReset) {
174 | if (isReset) {
175 | maxValue = maxPrice = Float.MIN_VALUE;
176 | minValue = minPrice = Float.MAX_VALUE;
177 | }
178 | ICandle data = mChart.getAdapter().getData(position).getCandle();
179 | if (isLine()) {
180 | maxValue = Math.max(data.getClose(), maxValue);
181 | minValue = Math.min(data.getClose(), minValue);
182 | } else {
183 | IDataLineSet dataLineSet = mChart.getAdapter().getDataLineSet(name);
184 | if (dataLineSet != null) {
185 | maxValue = Math.max(dataLineSet.getMax(position), maxValue);
186 | minValue = Math.min(dataLineSet.getMin(position), minValue);
187 | }
188 |
189 | maxPrice = Math.max(data.getHigh(), maxPrice);
190 | minPrice = Math.min(data.getLow(), minPrice);
191 | if (maxPrice == data.getHigh())
192 | mMaxValueIndex = position;
193 | if (minPrice == data.getLow())
194 | mMinValueIndex = position;
195 | }
196 |
197 | float latestPrice = mChart.getAdapter().getLatestPrice();
198 |
199 | maxValue = Math.max(latestPrice, maxValue);
200 | minValue = Math.min(latestPrice, minValue);
201 | }
202 |
203 | @Override
204 | public void fixMaxMin(float diff) {
205 | float minimum = (maxValue - minValue) / (getMinAxisY() - getMaxAxisY() - diff);
206 | maxValue += minimum * diff / 2;
207 | minValue -= minimum * diff / 2;
208 | }
209 |
210 | public void setMode(@Mode int mode) {
211 | this.mode = mode;
212 | }
213 |
214 | public boolean isLine() {
215 | return mode == Mode.LINE;
216 | }
217 |
218 | public boolean isCandle() {
219 | return mode == Mode.CANDLE;
220 | }
221 | }
222 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/wyang/klinechartdemo/KLineChartActivity.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartdemo;
2 |
3 | import android.animation.ValueAnimator;
4 | import android.content.Intent;
5 | import android.content.pm.ActivityInfo;
6 | import android.os.Bundle;
7 | import android.os.Handler;
8 | import android.support.annotation.ColorRes;
9 | import android.support.annotation.Nullable;
10 | import android.support.design.widget.TabLayout;
11 | import android.support.v4.view.ViewCompat;
12 | import android.support.v7.app.AppCompatActivity;
13 | import android.support.v7.widget.RecyclerView;
14 | import android.util.Log;
15 | import android.view.View;
16 | import android.view.ViewGroup;
17 | import android.view.animation.AccelerateDecelerateInterpolator;
18 | import android.widget.ImageView;
19 | import android.widget.TextView;
20 |
21 | import com.chad.library.adapter.base.BaseQuickAdapter;
22 | import com.chad.library.adapter.base.BaseViewHolder;
23 | import com.github.wyang.klinechartdemo.utils.AssetUtil;
24 | import com.github.wyang.klinechartdemo.widget.CommonPopupWindow;
25 | import com.github.wyang.klinechartlib.huobi.KLineChartAdapter;
26 | import com.github.wyang.klinechartlib.huobi.KLineChartView;
27 | import com.github.wyang.klinechartlib.huobi.data.DataLineSetProvider;
28 | import com.github.wyang.klinechartlib.huobi.data.KLineEntity;
29 | import com.github.wyang.klinechartlib.huobi.draw.MainDraw;
30 |
31 | import org.json.JSONArray;
32 |
33 | import java.util.ArrayList;
34 | import java.util.List;
35 |
36 | /**
37 | * Created by fxb on 2019-11-01.
38 | */
39 | public class KLineChartActivity extends AppCompatActivity {
40 | private TextView tv_title;
41 | private TabLayout mTabLayout;
42 | private KLineChartView mKLineChartView;
43 | private KLineChartAdapter mAdapter;
44 |
45 | private int pos;
46 | private TextView tv_buy, tv_sell;
47 | private List data;
48 |
49 | @Override
50 | protected void onCreate(@Nullable Bundle savedInstanceState) {
51 | super.onCreate(savedInstanceState);
52 | //设置竖屏Activity
53 | setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
54 | setContentView(R.layout.activity_k_line);
55 |
56 | tv_title = findViewById(R.id.tv_title);
57 | mTabLayout = findViewById(R.id.mTabLayout);
58 | mKLineChartView = findViewById(R.id.mKLineChartView);
59 | tv_buy = findViewById(R.id.tv_buy);
60 | tv_sell = findViewById(R.id.tv_sell);
61 |
62 | tv_title.setText("BTC/USDT");
63 |
64 | //设置买卖按钮颜色
65 | ViewCompat.setBackgroundTintList(tv_buy,
66 | getResources().getColorStateList(R.color.chart_red));
67 | ViewCompat.setBackgroundTintList(tv_sell,
68 | getResources().getColorStateList(R.color.chart_green));
69 | mTabLayout.addTab(mTabLayout.newTab().setText(getString(R.string.k_line_tab01)));
70 | mTabLayout.addTab(mTabLayout.newTab().setText(getString(R.string.k_line_tab02)));
71 | mTabLayout.addTab(mTabLayout.newTab().setText(getString(R.string.k_line_tab03)));
72 | mTabLayout.addTab(mTabLayout.newTab().setText(getString(R.string.k_line_tab04)));
73 | mTabLayout.addTab(mTabLayout.newTab().setText(getString(R.string.k_line_tab05)));
74 |
75 | //添加选择更多Tab
76 | mTabLayout.addTab(mTabLayout.newTab().setCustomView(R.layout.item_tab_k_line_more));
77 | TabLayout.Tab tab5 = mTabLayout.getTabAt(posMore);
78 | if (tab5 != null && tab5.getCustomView() != null) {
79 | tab5.getCustomView().setOnClickListener(v -> onClickMore());
80 | tv_label = tab5.getCustomView().findViewById(R.id.tv_label);
81 | iv_label = tab5.getCustomView().findViewById(R.id.iv_label);
82 | }
83 | //添加设置指标线Tab
84 | mTabLayout.addTab(mTabLayout.newTab().setCustomView(R.layout.item_tab_k_line_setting));
85 | TabLayout.Tab tab6 = mTabLayout.getTabAt(posSetting);
86 | if (tab6 != null && tab6.getCustomView() != null) {
87 | tab6.getCustomView().setOnClickListener(v -> onClickSetting());
88 | iv_setting = tab6.getCustomView().findViewById(R.id.iv_label);
89 | }
90 |
91 | mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
92 | @Override
93 | public void onTabSelected(TabLayout.Tab tab) {
94 | if (tab.getPosition() < posMore) {
95 | pos = tab.getPosition();
96 | } else if (tab.getPosition() == posMore) {
97 | setTabMoreColor(R.color.theme0xff);
98 | }
99 | if (tab.getPosition() == 0) {
100 | mKLineChartView.setMode(MainDraw.Mode.LINE);
101 | } else {
102 | mKLineChartView.setMode(MainDraw.Mode.CANDLE);
103 | }
104 | }
105 |
106 | @Override
107 | public void onTabUnselected(TabLayout.Tab tab) {
108 | if (tab.getPosition() == posMore) {
109 | tv_label.setText(R.string.k_line_tab06);
110 | setTabMoreColor(R.color.light);
111 | }
112 | }
113 |
114 | @Override
115 | public void onTabReselected(TabLayout.Tab tab) {
116 |
117 | }
118 | });
119 | TabLayout.Tab tab = mTabLayout.getTabAt(0);
120 | if (tab != null)
121 | tab.select();
122 |
123 |
124 | //mKLineChartView.setCandleFill(false);
125 | mKLineChartView.setOnRefreshListener(new KLineChartView.OnRefreshListener() {
126 | @Override
127 | public void onRefresh() {
128 | new Handler().postDelayed(() -> mKLineChartView.refreshComplete(), 2000);
129 | }
130 |
131 | @Override
132 | public void onLoadMore() {
133 | new Handler().postDelayed(() -> {
134 | List kLineData = getKLineData("little2.json");
135 | mAdapter.addData(kLineData);
136 |
137 | mKLineChartView.refreshComplete();
138 | }, 2000);
139 | }
140 | });
141 |
142 | mAdapter = new KLineChartAdapter();
143 | mAdapter.bindToKLineChartView(mKLineChartView);
144 | mAdapter.bindToDataLineSetProvider(new DataLineSetProvider());
145 |
146 | data = getKLineData("little.json");
147 | mAdapter.setNewData(data);
148 | handler.postDelayed(runnable, 3000);
149 |
150 | mKLineChartView.setMainSelected("ma");
151 | mKLineChartView.setChild1Selected("volume");
152 | mKLineChartView.setChild2Selected("wr");
153 | mKLineChartView.setMode(MainDraw.Mode.LINE);
154 | }
155 |
156 | private Handler handler = new Handler();
157 |
158 | private Runnable runnable = this::updateLast;
159 |
160 | private int i = 0;
161 |
162 | private void sendNewData() {
163 | if (i == data.size())
164 | i = 0;
165 | KLineEntity entity = data.get(i++);
166 | mAdapter.addData(entity, false);
167 |
168 | handler.postDelayed(runnable, 3000);
169 | }
170 |
171 | private void updateLast() {
172 | KLineEntity entity = data.get(data.size() - 1);
173 | float newClose = (float) (entity.open + (Math.random() * 50 - 25));
174 |
175 | float oldClose = entity.close;
176 | float diff = newClose - oldClose;
177 | ValueAnimator animator = ValueAnimator.ofFloat(1f);
178 | animator.setDuration(500);
179 | animator.setInterpolator(new AccelerateDecelerateInterpolator());
180 | animator.addUpdateListener(animation -> {
181 | float value = (float) animation.getAnimatedValue();
182 | entity.update(oldClose + value * diff);
183 | mAdapter.addData(entity, true);
184 | });
185 | animator.start();
186 |
187 | Log.e("KLine", "最新收盘价=" + entity.close);
188 | handler.postDelayed(runnable, 3000);
189 | }
190 |
191 | public void onBack(View view) {
192 | finish();
193 | }
194 |
195 | private TextView tv_label;
196 | private ImageView iv_label;
197 | private int posMore = 5;
198 |
199 | private void setTabMoreColor(@ColorRes int resId) {
200 | tv_label.setTextColor(getResources().getColor(resId));
201 | ViewCompat.setBackgroundTintList(iv_label, getResources().getColorStateList(resId));
202 | }
203 |
204 | public void onClickMore() {
205 | }
206 |
207 | private ImageView iv_setting;
208 | private int posSetting = 6;
209 | // 主图指标下标
210 | private int mainIndex = 0;
211 | // 副图指标下标
212 | private int subIndex = -1;
213 |
214 | private void setTabSettingColor(@ColorRes int resId) {
215 | ViewCompat.setBackgroundTintList(iv_setting, getResources().getColorStateList(resId));
216 | }
217 |
218 | private ItemTabAdapter mainItemTabAdapter = new ItemTabAdapter();
219 | private ItemTabAdapter subItemTabAdapter = new ItemTabAdapter();
220 |
221 | public void onClickSetting() {
222 | CommonPopupWindow popupWindow = new CommonPopupWindow.Builder(this)
223 | .setView(R.layout.popup_setting)
224 | .setOutsideTouchable(true)
225 | .setWidthAndHeight(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
226 | .setViewOnclickListener((view, layoutResId) -> {
227 | RecyclerView rv_main = view.findViewById(R.id.rv_main);
228 | rv_main.setAdapter(mainItemTabAdapter);
229 | RecyclerView rv_sub = view.findViewById(R.id.rv_sub);
230 | rv_sub.setAdapter(subItemTabAdapter);
231 | })
232 | .create();
233 | popupWindow.showAsDropDown(mTabLayout);
234 | }
235 |
236 | private List getKLineData(String filename) {
237 | List list = new ArrayList<>();
238 |
239 | try {
240 | String json = AssetUtil.readAsset(this, filename);
241 | JSONArray jsonArray = new JSONArray(json);
242 |
243 | for (int i = 0; i < jsonArray.length(); i++) {
244 | JSONArray arr = jsonArray.getJSONArray(i);
245 |
246 | KLineEntity entity = new KLineEntity();
247 | entity.open = (float) arr.getDouble(0);
248 | entity.close = (float) arr.getDouble(1);
249 | entity.high = (float) arr.getDouble(2);
250 | entity.low = (float) arr.getDouble(3);
251 | entity.time = arr.getLong(4);
252 | entity.vol = (float) arr.getDouble(5);
253 |
254 | list.add(entity);
255 | }
256 |
257 | } catch (Exception ignored) {
258 | }
259 | return list;
260 | }
261 |
262 | public void onLandscape(View view) {
263 | startActivity(new Intent(this, KLineChartLandActivity.class));
264 | }
265 |
266 | public static class ItemTabAdapter extends BaseQuickAdapter {
267 | private int selectPosition = 0;
268 |
269 | ItemTabAdapter() {
270 | super(R.layout.item_tab);
271 | }
272 |
273 | @Override
274 | protected void convert(BaseViewHolder helper, String item) {
275 | helper.setText(R.id.tv_label, item);
276 | helper.getView(R.id.tv_label).setSelected(selectPosition == helper.getAdapterPosition());
277 | }
278 | }
279 | }
280 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_k_line.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
24 |
25 |
33 |
34 |
45 |
46 |
47 |
51 |
52 |
56 |
57 |
64 |
65 |
75 |
76 |
85 |
86 |
97 |
98 |
105 |
106 |
112 |
113 |
120 |
121 |
128 |
129 |
130 |
138 |
139 |
146 |
147 |
155 |
156 |
164 |
165 |
166 |
167 |
171 |
172 |
186 |
187 |
193 |
202 |
203 |
204 |
205 |
209 |
210 |
213 |
214 |
215 |
216 |
225 |
226 |
237 |
238 |
241 |
242 |
253 |
254 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_k_line_land.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
20 |
21 |
34 |
35 |
47 |
48 |
59 |
60 |
71 |
72 |
73 |
78 |
79 |
85 |
86 |
93 |
94 |
100 |
101 |
108 |
109 |
115 |
116 |
123 |
124 |
134 |
135 |
136 |
137 |
146 |
147 |
155 |
156 |
161 |
162 |
171 |
172 |
181 |
182 |
191 |
192 |
193 |
201 |
202 |
206 |
207 |
215 |
216 |
221 |
222 |
231 |
232 |
241 |
242 |
251 |
252 |
261 |
262 |
271 |
272 |
273 |
281 |
282 |
286 |
287 |
296 |
297 |
298 |
311 |
312 |
318 |
319 |
333 |
334 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/base/BaseChartView.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.base;
2 |
3 | import android.content.Context;
4 | import android.database.DataSetObserver;
5 | import android.support.annotation.ColorRes;
6 | import android.support.annotation.DimenRes;
7 | import android.support.annotation.Nullable;
8 | import android.support.annotation.StringRes;
9 | import android.support.v4.content.ContextCompat;
10 | import android.support.v4.view.GestureDetectorCompat;
11 | import android.util.AttributeSet;
12 | import android.util.Log;
13 | import android.util.TypedValue;
14 | import android.view.GestureDetector;
15 | import android.view.MotionEvent;
16 | import android.view.ScaleGestureDetector;
17 | import android.view.ViewGroup;
18 | import android.widget.OverScroller;
19 |
20 | /**
21 | * Created by fxb on 2019-11-01.
22 | * K线图View基类,处理测量逻辑,滑动缩放逻辑,及数据适配器和长按选中哪一项,具体绘制交由子类
23 | */
24 | public abstract class BaseChartView extends ViewGroup implements
25 | GestureDetector.OnGestureListener, ScaleGestureDetector.OnScaleGestureListener {
26 | protected final String TAG = getClass().getSimpleName();
27 | protected GestureDetectorCompat mGestureDetector;
28 | protected ScaleGestureDetector mScaleDetector;
29 | private OverScroller mScroller;
30 |
31 | /**
32 | * mScroller当前x轴的滑动量
33 | */
34 | protected int mScrollX;
35 |
36 | protected float mOverScrollRange;//图标右方后可滑出距离
37 |
38 | protected float mOffsetX = Float.MIN_VALUE;//第一个数据相对屏幕最左侧的偏移量,与mScrollX相关
39 |
40 | protected float mScaleX = 1;//缩放大小
41 |
42 | protected int mWidth;
43 | protected int mHeight;
44 | private float aspectRatio = 0.95f;//宽高比
45 |
46 | private boolean isTouch = false;//触摸动作的标记
47 | private boolean isLongPress = false;//长按的标记
48 | private boolean mMultipleTouch = false;//多点触摸的标记
49 |
50 | private boolean mScrollEnable = true;//是否能够滚动
51 | private boolean mScaleEnable = true;//是否能够缩放
52 |
53 | private T mAdapter;
54 | protected float mPointWidth;//当mScaleX=1时,数据占据屏幕的宽
55 | protected float mDataLength;//数据宽度总和
56 |
57 | protected int mSelectedIndex;//长按选中索引
58 | protected int mStartIndex;//可见数据起始索引
59 | protected int mEndIndex;//可见数据结束索引
60 |
61 | private DataSetObserver mDataSetObserver = new DataSetObserver() {
62 | @Override
63 | public void onChanged() {
64 | notifyScrollChanged();
65 | }
66 |
67 | @Override
68 | public void onInvalidated() {
69 | notifyInvalidated();
70 | }
71 | };
72 |
73 | public BaseChartView(Context context) {
74 | this(context, null);
75 | }
76 |
77 | public BaseChartView(Context context, @Nullable AttributeSet attrs) {
78 | this(context, attrs, 0);
79 | }
80 |
81 | public BaseChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
82 | super(context, attrs, defStyleAttr);
83 | init();
84 | }
85 |
86 | private void init() {
87 | setWillNotDraw(false);
88 | setLayerType(LAYER_TYPE_SOFTWARE, null);//硬件加速,否则虚线绘制成实线
89 |
90 | mGestureDetector = new GestureDetectorCompat(getContext(), this);
91 | mScaleDetector = new ScaleGestureDetector(getContext(), this);
92 | mScroller = new OverScroller(getContext());
93 | }
94 |
95 | public float getDimension(@DimenRes int resId) {
96 | return getResources().getDimension(resId);
97 | }
98 |
99 | public int getColor(@ColorRes int resId) {
100 | return ContextCompat.getColor(getContext(), resId);
101 | }
102 |
103 | @Override
104 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
105 | mWidth = MeasureSpec.getSize(widthMeasureSpec);
106 | mHeight = (int) (mWidth * aspectRatio);
107 | int specMode = MeasureSpec.getMode(heightMeasureSpec);
108 | int specSize = MeasureSpec.getSize(heightMeasureSpec);
109 | switch (specMode) {
110 | case MeasureSpec.AT_MOST:
111 | break;
112 | case MeasureSpec.EXACTLY:
113 | mHeight = specSize;
114 | break;
115 | case MeasureSpec.UNSPECIFIED:
116 | break;
117 | }
118 | setMeasuredDimension(mWidth, mHeight);
119 | }
120 |
121 | private float downX;
122 |
123 | /**
124 | * 处理触摸逻辑
125 | */
126 | @Override
127 | public boolean onTouchEvent(MotionEvent event) {
128 | //多点触摸
129 | mMultipleTouch = event.getPointerCount() > 1;
130 | if (mMultipleTouch)
131 | isLongPress = false;
132 |
133 | switch (event.getAction() & MotionEvent.ACTION_MASK) {
134 | case MotionEvent.ACTION_DOWN:
135 | isTouch = true;
136 | downX = event.getX();
137 | break;
138 | case MotionEvent.ACTION_MOVE:
139 | if (isLongPress)
140 | onLongPress(event);
141 | break;
142 | case MotionEvent.ACTION_POINTER_UP:
143 | invalidate();
144 | break;
145 | case MotionEvent.ACTION_UP:
146 | if (downX == event.getX() && isLongPress) {
147 | isLongPress = false;
148 | }
149 | isTouch = false;
150 | invalidate();
151 | break;
152 | case MotionEvent.ACTION_CANCEL:
153 | isLongPress = false;
154 | isTouch = false;
155 | invalidate();
156 | break;
157 | default:
158 | break;
159 | }
160 | mGestureDetector.onTouchEvent(event);
161 | mScaleDetector.onTouchEvent(event);
162 | return true;
163 | }
164 |
165 | /**
166 | * 手势监听回调的6个方法
167 | */
168 | @Override
169 | public boolean onDown(MotionEvent e) {
170 | return false;
171 | }
172 |
173 | @Override
174 | public void onShowPress(MotionEvent e) {
175 |
176 | }
177 |
178 | @Override
179 | public boolean onSingleTapUp(MotionEvent e) {
180 | return false;
181 | }
182 |
183 | @Override
184 | public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
185 | if (!isLongPress && !mMultipleTouch) {
186 | scrollBy(Math.round(distanceX), 0);
187 | return true;
188 | }
189 | return false;
190 | }
191 |
192 | @Override
193 | public void onLongPress(MotionEvent e) {
194 | isLongPress = true;
195 |
196 | //长按选中点index计算,并回调
197 | int lastIndex = mSelectedIndex;
198 |
199 | mSelectedIndex = indexOfX(drawX2X(e.getX()));
200 | if (mSelectedIndex < mStartIndex) {
201 | mSelectedIndex = mStartIndex;
202 | }
203 | if (mSelectedIndex > mEndIndex) {
204 | mSelectedIndex = mEndIndex;
205 | }
206 |
207 | if (lastIndex != mSelectedIndex) {
208 | if (onSelectedChangedListener != null) {
209 | onSelectedChangedListener.onSelectedChanged(this, mSelectedIndex);
210 | }
211 | }
212 |
213 | invalidate();
214 | }
215 |
216 | @Override
217 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
218 | if (!isTouch && !isLongPress() && isScrollEnable()) {
219 | mScroller.fling(mScrollX, 0, Math.round(-velocityX),
220 | 0, Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 0);
221 | }
222 | return true;
223 | }
224 |
225 | /**
226 | * 缩放手势回调的3个方法
227 | */
228 | @Override
229 | public boolean onScale(ScaleGestureDetector detector) {
230 | if (!isScaleEnable())
231 | return false;
232 |
233 | float oldScaleX = mScaleX;
234 | mScaleX *= detector.getScaleFactor();
235 | if (mScaleX < getMinScaleX()) {
236 | mScaleX = getMinScaleX();
237 | } else if (mScaleX > getMaxScaleX()) {
238 | mScaleX = getMaxScaleX();
239 | } else {
240 | onScaleChanged(oldScaleX);
241 | }
242 | notifyScaleChange(oldScaleX);
243 |
244 | return true;
245 | }
246 |
247 | @Override
248 | public boolean onScaleBegin(ScaleGestureDetector detector) {
249 | return true;
250 | }
251 |
252 | @Override
253 | public void onScaleEnd(ScaleGestureDetector detector) {
254 |
255 | }
256 |
257 | /**
258 | * 处理滑动逻辑
259 | */
260 | @Override
261 | public void computeScroll() {
262 | if (mScroller.computeScrollOffset()) {
263 | if (!isTouch) {
264 | scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
265 |
266 | //这一行代码不加,惯性滑动时看不到滑动效果
267 | postInvalidate();
268 | } else {
269 | mScroller.forceFinished(true);
270 | }
271 | }
272 | }
273 |
274 | @Override
275 | public void scrollTo(int x, int y) {
276 | if (!isScrollEnable()) {
277 | mScroller.forceFinished(true);
278 | return;
279 | }
280 | int oldX = mScrollX;
281 | mScrollX = x;
282 |
283 | notifyScrollChanged();
284 | /*
285 | log("mScrollX=" + mScrollX);
286 |
287 | mOffsetX += mScrollX - oldX;
288 |
289 | checkOffsetX();*/
290 | }
291 |
292 | @Override
293 | public void scrollBy(int x, int y) {
294 | scrollTo(mScrollX + Math.round(x), 0);
295 | }
296 |
297 | /**
298 | * 缩放比例变化
299 | */
300 | protected void onScaleChanged(float oldScale) {
301 | }
302 |
303 | /**
304 | * 更新mScrollX
305 | */
306 | private void checkOffsetX() {
307 | if (mOffsetX > getMaxOffsetX()) {
308 | mOffsetX = getMaxOffsetX();
309 | onRightSide();
310 | }
311 | if (mOffsetX < getMinOffsetX()) {
312 | mOffsetX = getMinOffsetX();
313 | onLeftSide();
314 | }
315 |
316 | if (mScrollX > getMaxScrollX()) {
317 | mScrollX = getMaxScrollX();
318 | }
319 | if (mScrollX < getMinScrollX()) {
320 | mScrollX = getMinScrollX();
321 | }
322 |
323 | invalidate();
324 | }
325 |
326 | protected float getMaxScaleX() {
327 | return 2f;
328 | }
329 |
330 | protected float getMinScaleX() {
331 | return .5f;
332 | }
333 |
334 | protected float getMinOffsetX() {
335 | return 0;
336 | }
337 |
338 | protected float getMaxOffsetX() {
339 | return mDataLength - mWidth + mOverScrollRange;
340 | }
341 |
342 | private int getMinScrollX() {
343 | return (int) (getMinOffsetX() - getMaxOffsetX());
344 | }
345 |
346 | private int getMaxScrollX() {
347 | return 0;
348 | }
349 |
350 | /**
351 | * 设置超出右方后可滑动的范围
352 | */
353 | public void setOverScrollRange(float overScrollRange) {
354 | if (overScrollRange < 0) {
355 | overScrollRange = 0;
356 | }
357 | mOverScrollRange = overScrollRange;
358 | }
359 |
360 | public void onLeftSide() {
361 | log("滑动至最左侧");
362 | }
363 |
364 | public void onRightSide() {
365 | log("滑动至最右侧");
366 | }
367 |
368 | public boolean isScrollEnable() {
369 | return mScrollEnable;
370 | }
371 |
372 | public void setScrollEnable(boolean mScrollEnable) {
373 | this.mScrollEnable = mScrollEnable;
374 | }
375 |
376 | public boolean isScaleEnable() {
377 | return mScaleEnable;
378 | }
379 |
380 | public void setScaleEnable(boolean mScaleEnable) {
381 | this.mScaleEnable = mScaleEnable;
382 | }
383 |
384 | public boolean isLongPress() {
385 | return isLongPress;
386 | }
387 |
388 | public int getSelectedIndex() {
389 | return mSelectedIndex;
390 | }
391 |
392 | public int getStartIndex() {
393 | return mStartIndex;
394 | }
395 |
396 | public int getEndIndex() {
397 | return mEndIndex;
398 | }
399 |
400 | public boolean isLastVisible() {
401 | return mEndIndex == getAdapter().getCount() - 1;
402 | }
403 |
404 | public void setAdapter(T mAdapter) {
405 | if (this.mAdapter != null && mDataSetObserver != null) {
406 | this.mAdapter.unregisterDataSetObserver(mDataSetObserver);
407 | }
408 | this.mAdapter = mAdapter;
409 | if (mAdapter != null && mDataSetObserver != null) {
410 | mAdapter.registerDataSetObserver(mDataSetObserver);
411 | }
412 | }
413 |
414 | public T getAdapter() {
415 | if (mAdapter == null) {
416 | throw new NullPointerException("You must set an adapter for BaseChartView!");
417 | }
418 | return mAdapter;
419 | }
420 |
421 | /**
422 | * 什么时候调用?
423 | * 1.第一次设置数据
424 | * 2.分页添加历史数据(List中向最前添加)
425 | * 3.新增柱子(List中向末尾添加)
426 | */
427 | private void notifyScrollChanged() {
428 | //设置数据并测量完成
429 | if (getAdapter().getCount() != 0 && mWidth != 0) {
430 | mDataLength = mAdapter.getCount() * mPointWidth * mScaleX;
431 |
432 | mOffsetX = getMaxOffsetX() + mScrollX;
433 |
434 | checkOffsetX();
435 | }
436 | }
437 |
438 | /**
439 | * 手势缩放时调用
440 | */
441 | private void notifyScaleChange(float oldScaleX) {
442 | if (getAdapter().getCount() != 0 && mWidth != 0) {
443 | mDataLength = getAdapter().getCount() * mPointWidth * mScaleX;
444 |
445 | mOffsetX = (mOffsetX + mWidth * 0.5f) * mScaleX / oldScaleX - mWidth * 0.5f;
446 | mScrollX = (int) mOffsetX + getMinScrollX();
447 |
448 | checkOffsetX();
449 | }
450 | }
451 |
452 | /**
453 | * 最后一根柱子更新时调用
454 | */
455 | private void notifyInvalidated() {
456 |
457 | invalidate();
458 | }
459 |
460 | public void cancelSelect() {
461 | isLongPress = false;
462 | invalidate();
463 | }
464 |
465 | public float drawX2X(float x) {
466 | return mOffsetX + x;
467 | }
468 |
469 | public int indexOfX(float x) {
470 | return indexOfX(x, 0, getAdapter().getCount() - 1);
471 | }
472 |
473 | /**
474 | * 二分查找当前值的index
475 | */
476 | public int indexOfX(float x, int start, int end) {
477 | if (end == start) {
478 | return start;
479 | }
480 | if (end - start == 1) {
481 | float startX = getX(start);
482 | float endX = getX(end);
483 | return Math.abs(x - startX) < Math.abs(x - endX) ? start : end;
484 | }
485 | int mid = start + (end - start) / 2;
486 | float midX = getX(mid);
487 | if (x < midX) {
488 | return indexOfX(x, start, mid);
489 | } else if (x > midX) {
490 | return indexOfX(x, mid, end);
491 | } else {
492 | return mid;
493 | }
494 | }
495 |
496 | /**
497 | * mOffsetX==0时,每个数据相对坐标原点的x值
498 | */
499 | private float getX(int position) {
500 | return (position + 0.5f) * mPointWidth * mScaleX;
501 | }
502 |
503 | public float getDrawX(int position) {
504 | return getX(position) - mOffsetX;
505 | }
506 |
507 | public void setPointWidth(float mPointWidth) {
508 | this.mPointWidth = mPointWidth;
509 | }
510 |
511 | private OnSelectedChangedListener onSelectedChangedListener;
512 |
513 | public void setOnSelectedChangedListener(OnSelectedChangedListener onSelectedChangedListener) {
514 | this.onSelectedChangedListener = onSelectedChangedListener;
515 | }
516 |
517 | /**
518 | * 选中点变化时的监听
519 | */
520 | public interface OnSelectedChangedListener {
521 | /**
522 | * 当选点中变化时
523 | *
524 | * @param view 当前view
525 | * @param index 选中点的索引
526 | */
527 | void onSelectedChanged(BaseChartView view, int index);
528 | }
529 |
530 | public void log(String text) {
531 | Log.e(TAG, text);
532 | }
533 |
534 | protected float dp2px(float dpValue) {
535 | return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, getResources().getDisplayMetrics());
536 | }
537 |
538 | protected String getString(@StringRes int resId) {
539 | return getContext().getString(resId);
540 | }
541 | }
542 |
--------------------------------------------------------------------------------
/klinechartlib/src/main/java/com/github/wyang/klinechartlib/huobi/data/DataLineSetProvider.java:
--------------------------------------------------------------------------------
1 | package com.github.wyang.klinechartlib.huobi.data;
2 |
3 | import android.annotation.SuppressLint;
4 |
5 | import com.github.wyang.klinechartlib.huobi.interfaces.KLineConstant;
6 | import com.github.wyang.klinechartlib.huobi.interfaces.IDataLineSet;
7 | import com.github.wyang.klinechartlib.huobi.interfaces.IDataLineSetProvider;
8 |
9 | import java.util.HashMap;
10 | import java.util.List;
11 | import java.util.Map;
12 |
13 | /**
14 | * Created by fxb on 2019-12-29.
15 | */
16 | public class DataLineSetProvider implements IDataLineSetProvider, KLineConstant {
17 | private Map dataLineSetMap = new HashMap<>();
18 |
19 | private DataLineSet candleLineSet = new DataLineSet();
20 | private DataLineSet maLineSet = new DataLineSet();
21 | private DataLineSet emaLineSet = new DataLineSet();
22 | private DataLineSet bollLineSet = new DataLineSet();
23 |
24 | private DataLineSet volumeLineSet = new DataLineSet();
25 | private DataLineSet macdLineSet = new DataLineSet();
26 | private DataLineSet kdjLineSet = new DataLineSet();
27 | private DataLineSet rsiLineSet = new DataLineSet();
28 | private DataLineSet wrLineSet = new DataLineSet();
29 |
30 | public DataLineSetProvider() {
31 | resetCandle();
32 | resetMa();
33 | resetEma();
34 | resetBoll();
35 |
36 | resetVol();
37 | resetMacd();
38 | resetKdj();
39 | resetRsi();
40 | resetWr();
41 | }
42 |
43 | @Override
44 | public IDataLineSet get(String name) {
45 | return dataLineSetMap.get(name);
46 | }
47 |
48 | @Override
49 | public void calculateAll(List data) {
50 | resetCandle();
51 | resetMa();
52 | resetEma();
53 | resetBoll();
54 |
55 | resetVol();
56 | resetMacd();
57 | resetKdj();
58 | resetRsi();
59 | resetWr();
60 |
61 | maArr = new float[6];
62 | bollMa = 0f;
63 |
64 | volArr = new float[2];
65 | macdArr = new float[2];
66 |
67 | rsiArr = new float[3][2];
68 |
69 | //计算各项指标
70 | for (int i = 0; i < data.size(); i++) {
71 | calculate(data, i, false, false);
72 | }
73 | }
74 |
75 | @Override
76 | public void calculateLast(List data, boolean replace) {
77 | int end = data.size() - 1;
78 |
79 | int maxN = getMaxN();
80 | if (end < maxN)
81 | calculateAll(data);
82 | else
83 | calculate(data, end, true, replace);
84 | }
85 |
86 |
87 | private float[] maArr;
88 |
89 | private float bollMa;
90 |
91 | private float[] volArr;
92 | private float[] macdArr;
93 |
94 | private float[][] rsiArr;
95 |
96 | private int getMaxN() {
97 | int rst = Integer.MIN_VALUE;
98 | //ma均线
99 | for (int n : MA_N) {
100 | if (n != Integer.MAX_VALUE)
101 | rst = Math.max(rst, n);
102 | }
103 |
104 | rst = Math.max(rst, BOLL_N[0]);
105 |
106 | //成交量均线
107 | for (int n : VOL_N) {
108 | rst = Math.max(rst, n);
109 | }
110 |
111 | for (int n : RSI_N) {
112 | if (n != Integer.MAX_VALUE)
113 | rst = Math.max(rst, n);
114 | }
115 | for (int n : WR_N) {
116 | if (n != Integer.MAX_VALUE)
117 | rst = Math.max(rst, n);
118 | }
119 | return rst;
120 | }
121 |
122 | private void calculate(List data, int i, boolean isUpdateLast, boolean replace) {
123 | calculateCandle(data, i, replace);
124 | calculateMa(data, i, isUpdateLast, replace);
125 | calculateEma(data, i, replace);
126 | calculateBoll(data, i, isUpdateLast, replace);
127 |
128 | calculateVol(data, i, isUpdateLast, replace);
129 | calculateMacd(data, i, replace);
130 | calculateKdj(data, i, replace);
131 |
132 | calculateRri(data, i, replace);
133 | calculateWr(data, i, replace);
134 | }
135 |
136 | /**
137 | * 界面上绘制仅绘制蜡烛图,无指标线
138 | */
139 | private void calculateCandle(List data, int position, boolean replace) {
140 | candleLineSet.addData(replace, position, data.get(position).getCandle());
141 | }
142 |
143 | /**
144 | * 蜡烛图+ma指标
145 | */
146 | private void calculateMa(List data, int position, boolean isUpdateLast, boolean replace) {
147 | KLineEntity entity = data.get(position);
148 |
149 | for (int i = 0; i < MA_N.length; i++) {
150 | int n = MA_N[i];
151 | maArr[i] += entity.close;
152 | if (position >= n && !isUpdateLast)
153 | maArr[i] -= data.get(position - n).close;
154 | }
155 |
156 | maLineSet.addLinePoint(
157 | replace,
158 | position,
159 | position < MA_N[0] - 1 ? null : maArr[0] / MA_N[0],
160 | position < MA_N[1] - 1 ? null : maArr[1] / MA_N[1],
161 | position < MA_N[2] - 1 ? null : maArr[2] / MA_N[2],
162 | position < MA_N[3] - 1 ? null : maArr[3] / MA_N[3],
163 | position < MA_N[4] - 1 ? null : maArr[4] / MA_N[4],
164 | position < MA_N[5] - 1 ? null : maArr[5] / MA_N[5]
165 | );
166 | maLineSet.addData(replace, position, entity.getCandle());
167 |
168 | if (position == data.size() - 1)
169 | for (int i = 0; i < MA_N.length; i++) {
170 | maArr[i] -= entity.close;
171 | }
172 | }
173 |
174 | /**
175 | * 蜡烛图+ema指标
176 | */
177 | private void calculateEma(List data, int position, boolean replace) {
178 | float close = data.get(position).close;
179 |
180 | float ema0;
181 | float ema1;
182 | if (position == 0) {
183 | ema0 = close;
184 | ema1 = close;
185 | } else {
186 | int n0 = EMA_N[0];
187 | int n1 = EMA_N[1];
188 |
189 | Float lastEma0 = emaLineSet.getLinePoint(0, position - 1);
190 | Float lastEma1 = emaLineSet.getLinePoint(1, position - 1);
191 |
192 | ema0 = lastEma0 * (n0 - 1) / (n0 + 1) + close * 2 / (n0 + 1);
193 | ema1 = lastEma1 * (n1 - 1) / (n1 + 1) + close * 2 / (n1 + 1);
194 | }
195 | emaLineSet.addLinePoint(replace, position, ema0, ema1);
196 | emaLineSet.addData(replace, position, data.get(position).getCandle());
197 | }
198 |
199 | /**
200 | * 蜡烛图 + boll指标
201 | */
202 | private void calculateBoll(List data, int position, boolean isUpdateLast, boolean replace) {
203 | bollMa += data.get(position).close;
204 | if (position < BOLL_N[0] - 1) {
205 | bollLineSet.addLinePoint(replace, position, null, null, null);
206 | } else {
207 | if (position >= BOLL_N[0] && !isUpdateLast)
208 | bollMa -= data.get(position - BOLL_N[0]).close;
209 |
210 | float md = 0;
211 | for (int i = position - BOLL_N[0] + 1; i <= position; i++) {
212 | float c = data.get(i).close;
213 | float value = c - bollMa / BOLL_N[0];
214 | md += value * value;
215 | }
216 | md = md / (BOLL_N[0] - 1);
217 | md = (float) Math.sqrt(md);
218 |
219 | float boll = bollMa / BOLL_N[0];
220 | float ub = bollMa / BOLL_N[0] + BOLL_N[1] * md;
221 | float lb = bollMa / BOLL_N[0] - BOLL_N[1] * md;
222 | bollLineSet.addLinePoint(replace, position, boll, ub, lb);
223 | }
224 | bollLineSet.addData(replace, position, data.get(position).getCandle());
225 |
226 | if (position == data.size() - 1)
227 | bollMa -= data.get(position).close;
228 | }
229 |
230 | /**
231 | * 成交量 + 成交量ma指标
232 | */
233 | private void calculateVol(List data, int position, boolean isUpdateLast, boolean replace) {
234 | volArr[0] += data.get(position).vol;
235 | if (position >= VOL_N[0] && !isUpdateLast)
236 | volArr[0] -= data.get(position - VOL_N[0]).vol;
237 |
238 | volArr[1] += data.get(position).vol;
239 | if (position >= VOL_N[1] && !isUpdateLast)
240 | volArr[1] -= data.get(position - VOL_N[1]).vol;
241 |
242 | volumeLineSet.addLinePoint(
243 | replace,
244 | position,
245 | position < VOL_N[0] - 1 ? null : volArr[0] / VOL_N[0],
246 | position < VOL_N[1] - 1 ? null : volArr[1] / VOL_N[1]
247 | );
248 | volumeLineSet.addData(replace, position, data.get(position).getVolume());
249 | if (position == data.size() - 1) {
250 | volArr[0] -= data.get(position).vol;
251 | volArr[1] -= data.get(position).vol;
252 | }
253 | }
254 |
255 | private void calculateMacd(List data, int position, boolean replace) {
256 | int n0 = MACD_N[0];// 12
257 | int n1 = MACD_N[1];// 26
258 | int n2 = MACD_N[2];// 9
259 |
260 | float close = data.get(position).close;
261 |
262 | float[] temp = null;
263 | if (replace) {
264 | temp = new float[2];
265 | System.arraycopy(macdArr, 0, temp, 0, 2);
266 | }
267 | if (position == 0) {
268 | macdArr[0] = close;
269 | macdArr[1] = close;
270 | } else {
271 | macdArr[0] = macdArr[0] * (n0 - 1) / (n0 + 1) + close * 2 / (n0 + 1);
272 | macdArr[1] = macdArr[1] * (n1 - 1) / (n1 + 1) + close * 2 / (n1 + 1);
273 | }
274 |
275 | float dif, dea;
276 | if (position == 0) {
277 | dif = 0f;
278 | dea = 0f;
279 | } else {
280 | dif = macdArr[0] - macdArr[1];
281 |
282 | Float lastDea = macdLineSet.getLinePoint(1, position - 1);
283 | dea = lastDea * (n2 - 1) / (n2 + 1) + dif * 2 / (n2 + 1);
284 | }
285 | macdLineSet.addLinePoint(replace, position, dif, dea);
286 |
287 | Macd macd = new Macd((dif - dea) * 2f);
288 | macdLineSet.addData(replace, position, macd);
289 |
290 | if (replace) {
291 | macdArr = temp;
292 | }
293 | }
294 |
295 | /**
296 | * kdj指标
297 | */
298 | private void calculateKdj(List data, int position, boolean replace) {
299 | if (position < KDJ_N[0] - 1) {
300 | kdjLineSet.addLinePoint(replace, position, null, null, null);
301 | } else {
302 | KLineEntity entity = data.get(position);
303 |
304 | int startIndex = position - KDJ_N[0] + 1;
305 | float maxN = Float.MIN_VALUE;
306 | float minN = Float.MAX_VALUE;
307 | for (int index = startIndex; index <= position; index++) {
308 | maxN = Math.max(maxN, entity.high);
309 | minN = Math.min(minN, entity.low);
310 | }
311 | Float rsv = 100f * (entity.close - minN) / (maxN - minN);
312 | if (rsv.isNaN())
313 | rsv = 0f;
314 |
315 | Float lastK = kdjLineSet.getLinePoint(0, position - 1);
316 | Float lastD = kdjLineSet.getLinePoint(1, position - 1);
317 |
318 | if (lastK == null)
319 | lastK = 50f;
320 | if (lastD == null)
321 | lastD = 50f;
322 |
323 | //TODO 14,1,3 与 9,3,3的差别未解决
324 | Float k = (rsv + 2 * lastK) / 3;
325 | Float d = (k + 2 * lastD) / 3;
326 | Float j = 3 * k - 2 * d;
327 |
328 | if (position <= KDJ_N[0]) {
329 | d = null;
330 | j = null;
331 | }
332 |
333 | kdjLineSet.addLinePoint(replace, position, k, d, j);
334 | }
335 | }
336 |
337 | /**
338 | * rsi指标
339 | */
340 | private void calculateRri(List data, int position, boolean replace) {
341 | KLineEntity entity = data.get(position);
342 | if (position > 0) {
343 | KLineEntity last = data.get(position - 1);
344 | float rsiMax = Math.max(0, entity.close - last.close);
345 | float rsiAbs = Math.abs(entity.close - last.close);
346 |
347 | rsiArr[0][0] = (rsiMax + (RSI_N[0] - 1) * rsiArr[0][0]) / RSI_N[0];
348 | rsiArr[0][1] = (rsiAbs + (RSI_N[0] - 1) * rsiArr[0][1]) / RSI_N[0];
349 |
350 | rsiArr[1][0] = (rsiMax + (RSI_N[1] - 1) * rsiArr[1][0]) / RSI_N[1];
351 | rsiArr[1][1] = (rsiAbs + (RSI_N[1] - 1) * rsiArr[1][1]) / RSI_N[1];
352 |
353 | rsiArr[2][0] = (rsiMax + (RSI_N[2] - 1) * rsiArr[2][0]) / RSI_N[2];
354 | rsiArr[2][1] = (rsiAbs + (RSI_N[2] - 1) * rsiArr[2][1]) / RSI_N[2];
355 | }
356 |
357 |
358 | Float rsi1 = null;
359 | Float rsi2 = null;
360 | Float rsi3 = null;
361 | if (position >= RSI_N[0] - 1) {
362 | rsi1 = 100 * rsiArr[0][0] / rsiArr[0][1];
363 | if (rsi1.isNaN())
364 | rsi1 = 0f;
365 | }
366 | if (position >= RSI_N[1] - 1) {
367 | rsi2 = 100 * rsiArr[1][0] / rsiArr[1][1];
368 | if (rsi2.isNaN())
369 | rsi2 = 0f;
370 | }
371 | if (position >= RSI_N[2] - 1) {
372 | rsi3 = 100 * rsiArr[2][0] / rsiArr[2][1];
373 | if (rsi3.isNaN())
374 | rsi3 = 0f;
375 | }
376 | rsiLineSet.addLinePoint(replace, position, rsi1, rsi2, rsi3);
377 | }
378 |
379 | private void calculateWr(List data, int position, boolean replace) {
380 | KLineEntity entity = data.get(position);
381 |
382 | Float[] wr = new Float[3];
383 | for (int i = 0; i < WR_N.length; i++) {
384 | int startIndex = position - WR_N[i] + 1;
385 | if (startIndex < 0)
386 | startIndex = 0;
387 | float maxN = Float.MIN_VALUE;
388 | float minN = Float.MAX_VALUE;
389 | for (int j = startIndex; j <= position; j++) {
390 | maxN = Math.max(maxN, data.get(j).high);
391 | minN = Math.min(minN, data.get(j).low);
392 | }
393 |
394 | if (WR_N[i] == Integer.MAX_VALUE || position < WR_N[i] - 1)
395 | wr[i] = null;
396 | else
397 | wr[i] = -100 * (maxN - entity.close) / (maxN - minN);
398 |
399 | if (wr[i] != null && wr[i].isNaN())
400 | wr[i] = 0f;
401 | }
402 | wrLineSet.addLinePoint(replace, position, wr[0], wr[1], wr[2]);
403 | }
404 |
405 | private void resetCandle() {
406 | candleLineSet.clear();
407 | dataLineSetMap.put("", candleLineSet);
408 | }
409 |
410 | @SuppressLint("DefaultLocale")
411 | private void resetMa() {
412 | maLineSet.clear();
413 | maLineSet.setName("MA");
414 | for (int i = 0; i < MA_N.length; i++) {
415 | int n = MA_N[i];
416 | maLineSet.addLine(COLORS[i], String.format("MA%d:", n));
417 | }
418 | dataLineSetMap.put("ma", maLineSet);
419 | }
420 |
421 | @SuppressLint("DefaultLocale")
422 | private void resetEma() {
423 | emaLineSet.clear();
424 | emaLineSet.setName("EMA");
425 | emaLineSet.addLine(COLORS[0], String.format("EMA(%d):", EMA_N[0]));
426 | emaLineSet.addLine(COLORS[1], String.format("EMA(%d):", EMA_N[1]));
427 | dataLineSetMap.put("ema", emaLineSet);
428 | }
429 |
430 | private void resetBoll() {
431 | bollLineSet.clear();
432 | bollLineSet.setName("BOLL");
433 | bollLineSet.addLine(COLORS[0], "BOLL:");
434 | bollLineSet.addLine(COLORS[1], "UB:");
435 | bollLineSet.addLine(COLORS[2], "LB:");
436 | dataLineSetMap.put("boll", bollLineSet);
437 | }
438 |
439 | @SuppressLint("DefaultLocale")
440 | private void resetVol() {
441 | volumeLineSet.clear();
442 | volumeLineSet.setName("VOL");
443 | volumeLineSet.setDataLabel("VOL:");
444 | volumeLineSet.addLine(COLORS[0], String.format("MA%d:", VOL_N[0]));
445 | volumeLineSet.addLine(COLORS[1], String.format("MA%d:", VOL_N[1]));
446 | dataLineSetMap.put("volume", volumeLineSet);
447 | }
448 |
449 | @SuppressLint("DefaultLocale")
450 | private void resetMacd() {
451 | macdLineSet.clear();
452 | macdLineSet.setName(String.format(
453 | "MACD(%d,%d,%d)",
454 | MACD_N[0],
455 | MACD_N[1],
456 | MACD_N[2]
457 | ));
458 | macdLineSet.setShowName(true);
459 | macdLineSet.setDataLabel("MACD:");
460 | macdLineSet.addLine(COLORS[0], "DIF:");
461 | macdLineSet.addLine(COLORS[1], "DEA:");
462 | dataLineSetMap.put("macd", macdLineSet);
463 | }
464 |
465 | @SuppressLint("DefaultLocale")
466 | private void resetKdj() {
467 | kdjLineSet.clear();
468 | kdjLineSet.setName(String.format(
469 | "KDJ(%d,%d,%d)",
470 | KDJ_N[0],
471 | KDJ_N[1],
472 | KDJ_N[2]
473 | ));
474 | kdjLineSet.setShowName(true);
475 | kdjLineSet.addLine(COLORS[0], "K:");
476 | kdjLineSet.addLine(COLORS[1], "D:");
477 | kdjLineSet.addLine(COLORS[2], "J:");
478 | dataLineSetMap.put("kdj", kdjLineSet);
479 | }
480 |
481 | @SuppressLint("DefaultLocale")
482 | private void resetRsi() {
483 | rsiLineSet.clear();
484 | rsiLineSet.setName("RSI");
485 | for (int i = 0; i < RSI_N.length; i++) {
486 | int n = RSI_N[i];
487 | rsiLineSet.addLine(COLORS[i], String.format("RSI(%d):", n));
488 | }
489 | dataLineSetMap.put("rsi", rsiLineSet);
490 | }
491 |
492 | @SuppressLint("DefaultLocale")
493 | private void resetWr() {
494 | wrLineSet.clear();
495 | wrLineSet.setName("WR");
496 | for (int i = 0; i < WR_N.length; i++) {
497 | int n = WR_N[i];
498 | wrLineSet.addLine(COLORS[i], String.format("WR(%d):", n));
499 | }
500 | dataLineSetMap.put("wr", wrLineSet);
501 | }
502 | }
503 |
--------------------------------------------------------------------------------