This commit is contained in:
coco
2026-07-03 15:56:07 +08:00
commit caef23209c
5767 changed files with 1004268 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
/build
+144
View File
@@ -0,0 +1,144 @@
apply plugin: 'com.android.application'
/**获取git count HEAD,可以根据该值回退版本*/
def getMyVersionCode() {
Process process = "git rev-list --count HEAD".execute()
process.waitFor()
int cccc = process.getText().toInteger()
return cccc
}
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.tophold.example"
minSdkVersion 17
targetSdkVersion 28
versionCode getMyVersionCode() as int//查询versioncode即可找到对应git历史
versionName "0.0.1"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
signingConfigs {
//debug模式下签名的设置,这里的配置主要用于脚本运行。对于采用图形界面构建测不起效。
debug {
storeFile file(project.debug_storeFile)
storePassword project.debug_storePassword
keyAlias project.debug_keyAlias
keyPassword project.debug_keyPassword
v1SigningEnabled true
v2SigningEnabled true
}
beta {
storeFile file(project.debug_storeFile)
storePassword project.debug_storePassword
keyAlias project.debug_keyAlias
keyPassword project.debug_keyPassword
v1SigningEnabled true
v2SigningEnabled true
}
release {
storeFile file(project.release_storeFile)
storePassword project.release_storePassword
keyAlias project.release_keyAlias
keyPassword project.release_keyPassword
v1SigningEnabled true
v2SigningEnabled true
}
}
buildTypes {
//平时开发使用
debug {
// 显示Log
buildConfigField "boolean", "LOG_DEBUG", "true"
shrinkResources false
minifyEnabled false
zipAlignEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
//versionName的后缀
versionNameSuffix "_debug"
//appId后缀,加一个后缀保证各个版本(debug、beta、release可以共存)
applicationIdSuffix ".debug"
//签名选择
signingConfig signingConfigs.debug
//动态修改应用的名字和图标
manifestPlaceholders = [app_names: "金融View_debug"]
debuggable true
}
//内侧使用
beta {
// 显示Log
buildConfigField "boolean", "LOG_DEBUG", "true"
shrinkResources false
minifyEnabled false
zipAlignEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
//versionName的后缀
versionNameSuffix "_beta"
//appId后缀
applicationIdSuffix ".beta"
//签名选择
signingConfig signingConfigs.beta
manifestPlaceholders = [app_names: "金融View_beta"]
debuggable true
}
//正式版本
release {
//不显示log信息
buildConfigField "boolean", "LOG_DEBUG", "false"
// 移除无用的resource文件
shrinkResources true
//混淆
minifyEnabled true
//压缩
zipAlignEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
//签名
signingConfig signingConfigs.release
manifestPlaceholders = [app_names: "金融View"]
debuggable true
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:support-v4:28.0.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation project(path: ':financiallib')
//rx2.0
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation 'io.reactivex.rxjava2:rxjava:2.2.8'
//api
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'
implementation 'com.squareup.okhttp3:okhttp:3.12.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0'
/**https://github.com/CymChad/BaseRecyclerViewAdapterHelper:qucik recycle*/
implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.49'
implementation 'org.greenrobot:eventbus:3.1.1'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
+287
View File
@@ -0,0 +1,287 @@
###########################正文####################################
##这里是APP默认的混淆配置规则,和第三方没有关系
##这里禁止配置个性化第三方库的混淆配置
##应用第三方库混淆规则配置请配置在最下方"其它第三方"模块
#指定代码的压缩级别
-optimizationpasses 5
#包明不混合大小写
-dontusemixedcaseclassnames
#不去忽略非公共的库类
-dontskipnonpubliclibraryclasses
#优化 不优化输入的类文件
-dontoptimize
#预校验
-dontpreverify
#混淆时是否记录日志
-verbose
# 混淆时所采用的算法
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
#保护注解
-keepattributes *Annotation*
# 保持哪些类不被混淆
-keep public class * extends android.app.Fragment
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService
#如果引用了v4或者v7包
-dontwarn android.support.**
# support design
#@link http://stackoverflow.com/a/31028536
-dontwarn android.support.design.**
-keep class android.support.design.** { *; }
-keep interface android.support.design.** { *; }
-keep public class android.support.design.R$* { *; }
#忽略警告
-ignorewarning
##记录生成的日志数据,gradle build时在本项目根目录输出##
#apk 包内所有 class 的内部结构
-dump proguard/class_files.txt
#未混淆的类和成员
-printseeds proguard/seeds.txt
#列出从 apk 中删除的代码
-printusage proguard/unused.txt
#混淆前后的映射
-printmapping proguard/mapping.txt
#保持 native 方法不被混淆
-keepclasseswithmembernames class * {
native <methods>;
}
#保持自定义控件类不被混淆
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
-keep public class * extends android.view.View {
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
public void set*(...);
}
#保持 Parcelable 不被混淆
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
#保持 Serializable 不被混淆
-keepnames class * implements java.io.Serializable
#保持 Serializable 不被混淆并且enum 类也不被混淆
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient <fields>;
!private <fields>;
!private <methods>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
#保持枚举 enum 类不被混淆
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keepclassmembers class * {
public void *ButtonClicked(android.view.View);
}
#不混淆资源类
-keepclassmembers class **.R$* {
public static <fields>;
}
#避免混淆泛型 如果混淆报错建议关掉
-keepattributes Signature
#移除Log类打印各个等级日志的代码,打正式包的时候可以做为禁log使用,这里可以作为禁止log打印的功能使用,另外的一种实现方案是通过BuildConfig.DEBUG的变量来控制
#-assumenosideeffects class android.util.Log {
# public static *** v(...);
# public static *** i(...);
# public static *** d(...);
# public static *** w(...);
# public static *** e(...);
#}
#################################################必备开发第三方################################
# UI相关
## https://github.com/JakeWharton/butterknife:butterknife
-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }
-keepclasseswithmembernames class * {
@butterknife.* <fields>;
}
-keepclasseswithmembernames class * {
@butterknife.* <methods>;
}
## https://github.com/bumptech/glide:glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
## https://github.com/CymChad/BaseRecyclerViewAdapterHelper:recycle封装
-keep class com.chad.library.adapter.** {
*;
}
-keep public class * extends com.chad.library.adapter.base.BaseQuickAdapter
-keep public class * extends com.chad.library.adapter.base.BaseViewHolder
-keepclassmembers class **$** extends com.chad.library.adapter.base.BaseViewHolder {
<init>(...);
}
## https://github.com/gyf-dev/ImmersionBarw:沉浸式*/
-keep class com.gyf.barlibrary.* {*;}
# 数据相关
## https://github.com/greenrobot/EventBus:EventBus3.0混淆配置
-keepattributes *Annotation*
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# Only required if you use AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
<init>(java.lang.Throwable);
}
## https://github.com/google/gsongson
-keepattributes Signature
# Gson specific classes
-keep class sun.misc.Unsafe { *; }
# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.** { *; }
-keep class com.google.gson.stream.** { *; }
## https://github.com/Blankj/AndroidUtilCode:安卓开发常用工具类
-keep class com.blankj.utilcode.** { *; }
-keepclassmembers class com.blankj.utilcode.** { *; }
-dontwarn com.blankj.utilcode.**
# APP运维
#bugly
-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
# Umeng
-keepclassmembers class * {
public <init> (org.json.JSONObject);
}
-keep public class wgyscsf.quickapp.R$*{
public static final int *;
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
## UmengPush
-dontwarn com.taobao.**
-dontwarn anet.channel.**
-dontwarn anetwork.channel.**
-dontwarn org.android.**
-dontwarn org.apache.thrift.**
-dontwarn com.xiaomi.**
-dontwarn com.huawei.**
-keepattributes *Annotation*
-keep class com.taobao.** {*;}
-keep class org.android.** {*;}
-keep class anet.channel.** {*;}
-keep class com.umeng.** {*;}
-keep class com.xiaomi.** {*;}
-keep class com.huawei.** {*;}
-keep class org.apache.thrift.** {*;}
-keep class com.alibaba.sdk.android.**{*;}
-keep class com.ut.**{*;}
-keep class com.ta.**{*;}
-keep public class **.R$*{
public static final int *;
}
#(可选)避免Log打印输出
#-assumenosideeffects class android.util.Log {
# public static *** v(...);
# public static *** d(...);
# public static *** i(...);
# public static *** w(...);
# }
# rx
-dontwarn javax.annotation.**
-dontwarn javax.inject.**
# OkHttp3
-dontwarn okhttp3.logging.**
-keep class okhttp3.internal.**{*;}
-dontwarn okio.**
# Retrofit
-dontwarn retrofit2.**
-keep class retrofit2.** {*;}
-keepattributes Signature
-keepattributes Exceptions
# RxJava RxAndroid
-dontwarn sun.misc.**
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
long producerIndex;
long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
rx.internal.util.atomic.LinkedQueueNode producerNode;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
rx.internal.util.atomic.LinkedQueueNode consumerNode;
}
#################################################其它第三方########################
@@ -0,0 +1,26 @@
package com.tophold.example;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumentation test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.tophold.example", appContext.getPackageName());
}
}
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.tophold.example">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:name=".MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="${app_names}"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".demo.fund.FundActivity" />
<activity android:name=".demo.kview.beginner.ui.KViewVerticalActivity" />
<activity
android:name=".demo.kview.beginner.ui.KViewHorizontalActivityActivity"
android:screenOrientation="landscape"
android:theme="@style/AppTheme_Fullscreen" />
<activity android:name=".demo.kview.forex.ui.ForexListActivity" />
<activity android:name=".demo.kview.forex.ui.ForexActivity" />
<activity android:name=".demo.kview.btc.ui.HuobiListActivity" />
<activity android:name=".demo.kview.btc.ui.HuobiActivity" />
<activity android:name=".demo.pie.PieChartActivity" />
<!-- <intent-filter> -->
<!-- <action android:name="android.intent.action.MAIN"/> -->
<!-- <category android:name="android.intent.category.LAUNCHER"/> -->
<!-- </intent-filter> -->
<activity android:name=".demo.seekbar.DoubleThumbSeekBarActivity" />
<activity android:name=".demo.kview.KViewActivity"></activity>
</application>
</manifest>
@@ -0,0 +1,202 @@
[
{
"actual": "103",
"createTime": "Apr 10, 2017 12:04:14 AM",
"economicId": 518,
"forecast": "102",
"historyId": 449366,
"previous": "103",
"revised": "",
"timestamp": 1456213500
},
{
"actual": "102",
"createTime": "Apr 10, 2017 12:04:14 AM",
"economicId": 518,
"forecast": "103",
"historyId": 449367,
"previous": "103",
"revised": "101",
"timestamp": 1458805500
},
{
"actual": "105",
"createTime": "Apr 10, 2017 12:04:14 AM",
"economicId": 518,
"forecast": "101",
"historyId": 449368,
"previous": "102",
"revised": "104",
"timestamp": 1461221100
},
{
"actual": "104",
"createTime": "Apr 10, 2017 12:04:14 AM",
"economicId": 518,
"forecast": "104",
"historyId": 449369,
"previous": "105",
"revised": "",
"timestamp": 1464072300
},
{
"actual": "102",
"createTime": "Apr 10, 2017 12:04:14 AM",
"economicId": 518,
"forecast": "103",
"historyId": 449370,
"previous": "104",
"revised": "",
"timestamp": 1466664300
},
{
"actual": "103",
"createTime": "Apr 10, 2017 12:04:14 AM",
"economicId": 518,
"forecast": "101",
"historyId": 449371,
"previous": "102",
"revised": "",
"timestamp": 1469083500
},
{
"actual": "101",
"createTime": "Apr 10, 2017 12:04:14 AM",
"economicId": 518,
"forecast": "103",
"historyId": 449372,
"previous": "103",
"revised": "",
"timestamp": 1472107500
},
{
"actual": "103",
"createTime": "Apr 10, 2017 12:04:14 AM",
"economicId": 518,
"forecast": "101",
"historyId": 449373,
"previous": "101",
"revised": "",
"timestamp": 1474526700
},
{
"actual": "103",
"createTime": "Apr 10, 2017 12:04:14 AM",
"economicId": 518,
"forecast": "103",
"historyId": 449374,
"previous": "103",
"revised": "102",
"timestamp": 1477377900
},
{
"actual": "102",
"createTime": "Apr 10, 2017 12:04:14 AM",
"economicId": 518,
"forecast": "102",
"historyId": 449375,
"previous": "103",
"revised": "103",
"timestamp": 1479973500
},
{
"actual": "106",
"createTime": "Apr 10, 2017 12:04:14 AM",
"economicId": 518,
"forecast": "103",
"historyId": 449376,
"previous": "102",
"revised": "",
"timestamp": 1481874300
},
{
"actual": "106",
"createTime": "Apr 10, 2017 12:04:14 AM",
"economicId": 518,
"forecast": "106",
"historyId": 449377,
"previous": "106",
"revised": "",
"timestamp": 1485330300
},
{
"actual": "107",
"createTime": "Apr 10, 2017 12:04:14 AM",
"economicId": 518,
"forecast": "106",
"historyId": 449378,
"previous": "106",
"revised": "",
"timestamp": 1487835900
},
{
"actual": "105",
"createTime": "Aug 8, 2017 12:35:07 AM",
"economicId": 518,
"forecast": "107",
"historyId": 692633,
"previous": "107",
"revised": "104",
"timestamp": 1490255100
},
{
"actual": "108",
"createTime": "Aug 8, 2017 12:35:07 AM",
"economicId": 518,
"forecast": "104",
"historyId": 692634,
"previous": "105",
"revised": "104",
"timestamp": 1493102700
},
{
"actual": "109",
"createTime": "Aug 8, 2017 12:35:07 AM",
"economicId": 518,
"forecast": "108",
"historyId": 692635,
"previous": "109",
"revised": "",
"timestamp": 1495521900
},
{
"actual": "109",
"createTime": "Aug 8, 2017 12:35:07 AM",
"economicId": 518,
"forecast": "109",
"historyId": 692636,
"previous": "109",
"revised": "108",
"timestamp": 1498113900
},
{
"actual": "109",
"createTime": "Aug 8, 2017 12:35:07 AM",
"economicId": 518,
"forecast": "108",
"historyId": 692637,
"previous": "109",
"revised": "108",
"timestamp": 1500965100
},
{
"actual": "111",
"createTime": "Aug 24, 2017 6:14:38 PM",
"economicId": 518,
"forecast": "108",
"historyId": 696718,
"previous": "108",
"revised": "109",
"timestamp": 1503557100
},
{
"actual": "110",
"createTime": "Oct 9, 2017 9:07:19 AM",
"economicId": 518,
"forecast": "",
"historyId": 698671,
"previous": "111",
"revised": "",
"timestamp": 1506408300
}
]
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,100 @@
[
{
"c": "149.1755",
"h": "149.1935",
"l": "149.1755",
"o": "149.1755",
"t": "Nov 21, 2017 8:42:00 AM"
},
{
"c": "149.1755",
"h": "149.1935",
"l": "149.1755",
"o": "149.1755",
"t": "Nov 21, 2017 8:43:00 AM"
},
{
"c": "149.1755",
"h": "149.1935",
"l": "149.1755",
"o": "149.1755",
"t": "Nov 21, 2017 8:44:00 AM"
},
{
"c": "149.1755",
"h": "149.1935",
"l": "149.1755",
"o": "149.1755",
"t": "Nov 21, 2017 8:45:00 AM"
},
{
"c": "149.1755",
"h": "149.1935",
"l": "149.1755",
"o": "149.1755",
"t": "Nov 21, 2017 8:46:00 AM"
},
{
"c": "149.1755",
"h": "149.1935",
"l": "149.1755",
"o": "149.1755",
"t": "Nov 21, 2017 8:47:00 AM"
},
{
"c": "149.1755",
"h": "149.1935",
"l": "149.1755",
"o": "149.1755",
"t": "Nov 21, 2017 8:48:00 AM"
},
{
"c": "149.1755",
"h": "149.1935",
"l": "149.1755",
"o": "149.1755",
"t": "Nov 21, 2017 8:42:00 AM"
},
{
"c": "149.1755",
"h": "149.1935",
"l": "149.1755",
"o": "149.1755",
"t": "Nov 21, 2017 8:43:00 AM"
},
{
"c": "149.1755",
"h": "149.1935",
"l": "149.1755",
"o": "149.1755",
"t": "Nov 21, 2017 8:44:00 AM"
},
{
"c": "149.1755",
"h": "149.1935",
"l": "149.1755",
"o": "149.1755",
"t": "Nov 21, 2017 8:45:00 AM"
},
{
"c": "149.1755",
"h": "149.1935",
"l": "149.1755",
"o": "149.1755",
"t": "Nov 21, 2017 8:46:00 AM"
},
{
"c": "149.1755",
"h": "149.1935",
"l": "149.1755",
"o": "149.1755",
"t": "Nov 21, 2017 8:47:00 AM"
},
{
"c": "149.1755",
"h": "149.1935",
"l": "149.1755",
"o": "149.1755",
"t": "Nov 21, 2017 8:48:00 AM"
}
]
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,82 @@
package com.tophold.example;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import com.tophold.example.base.BaseActivity;
import com.tophold.example.demo.fund.FundActivity;
import com.tophold.example.demo.kview.KViewActivity;
import com.tophold.example.demo.kview.beginner.ui.KViewHorizontalActivityActivity;
import com.tophold.example.demo.kview.beginner.ui.KViewVerticalActivity;
import com.tophold.example.demo.kview.btc.ui.HuobiListActivity;
import com.tophold.example.demo.kview.forex.ui.ForexListActivity;
import com.tophold.example.demo.pie.PieChartActivity;
import com.tophold.example.demo.seekbar.DoubleThumbSeekBarActivity;
import com.tophold.example.utils.RxUtils;
import com.tophold.trade.utils.StringUtils;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Scheduler;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
public class MainActivity extends BaseActivity {
TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.version);
mTextView.setText(getVersionStr());
}
public String getVersionStr() {
String version = "verisonName:";
version += BuildConfig.VERSION_NAME;
version += ",versionCode(git head):" + BuildConfig.VERSION_CODE;
return version;
}
public void fundView(View view) {
go(FundActivity.class);
}
public void kViewDemo(View view) {
go(KViewActivity.class);
}
public void onPieTest(View view) {
go(PieChartActivity.class);
}
public void onSeekBarTest(View view) {
go(DoubleThumbSeekBarActivity.class);
}
public void onRx(View view) {
Disposable disposable = Observable.create((ObservableOnSubscribe<String>) emitter -> {
while (true) {
emitter.onNext("a");
Thread.sleep(500);
emitter.onNext("b");
Thread.sleep(2000);
emitter.onNext("c");
Thread.sleep(9000);
emitter.onNext("d");
}
}).observeOn(Schedulers.io())
.compose(RxUtils.rxApiSchedulerHelper())
.sample(1000, TimeUnit.MILLISECONDS)
.subscribe(s -> Log.d(TAG, "accept: " + s));
}
}
@@ -0,0 +1,26 @@
package com.tophold.example;
import android.app.Application;
import android.content.Context;
import com.tophold.example.demo.kview.btc.api.HuobiWebSocket;
import com.tophold.example.demo.kview.forex.api.ForexWebSocket;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2018/03/22 21:38
*
* ============================================================
**/
public class MyApplication extends Application {
public static Context mAppContext;
@Override
public void onCreate() {
super.onCreate();
mAppContext = this;
ForexWebSocket.getInstance().init();
HuobiWebSocket.getInstance().init();
}
}
@@ -0,0 +1,240 @@
package com.tophold.example.base;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.ColorRes;
import android.support.annotation.DrawableRes;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.Toast;
import org.greenrobot.eventbus.EventBus;
import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import com.tophold.example.demo.kview.forex.api.RetrofitManager;
/**
* ============================================================
* : wgyscsf
* 创建日期 2017/10/16 16:27
*
* ============================================================
**/
public class BaseActivity extends AppCompatActivity {
/**
* Activity 跳转
*
* @param clazz 目标activity
* @param bundle 传递参数
* @param finish 是否结束当前activity
*/
public static final int NON_CODE = -1;
protected Context mContext = null;
protected String TAG = null;
protected Activity mActivity;
/**
* 对系统系统的toast进行简单封装方便使用
*/
private Toast toast = null;
private CompositeDisposable compositeDisposable;
@Override
public void setContentView(int view) {
super.setContentView(view);
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mActivity = this;
mContext = this;
TAG = this.getClass().getSimpleName();
// getBundleExtras
Bundle extras = getIntent().getExtras();
if (null != extras) {
getBundleExtras(extras);
}
if (isBindEventBusHere()) {
EventBus.getDefault().register(mActivity);
}
}
public boolean isBindEventBusHere() {
return false;
}
protected void getBundleExtras(Bundle extras) {
}
protected Drawable getCompatDrawable(@DrawableRes int drawbaleId) {
return ContextCompat.getDrawable(mContext, drawbaleId);
}
protected int getCompatColor(@ColorRes int colorId) {
return ContextCompat.getColor(mContext, colorId);
}
/**
* 添加disposable
*
* @param disposable
*/
public void unSubscription(Disposable disposable) {
if (compositeDisposable == null) {
synchronized (CompositeDisposable.class) {
if (compositeDisposable == null) {
compositeDisposable = new CompositeDisposable();
}
}
}
compositeDisposable.add(disposable);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (isBindEventBusHere()) {
EventBus.getDefault().unregister(mActivity);
}
if (compositeDisposable != null) {
Log.d(TAG, "base activity dispose");
compositeDisposable.clear();
}
}
/**
* @param observable
* @param observer
*/
public void call(Observable observable, Observer observer) {
RetrofitManager.call(observable, observer);
}
/**
* startActivity
*
* @param clazz target Activity
*/
public void go(Class<? extends Activity> clazz) {
_goActivity(clazz, null, NON_CODE, false);
}
/**
* startActivity with bundle
*
* @param clazz target Activity
* @param bundle
*/
public void go(Class<? extends Activity> clazz, Bundle bundle) {
_goActivity(clazz, bundle, NON_CODE, false);
}
/**
* startActivity then finish this
*
* @param clazz target Activity
*/
public void goAndFinish(Class<? extends Activity> clazz) {
_goActivity(clazz, null, NON_CODE, true);
}
/**
* startActivity with bundle and then finish this
*
* @param clazz target Activity
* @param bundle bundle extra
*/
public void goAndFinish(Class<? extends Activity> clazz, Bundle bundle) {
_goActivity(clazz, bundle, NON_CODE, true);
}
/**
* startActivityForResult
*
* @param clazz
* @param requestCode
*/
protected void goForResult(Class<? extends Activity> clazz, int requestCode) {
_goActivity(clazz, null, requestCode, false);
}
/**
* startActivityForResult with bundle
*
* @param clazz
* @param bundle
* @param requestCode
*/
protected void goForResult(Class<? extends Activity> clazz, Bundle bundle, int requestCode) {
_goActivity(clazz, bundle, requestCode, false);
}
/**
* startActivityForResult then finish this
*
* @param clazz
* @param requestCode
*/
protected void goForResultAndFinish(Class<? extends Activity> clazz, int requestCode) {
_goActivity(clazz, null, requestCode, true);
}
/**
* startActivityForResult with bundle and then finish this
*
* @param clazz
* @param bundle
* @param requestCode
*/
protected void goForResultAndFinish(Class<? extends Activity> clazz, Bundle bundle, int requestCode) {
_goActivity(clazz, bundle, requestCode, true);
}
//可以立刻刷新Toast推荐使用该方式
public void showSingletonToast(String str) {
if (toast == null) {
toast = Toast.makeText(getApplicationContext(), str, Toast.LENGTH_SHORT);
} else {
toast.setText(str);
}
toast.show();
}
public void showToast(String msg) {
showToast(msg, Toast.LENGTH_SHORT);
}
public void showToast(String msg, int toastDuration) {
if (null != msg && (toastDuration == Toast.LENGTH_SHORT || toastDuration == Toast.LENGTH_LONG)) {
Toast.makeText(getApplicationContext(), msg, toastDuration).show();
}
}
private void _goActivity(Class<? extends Activity> clazz, Bundle bundle, int requestCode, boolean finish) {
if (null == clazz) {
throw new IllegalArgumentException("you must pass a target activity where to go.");
}
Intent intent = new Intent(this, clazz);
if (null != bundle) {
intent.putExtras(bundle);
}
if (requestCode > NON_CODE) {
startActivityForResult(intent, requestCode);
} else {
startActivity(intent);
}
if (finish) {
finish();
}
}
}
@@ -0,0 +1,68 @@
package com.tophold.example.base;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import org.greenrobot.eventbus.EventBus;
import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import com.tophold.example.demo.kview.forex.api.RetrofitManager;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2018/03/22 14:06
*
* ============================================================
**/
public class BaseFragment extends android.support.v4.app.Fragment {
CompositeDisposable disposables;
protected String TAG;
protected Context mContext;
protected Activity mActivity;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TAG = this.getClass().getSimpleName();
mContext = this.getActivity();
mActivity = getActivity();
if (isBindEventBusHere()) {
EventBus.getDefault().register(this);
}
}
public boolean isBindEventBusHere() {
return false;
}
@Override
public void onDestroy() {
super.onDestroy();
if (isBindEventBusHere()) {
EventBus.getDefault().unregister(this);
}
if (disposables != null) disposables.clear();
}
public void unSubscription(Disposable disposable) {
if (disposables == null) {
synchronized (CompositeDisposable.class) {
if (disposables == null) {
disposables = new CompositeDisposable();
}
}
}
disposables.add(disposable);
}
/**
* @param observable
* @param observer
*/
public void call(Observable observable, Observer<?> observer) {
RetrofitManager.call(observable, observer);
}
}
@@ -0,0 +1,106 @@
package com.tophold.example.demo.fund;
import android.os.Bundle;
import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import com.tophold.example.base.BaseActivity;
import com.tophold.example.R;
import com.tophold.example.demo.kview.beginner.model.OriginFundMode;
import com.tophold.example.demo.kview.beginner.api.FundSimulateNetAPI;
import com.tophold.example.utils.GsonUtil;
import com.tophold.trade.utils.StringUtils;
import com.tophold.trade.view.fund.FundMode;
import com.tophold.trade.view.fund.FundView;
public class FundActivity extends BaseActivity {
private FundView mFundView;
List<OriginFundMode> mOriginFundModeList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fund);
initView();
initData();
loadData();
}
private void initView() {
mFundView = (FundView) findViewById(R.id.af_fv_fundview);
/**
* 定制,所有的画笔以及其它属性都已经暴露出来有了更加大的定时灵活性更多参数可以直接查看源码...
*/
//常规setget...
mFundView.getBrokenPaint().setColor(getResources().getColor(R.color.colorAccent));//设置折现颜色
mFundView.getInnerXPaint().setStrokeWidth(1);//设置内部x轴虚线的宽度,px
mFundView.getBrokenPaint().setStrokeWidth(1);
//链式调用
mFundView
.setBasePaddingTop(140)
.setBasePaddingLeft(50)
.setBasePaddingRight(40)
.setBasePaddingBottom(30)
.setLoadingText("正在加载,马上就来...");
}
private void initData() {
mOriginFundModeList = new ArrayList<>();
}
private void loadData() {
Disposable subscribe = Observable.timer(StringUtils.getRadomNum(500, 3000), TimeUnit.MILLISECONDS)
.map(map -> {
String originalFundData = FundSimulateNetAPI.getOriginalFundData(mContext);
if (originalFundData == null) {
Log.e(TAG, "loadData: 从网络获取到的数据为空");
return null;
}
OriginFundMode[] originFunModes;
try {
originFunModes = GsonUtil.fromJson2Object(originalFundData, OriginFundMode[].class);
} catch (Exception e) {
e.printStackTrace();
return null;
}
List<OriginFundMode> OriginFundModeList = Arrays.asList(originFunModes);
//开始适配图表数据
return adapterData(OriginFundModeList);
})
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
mData -> {
if (StringUtils.isEmpty(mData)) {
Log.d(TAG, "loadData: data is empty!");
return;
}
mFundView.setDataList(mData);
}
, throwable -> throwable.printStackTrace());
unSubscription(subscribe);
}
private List<FundMode> adapterData(List<OriginFundMode> originFundModeList) {
List<FundMode> fundModeList = new ArrayList<>();//适配后的数据
for (OriginFundMode originFundMode : originFundModeList) {
FundMode fundMode = new FundMode(originFundMode.timestamp * 1000, originFundMode.actual);
fundModeList.add(fundMode);
Log.e(TAG, "adapterData: 适配之前:" + originFundMode.actual + "----->>" + fundMode.dataY);
}
return fundModeList;
}
}
@@ -0,0 +1,35 @@
package com.tophold.example.demo.kview
import android.os.Bundle
import android.view.View
import com.tophold.example.BuildConfig
import com.tophold.example.R
import com.tophold.example.base.BaseActivity
import com.tophold.example.demo.kview.beginner.ui.KViewHorizontalActivityActivity
import com.tophold.example.demo.kview.beginner.ui.KViewVerticalActivity
import com.tophold.example.demo.kview.btc.ui.HuobiListActivity
import com.tophold.example.demo.kview.forex.ui.ForexListActivity
class KViewActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_kview)
}
fun kViewVertical(view: View) {
go(KViewVerticalActivity::class.java)
}
fun kViewHorizontal(view: View) {
go(KViewHorizontalActivityActivity::class.java)
}
fun kViewEvaluation(view: View) {
go(ForexListActivity::class.java)
}
fun btnDemo(view: View) {
go(HuobiListActivity::class.java)
}
}
@@ -0,0 +1,51 @@
package com.tophold.example.demo.kview.beginner.api;
import android.content.Context;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2017/10/25 15:43
*
* ============================================================
**/
public class FundSimulateNetAPI {
/**
* 获取去最原始的数据信息
*
* @return json data
*/
public static String getOriginalFundData(Context context) {
InputStream input = null;
try {
input = context.getAssets().open("fund.json");
String json = convertStreamToString(input);
return json;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* input 流转换为字符串
*
* @param is
* @return
*/
private static String convertStreamToString(java.io.InputStream is) {
String s = null;
try {
Scanner scanner = new Scanner(is, "UTF-8").useDelimiter("\\A");
if (scanner.hasNext()) s = scanner.next();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
return s;
}
}
@@ -0,0 +1,51 @@
package com.tophold.example.demo.kview.beginner.api;
import android.content.Context;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2017/10/25 15:43
*
* ============================================================
**/
public class KViewSimulateNetAPI {
/**
* 获取去最原始的数据信息
*
* @return json data
*/
public static String getOriginalFundData(Context context, int index) {
InputStream input = null;
try {
input = context.getAssets().open("timesharing" + index + ".json");
String json = convertStreamToString(input);
return json;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* input 流转换为字符串
*
* @param is
* @return
*/
private static String convertStreamToString(InputStream is) {
String s = null;
try {
Scanner scanner = new Scanner(is, "UTF-8").useDelimiter("\\A");
if (scanner.hasNext()) s = scanner.next();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
return s;
}
}
@@ -0,0 +1,33 @@
package com.tophold.example.demo.kview.beginner.model;
import java.io.Serializable;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2017/10/24 15:00
*
* ============================================================
**/
public class OriginFundMode implements Serializable {
/**
* actual : 103
* createTime : Apr 10, 2017 12:04:14 AM
* economicId : 518
* forecast : 102
* historyId : 449366
* previous : 103
* revised :
* timestamp : 1456213500
*/
public String actual;
public String createTime;
public int economicId;
public String forecast;
public int historyId;
public String previous;
public String revised;
public long timestamp;
}
@@ -0,0 +1,26 @@
package com.tophold.example.demo.kview.beginner.model;
import java.io.Serializable;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2017/11/21 11:09
* 原始数据
* ============================================================
**/
public class OriginQuotes implements Serializable {
/**
* t : 2016-10-25 12:06:00
* o : 2357.25
* h : 2357.25
* l : 2357.25
* c : 2357.25
*/
public String t;
public String o;
public String h;
public String l;
public String c;
}
@@ -0,0 +1,278 @@
package com.tophold.example.demo.kview.beginner.ui;
import android.os.Bundle;
import android.support.annotation.ColorRes;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.google.gson.reflect.TypeToken;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import com.tophold.example.R;
import com.tophold.example.demo.kview.beginner.model.OriginQuotes;
import com.tophold.example.demo.kview.beginner.api.KViewSimulateNetAPI;
import com.tophold.example.base.BaseActivity;
import com.tophold.trade.utils.FormatUtil;
import com.tophold.example.utils.GsonUtil;
import com.tophold.trade.utils.StringUtils;
import com.tophold.trade.utils.TimeUtils;
import com.tophold.trade.view.kview.KView;
import com.tophold.trade.view.kview.KViewListener;
import com.tophold.trade.view.kview.Quotes;
/**
* timesharing0:模拟的是加载更多的数据注意会分段取模拟的是多次加载更多
* timesharing1模拟的是api请求的数据集合注意一次加载完毕模拟的是第一次加载的数据
* timesharing2模拟的是实时**推送**的数据注意会分段取一次取一个
*/
public class KViewHorizontalActivityActivity extends BaseActivity {
//bind view
private LinearLayout mAkvLlContainer;
private TextView mAkvTvH;
private TextView mAkvTvO;
private TextView mAkvTvTime;
private TextView mAkvTvL;
private TextView mAkvTvC;
private TextView mAkvTvP;
private KView mAkvKvKview;
//模拟网络过来的列表数据
List<Quotes> mQuotesList;
//模拟加载更多的数据
List<Quotes> mLoadMoreData;
//模拟socket推送过来的单个数据
List<Quotes> mPushData;
//加载更多加载到哪儿了因为真实应用中也存在加载完毕的情况这里对应加载到list的最后
int index = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_kview_horizontal);
initView();
initData();
loadData();
pushData();
}
private void pushData() {
Disposable disposable = Observable.interval(StringUtils.getRadomNum(300, 3000), TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
data -> {
int size = mPushData.size();
if (data < size) {
mAkvKvKview.pushKViewData(mPushData.get(data.intValue()), 0);
} else {
}
}
, throwable -> throwable.printStackTrace());
unSubscription(disposable);
}
private void initView() {
mAkvLlContainer = (LinearLayout) findViewById(R.id.akv_ll_container);
mAkvTvH = (TextView) findViewById(R.id.akv_tv_h);
mAkvTvO = (TextView) findViewById(R.id.akv_tv_o);
mAkvTvTime = (TextView) findViewById(R.id.akv_tv_time);
mAkvTvL = (TextView) findViewById(R.id.akv_tv_l);
mAkvTvC = (TextView) findViewById(R.id.akv_tv_c);
mAkvTvP = (TextView) findViewById(R.id.akv_tv_p);
mAkvKvKview = (KView) findViewById(R.id.akv_kv_kview);
}
private void initData() {
mQuotesList = new ArrayList<>();
mLoadMoreData = new ArrayList<>();
mPushData = new ArrayList<>();
//这里先预加载加载更多的数据然后加载更多的时候分段取出来模拟加载更多
initLoadMoreData();
//pushData
initPushData();
}
private void initPushData() {
String originalFundData = KViewSimulateNetAPI.getOriginalFundData(mContext, 2);
if (originalFundData == null) {
Log.e(TAG, "loadData: 从网络获取到的数据为空");
return;
}
List<OriginQuotes> OriginFundModeList;
try {
OriginFundModeList = GsonUtil.fromJson2Object(originalFundData, new TypeToken<List<OriginQuotes>>() {
}.getType());
} catch (Exception e) {
e.printStackTrace();
return;
}
//开始适配图表数据
mPushData = adapterData(OriginFundModeList);
}
private void initLoadMoreData() {
String originalFundData = KViewSimulateNetAPI.getOriginalFundData(mContext, 0);
if (originalFundData == null) {
Log.e(TAG, "loadData: 从网络获取到的数据为空");
return;
}
try {
List<OriginQuotes> quotesList = GsonUtil.fromJson2Object(originalFundData,
new TypeToken<List<OriginQuotes>>() {
}.getType());
mLoadMoreData = adapterData(quotesList);
} catch (Exception e) {
e.printStackTrace();
return;
}
}
private void loadData() {
//模拟网络环境加载数据列表
Disposable disposable = Observable.timer(StringUtils.getRadomNum(500, 2000),
TimeUnit.MILLISECONDS)
.doOnNext(data -> {
String originalData = KViewSimulateNetAPI.getOriginalFundData(mContext, 2);
if (originalData == null) {
Log.e(TAG, "loadData: 从网络获取到的数据为空");
return;
}
try {
List<OriginQuotes> originQuotes = GsonUtil
.fromJson2Object(originalData, new TypeToken<List<OriginQuotes>>() {
}.getType());
mQuotesList = adapterData(originQuotes);
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "loadData: json转换错误");
return;
}
})
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
data -> mAkvKvKview.setKViewData(mQuotesList, new KViewListener.MasterTouchListener() {
@Override
public void onLongTouch(Quotes preQuotes, Quotes currentQuotes) {
showContanier(preQuotes, currentQuotes);
}
@Override
public void onUnLongTouch() {
mAkvLlContainer.setVisibility(View.INVISIBLE);
}
@Override
public void needLoadMore() {
loadMore();
}
}),
Throwable::printStackTrace
);
unSubscription(disposable);
}
private void loadMore() {
if (mLoadMoreData == null) return;
Disposable disposable = Observable.timer(StringUtils.getRadomNum(1000, 5000), TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
data -> {
int size = mLoadMoreData.size();
int min = size / 20;
int max = size / 5;//一次最多加载多少
int loadSize = StringUtils.getRadomNum(min, max);
if (index == loadSize) {
//没有更多数据了
mAkvKvKview.loadMoreNoData();
}
if ((index + loadSize) > mLoadMoreData.size()) {
loadSize = mLoadMoreData.size();
}
List<Quotes> loadList = mLoadMoreData.subList(index, index + loadSize);
index = index + loadSize;//重置起始位置
mAkvKvKview.loadKViewData(loadList);
}
, throwable -> throwable.printStackTrace());
unSubscription(disposable);
}
private List<Quotes> adapterData(List<OriginQuotes> originFundModeList) {
List<Quotes> fundModeList = new ArrayList<>();//适配后的数据
for (OriginQuotes OriginQuotes : originFundModeList) {
Quotes Quotes = new Quotes(OriginQuotes.o, OriginQuotes.h, OriginQuotes.l,
OriginQuotes.c, OriginQuotes.t);
fundModeList.add(Quotes);
}
return fundModeList;
}
private void showContanier(Quotes preQuotes, Quotes currentQuotes) {
mAkvLlContainer.setVisibility(View.VISIBLE);
int digits = 4;
boolean isPositive;
String precent;
double dis = (currentQuotes.c - preQuotes.c) / currentQuotes.c * 100;
isPositive = dis >= 0;
precent = FormatUtil.formatBySubString(dis, 2);
precent += "%";
//
mAkvTvH.setText(FormatUtil.numFormat(currentQuotes.h, digits));
mAkvTvO.setText(FormatUtil.numFormat(currentQuotes.o, digits));
mAkvTvL.setText(FormatUtil.numFormat(currentQuotes.l, digits));
mAkvTvC.setText(FormatUtil.numFormat(currentQuotes.c, digits));
mAkvTvP.setText(precent);
mAkvTvTime.setText(TimeUtils.millis2String(currentQuotes.t, new SimpleDateFormat("MM-dd HH:mm")));
if (isPositive) {
mAkvTvH.setTextColor(getMyColor(R.color.color_timeSharing_callBackRed));
mAkvTvO.setTextColor(getMyColor(R.color.color_timeSharing_callBackRed));
mAkvTvL.setTextColor(getMyColor(R.color.color_timeSharing_callBackRed));
mAkvTvC.setTextColor(getMyColor(R.color.color_timeSharing_callBackRed));
mAkvTvP.setTextColor(getMyColor(R.color.color_timeSharing_callBackRed));
} else {
mAkvTvH.setTextColor(getMyColor(R.color.color_timeSharing_callBackGreen));
mAkvTvO.setTextColor(getMyColor(R.color.color_timeSharing_callBackGreen));
mAkvTvL.setTextColor(getMyColor(R.color.color_timeSharing_callBackGreen));
mAkvTvC.setTextColor(getMyColor(R.color.color_timeSharing_callBackGreen));
mAkvTvP.setTextColor(getMyColor(R.color.color_timeSharing_callBackGreen));
}
}
private int getMyColor(@ColorRes int colorId) {
return getResources().getColor(colorId);
}
public void showCandle(View view) {
boolean showTimSharing = mAkvKvKview.isShowTimSharing();
mAkvKvKview.setShowTimSharing(!showTimSharing);
if (!showTimSharing) {
((Button) view).setText("点击展示蜡烛图");
} else {
((Button) view).setText("点击展示分时图");
}
}
}
@@ -0,0 +1,294 @@
package com.tophold.example.demo.kview.beginner.ui;
import android.os.Bundle;
import android.support.annotation.ColorRes;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.google.gson.reflect.TypeToken;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import com.tophold.example.base.BaseActivity;
import com.tophold.example.R;
import com.tophold.example.demo.kview.beginner.api.KViewSimulateNetAPI;
import com.tophold.example.demo.kview.beginner.model.OriginQuotes;
import com.tophold.trade.utils.FormatUtil;
import com.tophold.example.utils.GsonUtil;
import com.tophold.trade.utils.StringUtils;
import com.tophold.trade.utils.TimeUtils;
import com.tophold.trade.view.kview.KView;
import com.tophold.trade.view.kview.KViewListener;
import com.tophold.trade.view.kview.Quotes;
/**
* timesharing0:模拟的是加载更多的数据注意会分段取模拟的是多次加载更多
* timesharing1模拟的是api请求的数据集合注意一次加载完毕模拟的是第一次加载的数据
* timesharing2模拟的是实时**推送**的数据注意会分段取一次取一个
*/
public class KViewVerticalActivity extends BaseActivity {
//bind view
private LinearLayout mAkvLlContainer;
private TextView mAkvTvH;
private TextView mAkvTvO;
private TextView mAkvTvTime;
private TextView mAkvTvL;
private TextView mAkvTvC;
private TextView mAkvTvP;
private KView mAkvKvKview;
private Button mAkvBtnShowCandle;
private Button mAkvBtnShowMinnor;
//模拟网络过来的列表数据
List<Quotes> mQuotesList;
//模拟加载更多的数据
List<Quotes> mLoadMoreData;
//模拟socket推送过来的单个数据
List<Quotes> mPushData;
//加载更多加载到哪儿了因为真实应用中也存在加载完毕的情况这里对应加载到list的最后
int index = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_kview_vertical);
initView();
initData();
loadData();
pushData();
}
private void pushData() {
Disposable disposable = Observable.interval(StringUtils.getRadomNum(300, 3000), TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
data -> {
int size = mPushData.size();
if (data < size) {
mAkvKvKview.pushKViewData(mPushData.get(data.intValue()), 0);
} else {
}
}
, throwable -> throwable.printStackTrace());
unSubscription(disposable);
}
private void initView() {
mAkvLlContainer = (LinearLayout) findViewById(R.id.akv_ll_container);
mAkvTvH = (TextView) findViewById(R.id.akv_tv_h);
mAkvTvO = (TextView) findViewById(R.id.akv_tv_o);
mAkvTvTime = (TextView) findViewById(R.id.akv_tv_time);
mAkvTvL = (TextView) findViewById(R.id.akv_tv_l);
mAkvTvC = (TextView) findViewById(R.id.akv_tv_c);
mAkvTvP = (TextView) findViewById(R.id.akv_tv_p);
mAkvKvKview = (KView) findViewById(R.id.akv_kv_kview);
mAkvBtnShowCandle = (Button) findViewById(R.id.akv_btn_showCandle);
mAkvBtnShowMinnor = (Button) findViewById(R.id.akv_btn_showMinnor);
}
private void initData() {
mQuotesList = new ArrayList<>();
mLoadMoreData = new ArrayList<>();
mPushData = new ArrayList<>();
//这里先预加载加载更多的数据然后加载更多的时候分段取出来模拟加载更多
initLoadMoreData();
//pushData
initPushData();
}
private void initPushData() {
String originalFundData = KViewSimulateNetAPI.getOriginalFundData(mContext, 3);
if (originalFundData == null) {
Log.e(TAG, "loadData: 从网络获取到的数据为空");
return;
}
List<OriginQuotes> OriginFundModeList;
try {
OriginFundModeList = GsonUtil.fromJson2Object(originalFundData, new TypeToken<List<OriginQuotes>>() {
}.getType());
} catch (Exception e) {
e.printStackTrace();
return;
}
//开始适配图表数据
mPushData = adapterData(OriginFundModeList);
}
private void initLoadMoreData() {
String originalFundData = KViewSimulateNetAPI.getOriginalFundData(mContext, 3);
if (originalFundData == null) {
Log.e(TAG, "loadData: 从网络获取到的数据为空");
return;
}
try {
List<OriginQuotes> quotesList = GsonUtil.fromJson2Object(originalFundData,
new TypeToken<List<OriginQuotes>>() {
}.getType());
mLoadMoreData = adapterData(quotesList);
} catch (Exception e) {
e.printStackTrace();
return;
}
}
private void loadData() {
//模拟网络环境加载数据列表
Disposable disposable = Observable.timer(StringUtils.getRadomNum(500, 2000),
TimeUnit.MILLISECONDS)
.doOnNext(data -> {
String originalData = KViewSimulateNetAPI.getOriginalFundData(mContext, 3);
if (originalData == null) {
Log.e(TAG, "loadData: 从网络获取到的数据为空");
return;
}
try {
List<OriginQuotes> originQuotes = GsonUtil
.fromJson2Object(originalData, new TypeToken<List<OriginQuotes>>() {
}.getType());
mQuotesList = adapterData(originQuotes);
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "loadData: json转换错误");
return;
}
})
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
data -> {
mAkvKvKview.setKViewData(mQuotesList, new KViewListener.MasterTouchListener() {
@Override
public void onLongTouch(Quotes preQuotes, Quotes currentQuotes) {
showContanier(preQuotes, currentQuotes);
}
@Override
public void onUnLongTouch() {
mAkvLlContainer.setVisibility(View.INVISIBLE);
}
@Override
public void needLoadMore() {
loadMore();
}
});
},
throwable -> throwable.printStackTrace()
);
unSubscription(disposable);
}
private void loadMore() {
if (mLoadMoreData == null) return;
Disposable disposable = Observable.timer(StringUtils.getRadomNum(1000, 5000), TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
data -> {
int size = mLoadMoreData.size();
int min = size / 20;
int max = size / 5;//一次最多加载多少
int loadSize = StringUtils.getRadomNum(min, max);
if (index == loadSize) {
//没有更多数据了
mAkvKvKview.loadMoreNoData();
}
if ((index + loadSize) > mLoadMoreData.size()) {
loadSize = mLoadMoreData.size();
}
List<Quotes> loadList = mLoadMoreData.subList(index, index + loadSize);
index = index + loadSize;//重置起始位置
mAkvKvKview.loadKViewData(loadList);
}
, throwable -> throwable.printStackTrace());
unSubscription(disposable);
}
public void showCandle(View view) {
boolean showTimSharing = mAkvKvKview.isShowTimSharing();
mAkvKvKview.setShowTimSharing(!showTimSharing);
if (!showTimSharing) {
mAkvBtnShowCandle.setText("点击展示蜡烛图");
} else {
mAkvBtnShowCandle.setText("点击展示分时图");
}
}
public void showMinor(View view) {
boolean showMinor = mAkvKvKview.isShowMinor();
mAkvKvKview.setShowMinor(!showMinor);
if (!showMinor) {
mAkvBtnShowMinnor.setText("点击不显示副图");
} else {
mAkvBtnShowMinnor.setText("点击显示副图");
}
}
private List<Quotes> adapterData(List<OriginQuotes> originFundModeList) {
List<Quotes> fundModeList = new ArrayList<>();//适配后的数据
for (OriginQuotes OriginQuotes : originFundModeList) {
Quotes Quotes = new Quotes(OriginQuotes.o, OriginQuotes.h, OriginQuotes.l,
OriginQuotes.c, OriginQuotes.t);
fundModeList.add(Quotes);
}
return fundModeList;
}
private void showContanier(Quotes preQuotes, Quotes currentQuotes) {
mAkvLlContainer.setVisibility(View.VISIBLE);
int digits = 4;
boolean isPositive;
String precent;
double dis = (currentQuotes.c - preQuotes.c) / currentQuotes.c * 100;
isPositive = dis >= 0;
precent = FormatUtil.formatBySubString(dis, 2);
precent += "%";
//
mAkvTvH.setText(FormatUtil.numFormat(currentQuotes.h, digits));
mAkvTvO.setText(FormatUtil.numFormat(currentQuotes.o, digits));
mAkvTvL.setText(FormatUtil.numFormat(currentQuotes.l, digits));
mAkvTvC.setText(FormatUtil.numFormat(currentQuotes.c, digits));
mAkvTvP.setText(precent);
mAkvTvTime.setText(TimeUtils.millis2String(currentQuotes.t, new SimpleDateFormat("MM-dd HH:mm")));
if (isPositive) {
mAkvTvH.setTextColor(getMyColor(R.color.color_timeSharing_callBackRed));
mAkvTvO.setTextColor(getMyColor(R.color.color_timeSharing_callBackRed));
mAkvTvL.setTextColor(getMyColor(R.color.color_timeSharing_callBackRed));
mAkvTvC.setTextColor(getMyColor(R.color.color_timeSharing_callBackRed));
mAkvTvP.setTextColor(getMyColor(R.color.color_timeSharing_callBackRed));
} else {
mAkvTvH.setTextColor(getMyColor(R.color.color_timeSharing_callBackGreen));
mAkvTvO.setTextColor(getMyColor(R.color.color_timeSharing_callBackGreen));
mAkvTvL.setTextColor(getMyColor(R.color.color_timeSharing_callBackGreen));
mAkvTvC.setTextColor(getMyColor(R.color.color_timeSharing_callBackGreen));
mAkvTvP.setTextColor(getMyColor(R.color.color_timeSharing_callBackGreen));
}
}
private int getMyColor(@ColorRes int colorId) {
return getResources().getColor(colorId);
}
}
@@ -0,0 +1,109 @@
package com.tophold.example.demo.kview.btc.api;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2018/03/28 14:14
*
* ============================================================
**/
public class GZipUtil {
// 压缩
public static byte[] compress(byte[] data) throws IOException {
if (data == null || data.length == 0) {
return null;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(out);
gzip.write(data);
gzip.close();
return out.toByteArray();//out.toString("ISO-8859-1");
}
public static byte[] compress(String str) throws IOException {
if (str == null || str.length() == 0) {
return null;
}
return compress(str.getBytes("utf-8"));
}
// 解压缩
public static byte[] uncompress(byte[] data) throws IOException {
if (data == null || data.length == 0) {
return data;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(data);
GZIPInputStream gunzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = gunzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
gunzip.close();
in.close();
return out.toByteArray();
}
public static String uncompressBytes(byte[] data) throws IOException {
byte[] bytes = uncompress(data);
return new String(bytes);
}
public static String uncompress(String str) throws IOException {
if (str == null || str.length() == 0) {
return str;
}
byte[] data = uncompress(str.getBytes("utf-8")); // ISO-8859-1
return new String(data);
}
/**
* @param @param unZipfile
* @param @param destFile 指定读取文件需要从压缩文件中读取文件内容的文件名
* @param @return 设定文件
* @return String 返回类型
* @throws
* @Title: unZip
* @Description: TODO(这里用一句话描述这个方法的作用)
*/
public static String unZip(String unZipfile, String destFile) {// unZipfileName需要解压的zip文件名
InputStream inputStream;
String inData = null;
try {
// 生成一个zip的文件
File f = new File(unZipfile);
ZipFile zipFile = new ZipFile(f);
// 遍历zipFile中所有的实体并把他们解压出来
ZipEntry entry = zipFile.getEntry(destFile);
if (!entry.isDirectory()) {
// 获取出该压缩实体的输入流
inputStream = zipFile.getInputStream(entry);
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] bys = new byte[4096];
for (int p = -1; (p = inputStream.read(bys)) != -1; ) {
out.write(bys, 0, p);
}
inData = out.toString();
out.close();
inputStream.close();
}
zipFile.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
return inData;
}
}
@@ -0,0 +1,87 @@
package com.tophold.example.demo.kview.btc.api;
import android.util.Log;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
/**
* 拦截器
*
* 向请求头里添加公共参数
* Created by zhouwei on 16/11/10.
*/
public class HttpCommonInterceptor implements Interceptor {
private Map<String,String> mHeaderParamsMap = new HashMap<>();
public HttpCommonInterceptor() {
}
@Override
public Response intercept(Chain chain) throws IOException {
Log.d("HttpCommonInterceptor","add common params");
Request oldRequest = chain.request();
// 添加新的参数添加到url
/* HttpUrl.Builder authorizedUrlBuilder = oldRequest.url()
.newBuilder()
.scheme(oldRequest.url().scheme())
.host(oldRequest.url().host());*/
// 新的请求
Request.Builder requestBuilder = oldRequest.newBuilder();
requestBuilder.method(oldRequest.method(), oldRequest.body());
//添加公共参数,添加到header中
if(mHeaderParamsMap.size() > 0){
for(Map.Entry<String,String> params:mHeaderParamsMap.entrySet()){
requestBuilder.header(params.getKey(),params.getValue());
}
}
Request newRequest = requestBuilder.build();
return chain.proceed(newRequest);
}
public static class Builder{
HttpCommonInterceptor mHttpCommonInterceptor;
public Builder(){
mHttpCommonInterceptor = new HttpCommonInterceptor();
}
public Builder addHeaderParams(String key, String value){
mHttpCommonInterceptor.mHeaderParamsMap.put(key,value);
return this;
}
public Builder addHeaderParams(String key, int value){
return addHeaderParams(key, String.valueOf(value));
}
public Builder addHeaderParams(String key, float value){
return addHeaderParams(key, String.valueOf(value));
}
public Builder addHeaderParams(String key, long value){
return addHeaderParams(key, String.valueOf(value));
}
public Builder addHeaderParams(String key, double value){
return addHeaderParams(key, String.valueOf(value));
}
public HttpCommonInterceptor build(){
return mHttpCommonInterceptor;
}
}
}
@@ -0,0 +1,27 @@
package com.tophold.example.demo.kview.btc.api;
import java.util.List;
import java.util.Map;
import io.reactivex.Observable;
import retrofit2.http.GET;
import retrofit2.http.QueryMap;
import com.tophold.example.demo.kview.btc.model.HuobiData;
import com.tophold.example.demo.kview.btc.model.HuobiSymbol;
import com.tophold.example.demo.kview.btc.model.HuobiQuote;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2018/03/22 11:31
*
* ============================================================
**/
public interface HuobiService {
@GET("/market/history/kline")
Observable<HuobiData<List<HuobiQuote>>> chartQuotes(@QueryMap Map<String, Object> params);
@GET("/v1/common/symbols")
Observable<HuobiData<List<HuobiSymbol>>> getSymbolList(@QueryMap Map<String, Object> params);
}
@@ -0,0 +1,70 @@
package com.tophold.example.demo.kview.btc.api;
import android.util.Log;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2018/03/22 21:31
*
* ============================================================
**/
public class HuobiSocketApi {
public static final String TAG = HuobiSocketApi.class.getSimpleName();
public static void ping(long time) {
String str = "{\"ping\":" + time + "}";
sendService(str);
}
public static void pong(long time) {
String str = "{\"pong\":" + time + "}";
sendService(str);
}
/**
* 订阅KLine和取消KLine
*
* @param id 一个唯一值建议用时间戳
* @param symbol
* @param period 周期不管任何周期全部转化为时间戳格式
* @param sub true:订阅false:取消订阅
*/
public static void kLine(long id, String symbol, long period, boolean sub) {
String periodByMs = getPeriodByMs(period);
if (periodByMs == null) {
Log.e(TAG, "subKLine: period 不合法!!!");
return;
}
String kline = "market." + symbol + ".kline." + periodByMs;
String json = "{" + (sub ? "\"sub\"" : "\"unsub\"") + ":\"" + kline + "\",\"id\":\"" + id + "\"}";
sendService(json);
}
public static void subKLine(String symbol, long period, boolean sub) {
long id = System.currentTimeMillis();
kLine(id, symbol, period, sub);
}
private static void sendService(String str) {
Log.d(TAG, "sendService: " + str);
HuobiWebSocket.getInstance().getWebSocket().send(str);
}
private static String getPeriodByMs(long ms) {
long s = ms / 1000;
if (s == 1 * 60) return "1min";
if (s == 5 * 60) return "5min";
if (s == 15 * 60) return "15min";
if (s == 30 * 60) return "30min";
if (s == 60 * 60) return "60min";
if (s == 24 * 60 * 60) return "1day";
if (s == 7 * 24 * 60 * 60) return "1week";
if (s == 30 * 24 * 60 * 60) return "1mon";
if (s == 365 * 24 * 60 * 60) return "1year";
Log.d(TAG, "getPeriodByMs: unkonwn ms!!");
return null;
}
}
@@ -0,0 +1,54 @@
package com.tophold.example.demo.kview.btc.api;
import android.util.Log;
import org.greenrobot.eventbus.EventBus;
import org.json.JSONException;
import org.json.JSONObject;
import com.tophold.example.demo.kview.btc.model.HuobiWsQuote;
import com.tophold.example.utils.GsonUtil;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2018/03/22 21:26
*
* ============================================================
**/
public class HuobiSocketParser {
public static final String TAG = HuobiSocketParser.class.getSimpleName();
public static void fix2Object(String str) {
Log.d(TAG, "fix2Object: " + str);
try {
formatJson(str);
} catch (JSONException e) {
e.printStackTrace();
}
}
private static void formatJson(String str) throws JSONException {
JSONObject json = new JSONObject(str);
if (json.has("ping")) {
long ping = json.getLong("ping");
HuobiSocketApi.pong(ping);
return;
}
//判断不严谨
if (json.has("ch")) {
HuobiWsQuote huobiWsQuote = GsonUtil.fromJson2Object(str, HuobiWsQuote.class);
if (huobiWsQuote != null) {
obj2EventBus(huobiWsQuote);
}
return;
}
}
private static void obj2EventBus(Object object) {
EventBus.getDefault().post(object);
}
}
@@ -0,0 +1,131 @@
package com.tophold.example.demo.kview.btc.api;
import android.support.annotation.Nullable;
import android.util.Log;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okhttp3.logging.HttpLoggingInterceptor;
import okio.ByteString;
import com.tophold.example.BuildConfig;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2018/03/22 21:15
*
* ============================================================
**/
public class HuobiWebSocket {
public static final String TAG = HuobiWebSocket.class.getSimpleName();
private static final int DEFAULT_TIME_OUT = 20;//超时时间 5s
private static final int DEFAULT_READ_TIME_OUT = 20;
//temp quotes
private static final String BASE_URL = "https://api.huobi.br.com:443/ws";
Request mRequest;
private okhttp3.WebSocket mWebSocket;
OkHttpClient client;
boolean isConnctted = false;
public WebSocket getWebSocket() {
return mWebSocket;
}
private HuobiWebSocket() {
// 创建 OKHttpClient
OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
builder.connectTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS);//连接超时时间
builder.writeTimeout(DEFAULT_READ_TIME_OUT, TimeUnit.SECONDS);//写操作 超时时间
builder.readTimeout(DEFAULT_READ_TIME_OUT, TimeUnit.SECONDS);//读操作超时时间
builder.pingInterval(5000, TimeUnit.MILLISECONDS);
builder.retryOnConnectionFailure(true);//失败重试
//DEBUG模式下 添加日志拦截器
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
builder.addInterceptor(interceptor);
}
// 添加公共参数拦截器
HttpCommonInterceptor commonInterceptor = new HttpCommonInterceptor.Builder()
.addHeaderParams("paltform", "android")
.addHeaderParams("userToken", "1234343434dfdfd3434")
.addHeaderParams("userId", "123445")
.build();
//builder.addInterceptor(commonInterceptor);
//HuobiWebSocket
client = builder.build();
}
public void init() {
mRequest = new Request.Builder().url(BASE_URL).build();
mWebSocket = client.newWebSocket(mRequest, new WebSocketListener() {
@Override
public void onOpen(okhttp3.WebSocket webSocket, Response response) {
super.onOpen(webSocket, response);
isConnctted = true;
Log.d(TAG, "onOpen: ");
//Toast.makeText(MyApplication.mAppContext, "websocket连接成功...", Toast.LENGTH_SHORT).show();
}
@Override
public void onMessage(okhttp3.WebSocket webSocket, String text) {
super.onMessage(webSocket, text);
Log.d(TAG, "onMessage: " + text);
HuobiSocketParser.fix2Object(text);
}
@Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
super.onMessage(webSocket, bytes);
String s = null;
try {
s = GZipUtil.uncompressBytes(bytes.toByteArray());
HuobiSocketParser.fix2Object(s);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onClosed(okhttp3.WebSocket webSocket, int code, String reason) {
super.onClosed(webSocket, code, reason);
isConnctted = false;
Log.d(TAG, "onClosed,code: " + code + ",reason:" + reason);
//Toast.makeText(MyApplication.mAppContext, "websocket关闭...", Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(okhttp3.WebSocket webSocket, Throwable t, @Nullable Response response) {
super.onFailure(webSocket, t, response);
isConnctted = false;
Log.e(TAG, "onFailure: " + (response != null ? response.toString() : "response==null"));
t.printStackTrace();
}
});
}
private static class SingletonHolder {
private static final HuobiWebSocket INSTANCE = new HuobiWebSocket();
}
/**
* 获取RetrofitServiceManager
*
* @return
*/
public static HuobiWebSocket getInstance() {
return HuobiWebSocket.SingletonHolder.INSTANCE;
}
}
@@ -0,0 +1,90 @@
package com.tophold.example.demo.kview.btc.api;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import com.tophold.example.BuildConfig;
public class RetrofitManager {
private static final int DEFAULT_TIME_OUT = 20;//超时时间 5s
private static final int DEFAULT_READ_TIME_OUT = 20;
//temp quotes
// private static final String BASE_URL = "https://api.huobipro.com";
private static final String BASE_URL = "https://api.huobi.br.com";
private Retrofit mRetrofit;
private RetrofitManager() {
// 创建 OKHttpClient
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS);//连接超时时间
builder.writeTimeout(DEFAULT_READ_TIME_OUT, TimeUnit.SECONDS);//写操作 超时时间
builder.readTimeout(DEFAULT_READ_TIME_OUT, TimeUnit.SECONDS);//读操作超时时间
builder.retryOnConnectionFailure(true);//失败重试
//DEBUG模式下 添加日志拦截器
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
builder.addInterceptor(interceptor);
}
// 添加公共参数拦截器
HttpCommonInterceptor commonInterceptor = new HttpCommonInterceptor.Builder()
.addHeaderParams("paltform", "android")
.addHeaderParams("userToken", "1234343434dfdfd3434")
.addHeaderParams("userId", "123445")
.build();
//builder.addInterceptor(commonInterceptor);
// 创建Retrofit
mRetrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
.addConverterFactory(GsonConverterFactory.create())
.client(builder.build())
.build();
}
private static class SingletonHolder {
private static final RetrofitManager INSTANCE = new RetrofitManager();
}
/**
* 获取RetrofitServiceManager
*
* @return
*/
public static RetrofitManager getInstance() {
return SingletonHolder.INSTANCE;
}
/**
* 获取对应的Service
*
* @param service Service class
* @param <T>
* @return
*/
public <T> T create(Class<T> service) {
return mRetrofit.create(service);
}
public static Observable call(Observable<?> observable, Observer observer) {
observable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
return observable;
}
}
@@ -0,0 +1,29 @@
package com.tophold.example.demo.kview.btc.model;
import java.io.Serializable;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2018/03/26 13:29
*
* ============================================================
**/
public class HuobiData<T> implements Serializable{
public String status;
public String ch;
public long ts;
public T data;
@Override
public String toString() {
return "HuobiData{" +
"status='" + status + '\'' +
", ch='" + ch + '\'' +
", ts=" + ts +
", data=" + data +
'}';
}
}
@@ -0,0 +1,35 @@
package com.tophold.example.demo.kview.btc.model;
import java.io.Serializable;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2018/03/26 13:31
*
* ============================================================
**/
public class HuobiQuote implements Serializable{
public long id;
public double open;
public double close;
public double low;
public double high;
public double amount;
public double vol;
public double count;
@Override
public String toString() {
return "HuobiQuote{" +
"id='" + id + '\'' +
", open=" + open +
", close=" + close +
", low=" + low +
", high=" + high +
", amount=" + amount +
", vol=" + vol +
", count=" + count +
'}';
}
}
@@ -0,0 +1,44 @@
package com.tophold.example.demo.kview.btc.model;
import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2018/03/26 13:42
*
* ============================================================
**/
public class HuobiSymbol implements Serializable {
@SerializedName("base-currency")
public String base_currency;
@SerializedName("quote-currency")
public String quote_currency;
@SerializedName("price-precision")
public int price_precision;
@SerializedName("amount-precision")
public int amount_precision;
@SerializedName("symbol-partition")
public String symbol_partition;
public HuobiSymbol(String base_currency, String quote_currency, int price_precision, int amount_precision, String symbol_partition) {
this.base_currency = base_currency;
this.quote_currency = quote_currency;
this.price_precision = price_precision;
this.amount_precision = amount_precision;
this.symbol_partition = symbol_partition;
}
@Override
public String toString() {
return "HuobiSymbol{" +
"base_currency='" + base_currency + '\'' +
", quote_currency='" + quote_currency + '\'' +
", price_precision=" + price_precision +
", amount_precision=" + amount_precision +
", symbol_partition='" + symbol_partition + '\'' +
'}';
}
}
@@ -0,0 +1,123 @@
package com.tophold.example.demo.kview.btc.model;
import java.io.Serializable;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2018/03/26 16:07
*
* ============================================================
**/
public class HuobiTab implements Serializable {
public HuobiTab.HuobiType mForexType;
public String tabName;
public HuobiTab(HuobiTab.HuobiType forexType, String tabName) {
this.mForexType = forexType;
this.tabName = tabName;
}
/**
* 获取对应类型的长度
*
* @return sencond
*/
public long getTypeLength() {
long len = 0;
switch (mForexType) {
case _timeShring:
len = 60;
break;
case _1m:
len = 60;
break;
case _5m:
len = 5 * 60;
break;
case _15m:
len = 15 * 60;
break;
case _30m:
len = 30 * 60;
break;
case _60m:
len = 60 * 60;
break;
case _1d:
len = 24 * 60 * 60;
break;
case _1w:
len = 7 * 24 * 60 * 60;
break;
case _1mon:
len = 30 * 24 * 60 * 60;
break;
case _1y:
len = 365 * 24 * 60 * 60;
break;
default:
len = 60;
break;
}
return len;
}
/**
* 根据mForexType返回接口需要的类型字段
*
* @return
*/
public String getType() {
String typeMsg = "1min";
switch (mForexType) {
case _timeShring:
typeMsg = "1min";
break;
case _1m:
typeMsg = "1min";
break;
case _5m:
typeMsg = "5min";
break;
case _15m:
typeMsg = "15min";
break;
case _30m:
typeMsg = "30min";
break;
case _60m:
typeMsg = "60min";
break;
case _1d:
typeMsg = "1day";
break;
case _1w:
typeMsg = "1week";
break;
case _1mon:
typeMsg = "1mon";
break;
case _1y:
typeMsg = "1year";
break;
default:
typeMsg = "1min";
break;
}
return typeMsg;
}
public enum HuobiType {
_timeShring,
_1m,
_5m,
_15m,
_30m,
_60m,
_1d,
_1w,
_1mon,
_1y
}
}
@@ -0,0 +1,45 @@
package com.tophold.example.demo.kview.btc.model;
import java.io.Serializable;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2018/03/28 18:39
*
* ============================================================
**/
public class HuobiWsQuote implements Serializable{
/**
* ch : market.eosusdt.kline.1min
* ts : 1522233524438
* tick : {"id":1522233480,"open":6.317,"close":6.3216,"low":6.3091,"high":6.3216,"amount":2024.6227,"vol":12789.80758224,"count":21}
*/
public String ch;
public long ts;
public TickBean tick;
public static class TickBean {
/**
* id : 1522233480
* open : 6.317
* close : 6.3216
* low : 6.3091
* high : 6.3216
* amount : 2024.6227
* vol : 12789.80758224
* count : 21
*/
public int id;
public double open;
public double close;
public double low;
public double high;
public double amount;
public double vol;
public int count;
}
}
@@ -0,0 +1,199 @@
package com.tophold.example.demo.kview.btc.ui;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.support.annotation.ColorRes;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import com.tophold.example.base.BaseActivity;
import com.tophold.example.utils.NoScrollViewPager;
import com.tophold.example.R;
import com.tophold.example.demo.kview.btc.model.HuobiSymbol;
import com.tophold.example.demo.kview.btc.model.HuobiTab;
import com.tophold.trade.utils.FormatUtil;
import com.tophold.trade.utils.TimeUtils;
import com.tophold.trade.view.kview.Quotes;
public class HuobiActivity extends BaseActivity {
public static final String KEY_FOREX = "KEY_FOREX";
public static final String KEY_HORIZONTAL = "KEY_HORIZONTAL";
private TabLayout mAfTlTablayout;
private NoScrollViewPager mAfVpViewpager;
private LinearLayout mAkvLlContainer;
private TextView mAkvTvH;
private TextView mAkvTvO;
private TextView mAkvTvTime;
private TextView mAkvTvL;
private TextView mAkvTvC;
private TextView mAkvTvP;
private TextView af_tv_symbol;
private TextView mAkvTvBlank;
//是否是横屏
boolean mIsHorizontal = false;
private List<HuobiTab> mForexTabList;
private List<Fragment> mKViewFragmentList;
HuobiSymbol mForex;
@Override
protected void getBundleExtras(Bundle extras) {
super.getBundleExtras(extras);
mForex = (HuobiSymbol) extras.getSerializable(KEY_FOREX);
mIsHorizontal = extras.getBoolean(KEY_HORIZONTAL);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (mIsHorizontal) {
requestWindowFeature(Window.FEATURE_NO_TITLE);// 隐藏标题
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);// 设置全屏
}
setContentView(R.layout.activity_huobi);
initView();
initData();
initAdapter();
loadData();
initListener();
}
@Override
protected void onResume() {
if (mIsHorizontal) {
if (getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
}
super.onResume();
}
private void initView() {
mAfTlTablayout = (TabLayout) findViewById(R.id.af_tl_tablayout);
mAfVpViewpager = (NoScrollViewPager) findViewById(R.id.af_vp_viewpager);
mAkvLlContainer = (LinearLayout) findViewById(R.id.akv_ll_container);
mAkvTvH = (TextView) findViewById(R.id.akv_tv_h);
mAkvTvO = (TextView) findViewById(R.id.akv_tv_o);
mAkvTvTime = (TextView) findViewById(R.id.akv_tv_time);
mAkvTvL = (TextView) findViewById(R.id.akv_tv_l);
mAkvTvC = (TextView) findViewById(R.id.akv_tv_c);
mAkvTvP = (TextView) findViewById(R.id.akv_tv_p);
mAkvTvBlank = (TextView) findViewById(R.id.af_tv_blank);
af_tv_symbol = (TextView) findViewById(R.id.af_tv_symbol);
if (mIsHorizontal) {
af_tv_symbol.setVisibility(View.GONE);
mAkvTvBlank.setVisibility(View.GONE);
} else {
af_tv_symbol.setVisibility(View.VISIBLE);
mAkvTvBlank.setVisibility(View.VISIBLE);
af_tv_symbol.setText(mForex.base_currency + "/" + mForex.quote_currency);
}
}
private void initData() {
mForexTabList = new ArrayList<>();
mForexTabList.add(new HuobiTab(HuobiTab.HuobiType._timeShring, "分时"));
mForexTabList.add(new HuobiTab(HuobiTab.HuobiType._1m, "1分"));
mForexTabList.add(new HuobiTab(HuobiTab.HuobiType._5m, "5分"));
mForexTabList.add(new HuobiTab(HuobiTab.HuobiType._15m, "15分"));
mForexTabList.add(new HuobiTab(HuobiTab.HuobiType._30m, "30分"));
mForexTabList.add(new HuobiTab(HuobiTab.HuobiType._60m, "1小时"));
mForexTabList.add(new HuobiTab(HuobiTab.HuobiType._1d, "日K"));
mForexTabList.add(new HuobiTab(HuobiTab.HuobiType._1w, "周K"));
mForexTabList.add(new HuobiTab(HuobiTab.HuobiType._1mon, "月K"));
mForexTabList.add(new HuobiTab(HuobiTab.HuobiType._1y, "年K"));
mAfTlTablayout.setTabMode(TabLayout.MODE_SCROLLABLE);
mKViewFragmentList = new ArrayList<>();
for (HuobiTab forexTab : mForexTabList) {
mKViewFragmentList.add(HuobiFragment.newInstance(forexTab, mForex));
}
}
private void initAdapter() {
mAfVpViewpager.setAdapter(new HuobiAdapter(getSupportFragmentManager(), mKViewFragmentList, mForexTabList));
mAfTlTablayout.setupWithViewPager(mAfVpViewpager);
}
private void loadData() {
}
private void initListener() {
}
public void hidenContainer() {
mAfTlTablayout.setVisibility(View.VISIBLE);
mAkvLlContainer.setVisibility(View.GONE);
}
public void showContanier(Quotes preQuotes, Quotes currentQuotes) {
mAfTlTablayout.setVisibility(View.GONE);
mAkvLlContainer.setVisibility(View.VISIBLE);
int digits = 5;
boolean isPositive;
String precent;
double dis = (currentQuotes.c - preQuotes.c) / currentQuotes.c * 100;
isPositive = dis >= 0;
precent = FormatUtil.formatBySubString(dis, 2);
precent += "%";
//
mAkvTvH.setText(FormatUtil.numFormat(currentQuotes.h, digits));
mAkvTvO.setText(FormatUtil.numFormat(currentQuotes.o, digits));
mAkvTvL.setText(FormatUtil.numFormat(currentQuotes.l, digits));
mAkvTvC.setText(FormatUtil.numFormat(currentQuotes.c, digits));
mAkvTvP.setText(precent);
mAkvTvTime.setText(TimeUtils.millis2String(currentQuotes.t, new SimpleDateFormat("MM-dd HH:mm")));
if (isPositive) {
mAkvTvH.setTextColor(getMyColor(R.color.color_timeSharing_callBackRed));
mAkvTvO.setTextColor(getMyColor(R.color.color_timeSharing_callBackRed));
mAkvTvL.setTextColor(getMyColor(R.color.color_timeSharing_callBackRed));
mAkvTvC.setTextColor(getMyColor(R.color.color_timeSharing_callBackRed));
mAkvTvP.setTextColor(getMyColor(R.color.color_timeSharing_callBackRed));
} else {
mAkvTvH.setTextColor(getMyColor(R.color.color_timeSharing_callBackGreen));
mAkvTvO.setTextColor(getMyColor(R.color.color_timeSharing_callBackGreen));
mAkvTvL.setTextColor(getMyColor(R.color.color_timeSharing_callBackGreen));
mAkvTvC.setTextColor(getMyColor(R.color.color_timeSharing_callBackGreen));
mAkvTvP.setTextColor(getMyColor(R.color.color_timeSharing_callBackGreen));
}
}
private int getMyColor(@ColorRes int colorId) {
return getResources().getColor(colorId);
}
public void showHorizontal(View view) {
Bundle bundle = new Bundle();
bundle.putBoolean(KEY_HORIZONTAL, true);
bundle.putSerializable(KEY_FOREX, mForex);
go(HuobiActivity.class, bundle);
}
public boolean isHorizontal() {
return mIsHorizontal;
}
}
@@ -0,0 +1,43 @@
package com.tophold.example.demo.kview.btc.ui;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import java.util.List;
import com.tophold.example.demo.kview.btc.model.HuobiTab;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2018/03/26 13:37
*
* ============================================================
**/
public class HuobiAdapter extends FragmentPagerAdapter {
private List<Fragment> mFragmentList;
List<HuobiTab> mForexTabList;
public HuobiAdapter(FragmentManager fm, List<Fragment> fragmentList, List<HuobiTab> mForexTabList) {
super(fm);
this.mFragmentList = fragmentList;
this.mForexTabList = mForexTabList;
}
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
@Override
public int getCount() {
return mFragmentList.size();
}
@Override
public CharSequence getPageTitle(int position) {
return mForexTabList.get(position).tabName;
}
}
@@ -0,0 +1,242 @@
package com.tophold.example.demo.kview.btc.ui;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
import com.tophold.example.base.BaseFragment;
import com.tophold.example.R;
import com.tophold.example.demo.kview.btc.api.HuobiService;
import com.tophold.example.demo.kview.btc.api.HuobiSocketApi;
import com.tophold.example.demo.kview.btc.api.RetrofitManager;
import com.tophold.example.demo.kview.btc.model.HuobiData;
import com.tophold.example.demo.kview.btc.model.HuobiQuote;
import com.tophold.example.demo.kview.btc.model.HuobiSymbol;
import com.tophold.example.demo.kview.btc.model.HuobiTab;
import com.tophold.example.demo.kview.btc.model.HuobiWsQuote;
import com.tophold.trade.utils.StringUtils;
import com.tophold.trade.view.kview.KView;
import com.tophold.trade.view.kview.KViewListener;
import com.tophold.trade.view.kview.Quotes;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2018/03/26 13:36
*
* ============================================================
**/
public class HuobiFragment extends BaseFragment {
public static final int DEF_PAGER_SIZE = 1000;
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
public static final int DEF_COUNT = 100;
private View mRootView;
private KView mFkKvKview;
HuobiTab mForexTab;
HuobiSymbol mForex;
//net
List<Quotes> mQuotesList;
public HuobiFragment() {
}
public static HuobiFragment newInstance(HuobiTab mForexTab, HuobiSymbol forex) {
HuobiFragment fragment = new HuobiFragment();
Bundle args = new Bundle();
args.putSerializable(ARG_PARAM1, mForexTab);
args.putSerializable(ARG_PARAM2, forex);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mForexTab = (HuobiTab) getArguments().getSerializable(ARG_PARAM1);
mForex = (HuobiSymbol) getArguments().getSerializable(ARG_PARAM2);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mRootView = inflater.inflate(R.layout.fragment_huobi, container, false);
initView();
initData();
initAdapter();
loadData();
initListener();
return mRootView;
}
private void loadData() {
loadKLineData(null, false);
subKLineData();
}
private void subKLineData() {
HuobiSocketApi.subKLine(mForex.base_currency + mForex.quote_currency, mForexTab.getTypeLength() * 1000, true);
}
private void initView() {
mFkKvKview = (KView) mRootView.findViewById(R.id.fk_kv_kview);
mFkKvKview.setDigit(5);
if (mForexTab.mForexType == HuobiTab.HuobiType._timeShring) {
mFkKvKview.setShowTimSharing(true);
} else {
mFkKvKview.setShowTimSharing(false);
}
if (((HuobiActivity) getActivity()).isHorizontal()) {
mFkKvKview.setShowMinor(true);
mFkKvKview.setShowVol(true);
} else {
mFkKvKview.setShowMinor(false);
mFkKvKview.setShowVol(false);
}
}
private void initData() {
mQuotesList = new ArrayList<>();
}
private void initAdapter() {
}
private void loadKLineData(String start_time, final boolean isLoadMore) {
Map<String, Object> map = new HashMap<>();
String symbol = mForex.base_currency + "" + mForex.quote_currency;
map.put("symbol", symbol);
map.put("period", mForexTab.getType());
map.put("size", DEF_PAGER_SIZE);
//if (start_time != null) map.put("st", start_time);
call(RetrofitManager.getInstance().create(HuobiService.class).chartQuotes(map), new Observer<HuobiData<List<HuobiQuote>>>() {
@Override
public void onSubscribe(Disposable d) {
unSubscription(d);
}
@Override
public void onNext(HuobiData<List<HuobiQuote>> huobiData) {
String status = huobiData.status;
if (!"ok".equals(status)) {
Toast.makeText(mContext, "数据获取失败...", Toast.LENGTH_SHORT).show();
return;
}
List<HuobiQuote> data = huobiData.data;
if (StringUtils.isEmpty(data)) {
Toast.makeText(mContext, "数据为空...", Toast.LENGTH_SHORT).show();
return;
}
adapterData(data);
drawKView(isLoadMore);
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onComplete() {
}
});
}
private void drawKView(boolean isLoadMore) {
if (!isLoadMore) {
mFkKvKview.setKViewData(mQuotesList, new KViewListener.MasterTouchListener() {
@Override
public void onLongTouch(Quotes preQuotes, Quotes currentQuotes) {
((HuobiActivity) getActivity()).showContanier(preQuotes, currentQuotes);
}
@Override
public void onUnLongTouch() {
((HuobiActivity) getActivity()).hidenContainer();
}
@Override
public void needLoadMore() {
if (StringUtils.isEmpty(mQuotesList)) {
mFkKvKview.loadMoreNoData();
return;
}
Quotes quotes = mQuotesList.get(0);
String showTime = quotes.getShowTime();
//loadKLineData(showTime, true);火币不支持分页
}
});
} else {
mFkKvKview.loadKViewData(mQuotesList);
}
}
private void adapterData(List<HuobiQuote> huobiQuoteList) {
Collections.reverse(huobiQuoteList);
List<Quotes> quotesList = new ArrayList<>();
for (HuobiQuote huobiQuote : huobiQuoteList) {
Quotes quotes = new Quotes(huobiQuote.open, huobiQuote.high, huobiQuote.low, huobiQuote.close, huobiQuote.id * 1000, huobiQuote.amount);
quotesList.add(quotes);
}
mQuotesList.addAll(0, quotesList);//注意时序问题
}
private void initListener() {
}
@Override
public boolean isBindEventBusHere() {
return true;
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onReviceWsQuotes(HuobiWsQuote huobiWsQuote) {
if (huobiWsQuote == null || huobiWsQuote.tick == null) return;
//返回的数据真实醉了
String ch = huobiWsQuote.ch;
String[] split = ch.split("\\.");
String symbol = split[1];
String lineType = split[3];
if (!symbol.equals(mForex.base_currency + mForex.quote_currency)) return;
if (!lineType.equals(mForexTab.getType())) return;
HuobiWsQuote.TickBean tick = huobiWsQuote.tick;
Quotes quotes = new Quotes(tick.open, tick.high, tick.low, tick.close, huobiWsQuote.ts);
mFkKvKview.pushKViewData(quotes, mForexTab.getTypeLength() * 1000);
}
@Override
public void onDestroy() {
super.onDestroy();
HuobiSocketApi.subKLine(mForex.base_currency + mForex.quote_currency, mForexTab.getTypeLength() * 1000, false);
}
}
@@ -0,0 +1,122 @@
package com.tophold.example.demo.kview.btc.ui;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.widget.Toast;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
import com.tophold.example.base.BaseActivity;
import com.tophold.example.R;
import com.tophold.example.demo.kview.btc.api.HuobiService;
import com.tophold.example.demo.kview.btc.api.RetrofitManager;
import com.tophold.example.demo.kview.btc.model.HuobiData;
import com.tophold.example.demo.kview.btc.model.HuobiSymbol;
import com.tophold.trade.utils.StringUtils;
public class HuobiListActivity extends BaseActivity {
RecyclerView mAflRlListview;
BaseQuickAdapter<HuobiSymbol, BaseViewHolder> mQuickAdapter;
List<HuobiSymbol> mForexList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_huobi_list);
initView();
initData();
initAdapter();
loadData();
initListener();
}
private void initView() {
mAflRlListview = (RecyclerView) findViewById(R.id.afl_rl_listview);
Toast.makeText(mContext, "请稍后,在拉取列表...", Toast.LENGTH_SHORT).show();
}
private void initData() {
mForexList = new ArrayList<>();
}
private void initAdapter() {
mQuickAdapter = new BaseQuickAdapter<HuobiSymbol, BaseViewHolder>(R.layout.item_activity_huobi_list, mForexList) {
@Override
protected void convert(BaseViewHolder helper, HuobiSymbol item) {
helper.setText(R.id.iafl_tv_znName, item.base_currency + "/" + item.quote_currency);
helper.setText(R.id.iafl_tv_enName, item.quote_currency);
helper.setText(R.id.iafl_tv_part, item.symbol_partition);
}
};
mAflRlListview.setLayoutManager(new LinearLayoutManager(mContext));
mAflRlListview.setAdapter(mQuickAdapter);
}
private void loadData() {
Map<String, Object> map = new HashMap<>();
call(RetrofitManager.getInstance().create(HuobiService.class).getSymbolList(map), new Observer<HuobiData<List<HuobiSymbol>>>() {
@Override
public void onSubscribe(Disposable d) {
unSubscription(d);
}
@Override
public void onNext(HuobiData<List<HuobiSymbol>> huobiData) {
String status = huobiData.status;
if (!"ok".equals(status)) {
Toast.makeText(mContext, "数据获取失败...", Toast.LENGTH_SHORT).show();
return;
}
List<HuobiSymbol> data = huobiData.data;
if (StringUtils.isEmpty(data)) {
Toast.makeText(mContext, "数据为空...", Toast.LENGTH_SHORT).show();
return;
}
mForexList.addAll(data);
//排序操作
Collections.sort(mForexList, (o1, o2) -> {
if (!o1.symbol_partition.equals(o2.symbol_partition))
return -o1.symbol_partition.compareTo(o2.symbol_partition);
if (!o1.quote_currency.equals(o2.quote_currency))
return -o1.quote_currency.compareTo(o2.quote_currency);
return 0;
});
mQuickAdapter.notifyLoadMoreToLoading();
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete: ");
}
});
}
private void initListener() {
mQuickAdapter.setOnItemClickListener((adapter, v, p) -> {
HuobiSymbol forex = mForexList.get(p);
Bundle bundle = new Bundle();
bundle.putSerializable(HuobiActivity.KEY_FOREX, forex);
go(HuobiActivity.class, bundle);
});
}
}
@@ -0,0 +1,123 @@
package com.tophold.example.demo.kview.forex;
import java.io.Serializable;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2018/03/22 14:16
*
* ============================================================
**/
public class ForexTab implements Serializable {
public ForexType mForexType;
public String tabName;
public ForexTab(ForexType forexType, String tabName) {
this.mForexType = forexType;
this.tabName = tabName;
}
/**
* 获取对应类型的长度
*
* @return sencond
*/
public long getTypeLength() {
long len = 0;
switch (mForexType) {
case _timeShring:
len = 60;
break;
case _1m:
len = 60;
break;
case _5m:
len = 5 * 60;
break;
case _15m:
len = 15 * 60;
break;
case _30m:
len = 30 * 60;
break;
case _1h:
len = 60 * 60;
break;
case _4h:
len = 4 * 60 * 60;
break;
case _1d:
len = 24 * 60 * 60;
break;
case _1w:
len = 7 * 24 * 60 * 60;
break;
case _1mon:
len = 30 * 24 * 60 * 60;
break;
default:
len = 60;
break;
}
return len;
}
/**
* 根据mForexType返回接口需要的类型字段
*
* @return
*/
public String getType() {
String typeMsg = "1m";
switch (mForexType) {
case _timeShring:
typeMsg = "1m";
break;
case _1m:
typeMsg = "1m";
break;
case _5m:
typeMsg = "5m";
break;
case _15m:
typeMsg = "15m";
break;
case _30m:
typeMsg = "30m";
break;
case _1h:
typeMsg = "1h";
break;
case _4h:
typeMsg = "4h";
break;
case _1d:
typeMsg = "1d";
break;
case _1w:
typeMsg = "1w";
break;
case _1mon:
typeMsg = "1mon";
break;
default:
typeMsg = "1m";
break;
}
return typeMsg;
}
public enum ForexType {
_timeShring,
_1m,
_5m,
_15m,
_30m,
_1h,
_4h,
_1d,
_1w,
_1mon
}
}
@@ -0,0 +1,112 @@
package com.tophold.example.demo.kview.forex.api;
import android.support.annotation.Nullable;
import android.util.Log;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocketListener;
import okhttp3.logging.HttpLoggingInterceptor;
import com.tophold.example.BuildConfig;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2018/03/22 21:15
*
* ============================================================
**/
public class ForexWebSocket {
public static final String TAG = ForexWebSocket.class.getSimpleName();
private static final int DEFAULT_TIME_OUT = 5;//超时时间 5s
private static final int DEFAULT_READ_TIME_OUT = 10;
//temp quotes
private static final String BASE_URL = "http://***:8990/websocket";
private static final String TOKEN="eyJ0eXAiOiJKV1QiLCJhbGc***Y2NvdW50SWQiOiJFVVI2MTQxLjAwMSIsImlhdCI6MTUyMjgxOTE0NiwiZXhwIjoxNTIzNDIzOTQ2fQ.je5e76RSNh07ZYtu3_yOA8BsBl_wH0_tkqxlgX5hhww";
Request mRequest;
okhttp3.WebSocket mWebSocket;
OkHttpClient client;
boolean isConnctted = false;
private ForexWebSocket() {
// 创建 OKHttpClient
OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
builder.connectTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS);//连接超时时间
builder.writeTimeout(DEFAULT_READ_TIME_OUT, TimeUnit.SECONDS);//写操作 超时时间
builder.readTimeout(DEFAULT_READ_TIME_OUT, TimeUnit.SECONDS);//读操作超时时间
builder.retryOnConnectionFailure(true);//失败重试
//DEBUG模式下 添加日志拦截器
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
builder.addInterceptor(interceptor);
}
// 添加公共参数拦截器
HttpCommonInterceptor commonInterceptor = new HttpCommonInterceptor.Builder()
.addHeaderParams("paltform", "android")
.addHeaderParams("userToken", "1234343434dfdfd3434")
.addHeaderParams("userId", "123445")
.build();
builder.addInterceptor(commonInterceptor);
//HuobiWebSocket
client = builder.build();
}
public void init() {
mRequest = new Request.Builder().header("Sec-WebSocket-Protocol", TOKEN).url(BASE_URL).build();
mWebSocket = client.newWebSocket(mRequest, new WebSocketListener() {
@Override
public void onOpen(okhttp3.WebSocket webSocket, Response response) {
super.onOpen(webSocket, response);
isConnctted = true;
Log.d(TAG, "onOpen: ");
//Toast.makeText(MyApplication.mAppContext, "websocket连接成功...", Toast.LENGTH_SHORT).show();
}
@Override
public void onMessage(okhttp3.WebSocket webSocket, String text) {
super.onMessage(webSocket, text);
WebSocketParser.fix2Object(text);
}
@Override
public void onClosed(okhttp3.WebSocket webSocket, int code, String reason) {
super.onClosed(webSocket, code, reason);
isConnctted = false;
Log.d(TAG, "onClosed,code: " + code + ",reason:" + reason);
//Toast.makeText(MyApplication.mAppContext, "websocket关闭...", Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(okhttp3.WebSocket webSocket, Throwable t, @Nullable Response response) {
super.onFailure(webSocket, t, response);
isConnctted = false;
//Toast.makeText(MyApplication.mAppContext, "websocket异常:" + response.toString(), Toast.LENGTH_SHORT).show();
Log.e(TAG, "onFailure: " + (response != null ? response.toString() : "response==null"));
t.printStackTrace();
}
});
}
private static class SingletonHolder {
private static final ForexWebSocket INSTANCE = new ForexWebSocket();
}
/**
* 获取RetrofitServiceManager
*
* @return
*/
public static ForexWebSocket getInstance() {
return ForexWebSocket.SingletonHolder.INSTANCE;
}
}
@@ -0,0 +1,87 @@
package com.tophold.example.demo.kview.forex.api;
import android.util.Log;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
/**
* 拦截器
*
* 向请求头里添加公共参数
* Created by zhouwei on 16/11/10.
*/
public class HttpCommonInterceptor implements Interceptor {
private Map<String,String> mHeaderParamsMap = new HashMap<>();
public HttpCommonInterceptor() {
}
@Override
public Response intercept(Chain chain) throws IOException {
Log.d("HttpCommonInterceptor","add common params");
Request oldRequest = chain.request();
// 添加新的参数添加到url
/* HttpUrl.Builder authorizedUrlBuilder = oldRequest.url()
.newBuilder()
.scheme(oldRequest.url().scheme())
.host(oldRequest.url().host());*/
// 新的请求
Request.Builder requestBuilder = oldRequest.newBuilder();
requestBuilder.method(oldRequest.method(), oldRequest.body());
//添加公共参数,添加到header中
if(mHeaderParamsMap.size() > 0){
for(Map.Entry<String,String> params:mHeaderParamsMap.entrySet()){
requestBuilder.header(params.getKey(),params.getValue());
}
}
Request newRequest = requestBuilder.build();
return chain.proceed(newRequest);
}
public static class Builder{
HttpCommonInterceptor mHttpCommonInterceptor;
public Builder(){
mHttpCommonInterceptor = new HttpCommonInterceptor();
}
public Builder addHeaderParams(String key, String value){
mHttpCommonInterceptor.mHeaderParamsMap.put(key,value);
return this;
}
public Builder addHeaderParams(String key, int value){
return addHeaderParams(key, String.valueOf(value));
}
public Builder addHeaderParams(String key, float value){
return addHeaderParams(key, String.valueOf(value));
}
public Builder addHeaderParams(String key, long value){
return addHeaderParams(key, String.valueOf(value));
}
public Builder addHeaderParams(String key, double value){
return addHeaderParams(key, String.valueOf(value));
}
public HttpCommonInterceptor build(){
return mHttpCommonInterceptor;
}
}
}
@@ -0,0 +1,89 @@
package com.tophold.example.demo.kview.forex.api;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import com.tophold.example.BuildConfig;
public class RetrofitManager {
private static final int DEFAULT_TIME_OUT = 5;//超时时间 5s
private static final int DEFAULT_READ_TIME_OUT = 10;
//temp quotes
private static final String BASE_URL = "http://***:8700/";
private Retrofit mRetrofit;
private RetrofitManager() {
// 创建 OKHttpClient
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS);//连接超时时间
builder.writeTimeout(DEFAULT_READ_TIME_OUT, TimeUnit.SECONDS);//写操作 超时时间
builder.readTimeout(DEFAULT_READ_TIME_OUT, TimeUnit.SECONDS);//读操作超时时间
builder.retryOnConnectionFailure(true);//失败重试
//DEBUG模式下 添加日志拦截器
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
builder.addInterceptor(interceptor);
}
// 添加公共参数拦截器
HttpCommonInterceptor commonInterceptor = new HttpCommonInterceptor.Builder()
.addHeaderParams("paltform", "android")
.addHeaderParams("userToken", "1234343434dfdfd3434")
.addHeaderParams("userId", "123445")
.build();
builder.addInterceptor(commonInterceptor);
// 创建Retrofit
mRetrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
.addConverterFactory(GsonConverterFactory.create())
.client(builder.build())
.build();
}
private static class SingletonHolder {
private static final RetrofitManager INSTANCE = new RetrofitManager();
}
/**
* 获取RetrofitServiceManager
*
* @return
*/
public static RetrofitManager getInstance() {
return SingletonHolder.INSTANCE;
}
/**
* 获取对应的Service
*
* @param service Service class
* @param <T>
* @return
*/
public <T> T create(Class<T> service) {
return mRetrofit.create(service);
}
public static Observable call(Observable<?> observable, Observer observer) {
observable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
return observable;
}
}
@@ -0,0 +1,29 @@
package com.tophold.example.demo.kview.forex.api;
import android.util.Log;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2018/03/22 21:31
*
* ============================================================
**/
public class WebSocketApi {
public static final String TAG = WebSocketApi.class.getSimpleName();
/**
* 订阅价格和取消订阅
*
* @param isSub true:订阅false:取消订阅
*/
public static void subPrice(String symbol, boolean isSub) {
String str = "35=V|55=" + symbol + "|263=" + (isSub ? 1 : 2);
Log.d(TAG, "subPrice: " + str);
if (ForexWebSocket.getInstance().isConnctted) {
ForexWebSocket.getInstance().mWebSocket.send(str);
} else {
Log.e(TAG, "subPrice: websocket未连接");
}
}
}
@@ -0,0 +1,61 @@
package com.tophold.example.demo.kview.forex.api;
import android.util.Log;
import org.greenrobot.eventbus.EventBus;
import java.util.HashMap;
import java.util.Map;
import com.tophold.example.demo.kview.forex.model.WsPrice;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2018/03/22 21:26
*
* ============================================================
**/
public class WebSocketParser {
public static final String TAG = WebSocketParser.class.getSimpleName();
public static void fix2Object(String str) {
try {
if (str == null || str.length() == 0) return;
Log.d(TAG, "fix2Object: " + str);
String[] arrs = str.split("\\|");
Map<String, String> map = new HashMap<>();
for (String arr : arrs) {
String[] items = arr.split("=");
map.put(items[0], items[1]);
}
//maping
map2Obj(map);
} catch (Exception e) {
e.printStackTrace();
}
}
private static void map2Obj(Map<String, String> map) {
String type = map.get("35");
if (type == null) {
Log.e(TAG, "map2Obj: type == null");
return;
}
if (type.equals("V")) {
map2WsPrice(map);
}
}
private static void map2WsPrice(Map<String, String> map) {
WsPrice wsPrice = new WsPrice();
wsPrice.productName = map.get("55");
wsPrice.lastUpdateTime = Long.parseLong(map.get("20068"));
wsPrice.bidPrice = Double.parseDouble(map.get("132"));
wsPrice.askPrice = Double.parseDouble(map.get("133"));
EventBus.getDefault().post(wsPrice);
}
}
@@ -0,0 +1,23 @@
package com.tophold.example.demo.kview.forex.api;
import java.util.List;
import java.util.Map;
import io.reactivex.Observable;
import retrofit2.http.GET;
import retrofit2.http.Path;
import retrofit2.http.QueryMap;
import com.tophold.example.demo.kview.forex.model.XcfdQuotes;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2018/03/22 11:31
*
* ============================================================
**/
public interface XcfdService {
@GET("/kline/{productCode}/{type}")
Observable<List<XcfdQuotes>> chartQuotes(@Path("productCode") String productCode, @Path("type") String type, @QueryMap Map<String, Object> params);
}
@@ -0,0 +1,31 @@
package com.tophold.example.demo.kview.forex.model;
import java.io.Serializable;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2018/03/22 13:03
*
* ============================================================
**/
public class Forex implements Serializable{
public String id;
public String enName;
public String cnName;
public Forex(String id, String enName, String cnName) {
this.id=id;
this.enName=enName;
this.cnName=cnName;
}
@Override
public String toString() {
return "Forex{" +
"id='" + id + '\'' +
", enName='" + enName + '\'' +
", cnName='" + cnName + '\'' +
'}';
}
}
@@ -0,0 +1,36 @@
package com.tophold.example.demo.kview.forex.model;
import com.google.gson.annotations.SerializedName;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2017/12/26 11:38
*
* ============================================================
**/
public class WsPrice {
//TAG:55,COMMENT:产品
@SerializedName("55")
public String productName;
//TAG:20068,COMMENT:系统最后价格时间
@SerializedName("20068")
public long lastUpdateTime;
//TAG:132,COMMENT:--->对应老系统的卖价
@SerializedName("132")
public double bidPrice;
//TAG:133,COMMENT:--->对应老系统的买价
@SerializedName("133")
public double askPrice;
@Override
public String toString() {
return "WsPrice{" +
", productName='" + productName + '\'' +
", bidPrice='" + bidPrice + '\'' +
", askPrice='" + askPrice + '\'' +
"现价:" + ((bidPrice + askPrice) / 2) +
'}';
}
}
@@ -0,0 +1,34 @@
package com.tophold.example.demo.kview.forex.model;
import java.io.Serializable;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2018/03/22 11:52
*
* ============================================================
**/
public class XcfdQuotes implements Serializable{
public String t; //201802080610
public String o;
public String h;
public String l;
public String c;
public long s;//数据开始时间
public long e;//结束时间
@Override
public String toString() {
return "Quotes{" +
"t=" + t +
", o='" + o + '\'' +
", h='" + h + '\'' +
", l='" + l + '\'' +
", c='" + c + '\'' +
", s=" + s +
", e=" + e +
'}';
}
}
@@ -0,0 +1,208 @@
package com.tophold.example.demo.kview.forex.ui;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.support.annotation.ColorRes;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import com.tophold.example.R;
import com.tophold.example.demo.kview.forex.api.WebSocketApi;
import com.tophold.example.demo.kview.forex.model.Forex;
import com.tophold.example.base.BaseActivity;
import com.tophold.example.utils.NoScrollViewPager;
import com.tophold.trade.utils.FormatUtil;
import com.tophold.trade.utils.TimeUtils;
import com.tophold.example.demo.kview.forex.ForexTab;
import com.tophold.trade.view.kview.Quotes;
public class ForexActivity extends BaseActivity {
public static final String KEY_FOREX = "KEY_FOREX";
public static final String KEY_HORIZONTAL = "KEY_HORIZONTAL";
private TabLayout mAfTlTablayout;
private NoScrollViewPager mAfVpViewpager;
private LinearLayout mAkvLlContainer;
private TextView mAkvTvH;
private TextView mAkvTvO;
private TextView mAkvTvTime;
private TextView mAkvTvL;
private TextView mAkvTvC;
private TextView mAkvTvP;
private TextView af_tv_symbol;
private TextView mAkvTvBlank;
//是否是横屏
boolean mIsHorizontal = false;
private List<ForexTab> mForexTabList;
private List<Fragment> mKViewFragmentList;
Forex mForex;
@Override
protected void getBundleExtras(Bundle extras) {
super.getBundleExtras(extras);
mForex = (Forex) extras.getSerializable(KEY_FOREX);
mIsHorizontal = extras.getBoolean(KEY_HORIZONTAL);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (mIsHorizontal) {
requestWindowFeature(Window.FEATURE_NO_TITLE);// 隐藏标题
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);// 设置全屏
}
setContentView(R.layout.activity_forex);
initView();
initData();
initAdapter();
loadData();
initListener();
}
@Override
protected void onResume() {
if (mIsHorizontal) {
if (getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
}
super.onResume();
}
private void initView() {
mAfTlTablayout = (TabLayout) findViewById(R.id.af_tl_tablayout);
mAfVpViewpager = (NoScrollViewPager) findViewById(R.id.af_vp_viewpager);
mAkvLlContainer = (LinearLayout) findViewById(R.id.akv_ll_container);
mAkvTvH = (TextView) findViewById(R.id.akv_tv_h);
mAkvTvO = (TextView) findViewById(R.id.akv_tv_o);
mAkvTvTime = (TextView) findViewById(R.id.akv_tv_time);
mAkvTvL = (TextView) findViewById(R.id.akv_tv_l);
mAkvTvC = (TextView) findViewById(R.id.akv_tv_c);
mAkvTvP = (TextView) findViewById(R.id.akv_tv_p);
mAkvTvBlank = (TextView) findViewById(R.id.af_tv_blank);
af_tv_symbol = (TextView) findViewById(R.id.af_tv_symbol);
if (mIsHorizontal) {
af_tv_symbol.setVisibility(View.GONE);
mAkvTvBlank.setVisibility(View.GONE);
} else {
af_tv_symbol.setVisibility(View.VISIBLE);
mAkvTvBlank.setVisibility(View.VISIBLE);
af_tv_symbol.setText(mForex.cnName + "(" + mForex.enName + ")");
}
}
private void initData() {
mForexTabList = new ArrayList<>();
mForexTabList.add(new ForexTab(ForexTab.ForexType._timeShring, "分时"));
mForexTabList.add(new ForexTab(ForexTab.ForexType._1m, "1分"));
mForexTabList.add(new ForexTab(ForexTab.ForexType._5m, "5分"));
mForexTabList.add(new ForexTab(ForexTab.ForexType._15m, "15分"));
mForexTabList.add(new ForexTab(ForexTab.ForexType._30m, "30分"));
mForexTabList.add(new ForexTab(ForexTab.ForexType._1h, "1时"));
mForexTabList.add(new ForexTab(ForexTab.ForexType._4h, "4时"));
mForexTabList.add(new ForexTab(ForexTab.ForexType._1d, "日K"));
mForexTabList.add(new ForexTab(ForexTab.ForexType._1w, "周K"));
mForexTabList.add(new ForexTab(ForexTab.ForexType._1mon, ""));
mAfTlTablayout.setTabMode(TabLayout.MODE_SCROLLABLE);
mKViewFragmentList = new ArrayList<>();
for (ForexTab forexTab : mForexTabList) {
mKViewFragmentList.add(KViewFragment.newInstance(forexTab, mForex));
}
//加载实时价格数据
WebSocketApi.subPrice(mForex.enName, true);
}
private void initAdapter() {
mAfVpViewpager.setAdapter(new KViewAdapter(getSupportFragmentManager(), mKViewFragmentList, mForexTabList));
mAfTlTablayout.setupWithViewPager(mAfVpViewpager);
}
private void loadData() {
}
private void initListener() {
}
public void hidenContainer() {
mAfTlTablayout.setVisibility(View.VISIBLE);
mAkvLlContainer.setVisibility(View.GONE);
}
public void showContanier(Quotes preQuotes, Quotes currentQuotes) {
mAfTlTablayout.setVisibility(View.GONE);
mAkvLlContainer.setVisibility(View.VISIBLE);
int digits = 5;
boolean isPositive;
String precent;
double dis = (currentQuotes.c - preQuotes.c) / currentQuotes.c * 100;
isPositive = dis >= 0;
precent = FormatUtil.formatBySubString(dis, 2);
precent += "%";
//
mAkvTvH.setText(FormatUtil.numFormat(currentQuotes.h, digits));
mAkvTvO.setText(FormatUtil.numFormat(currentQuotes.o, digits));
mAkvTvL.setText(FormatUtil.numFormat(currentQuotes.l, digits));
mAkvTvC.setText(FormatUtil.numFormat(currentQuotes.c, digits));
mAkvTvP.setText(precent);
mAkvTvTime.setText(TimeUtils.millis2String(currentQuotes.t, new SimpleDateFormat("MM-dd HH:mm")));
if (isPositive) {
mAkvTvH.setTextColor(getMyColor(R.color.color_timeSharing_callBackRed));
mAkvTvO.setTextColor(getMyColor(R.color.color_timeSharing_callBackRed));
mAkvTvL.setTextColor(getMyColor(R.color.color_timeSharing_callBackRed));
mAkvTvC.setTextColor(getMyColor(R.color.color_timeSharing_callBackRed));
mAkvTvP.setTextColor(getMyColor(R.color.color_timeSharing_callBackRed));
} else {
mAkvTvH.setTextColor(getMyColor(R.color.color_timeSharing_callBackGreen));
mAkvTvO.setTextColor(getMyColor(R.color.color_timeSharing_callBackGreen));
mAkvTvL.setTextColor(getMyColor(R.color.color_timeSharing_callBackGreen));
mAkvTvC.setTextColor(getMyColor(R.color.color_timeSharing_callBackGreen));
mAkvTvP.setTextColor(getMyColor(R.color.color_timeSharing_callBackGreen));
}
}
private int getMyColor(@ColorRes int colorId) {
return getResources().getColor(colorId);
}
public void showHorizontal(View view) {
Bundle bundle = new Bundle();
bundle.putBoolean(KEY_HORIZONTAL, true);
bundle.putSerializable(KEY_FOREX, mForex);
go(ForexActivity.class, bundle);
}
public boolean isHorizontal() {
return mIsHorizontal;
}
@Override
protected void onDestroy() {
super.onDestroy();
WebSocketApi.subPrice(mForex.enName, false);//取消订阅
}
}
@@ -0,0 +1,79 @@
package com.tophold.example.demo.kview.forex.ui;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import java.util.ArrayList;
import java.util.List;
import com.tophold.example.R;
import com.tophold.example.demo.kview.forex.model.Forex;
import com.tophold.example.base.BaseActivity;
public class ForexListActivity extends BaseActivity {
RecyclerView mAflRlListview;
BaseQuickAdapter<Forex, BaseViewHolder> mQuickAdapter;
List<Forex> mForexList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_forex_list);
initView();
initData();
initAdapter();
loadData();
initListener();
}
private void initView() {
mAflRlListview = (RecyclerView) findViewById(R.id.afl_rl_listview);
}
private void initData() {
mForexList = new ArrayList<>();
mForexList.add(new Forex("1", "EURUSD", "欧元/美元"));
mForexList.add(new Forex("2", "USDJPY", "美元/日元"));
mForexList.add(new Forex("3", "AUDUSD", "澳元/美元"));
mForexList.add(new Forex("4", "GBPUSD", "英镑/美元"));
mForexList.add(new Forex("5", "USDCHF", "美元/瑞郎"));
mForexList.add(new Forex("6", "USDSGD", "美元/新加坡元"));
mForexList.add(new Forex("7", "USDCAD", "美元/加元"));
mForexList.add(new Forex("8", "EURJPY", "欧元/日元"));
mForexList.add(new Forex("9", "EURGBP", "欧元/英镑"));
mForexList.add(new Forex("10", "NZDUSD", "纽元/美元"));
mForexList.add(new Forex("11", "GBPJYP", "英镑/日元"));
mForexList.add(new Forex("12", "USDMXN", "美元/墨西哥比索"));
mForexList.add(new Forex("13", "GOLD", "黄金"));
mForexList.add(new Forex("14", "SILVER", "白银"));
}
private void initAdapter() {
mQuickAdapter = new BaseQuickAdapter<Forex, BaseViewHolder>(R.layout.item_activity_forex_list, mForexList) {
@Override
protected void convert(BaseViewHolder helper, Forex item) {
helper.setText(R.id.iafl_tv_enName, item.enName);
helper.setText(R.id.iafl_tv_znName, item.cnName);
}
};
mAflRlListview.setLayoutManager(new LinearLayoutManager(mContext));
mAflRlListview.setAdapter(mQuickAdapter);
}
private void loadData() {
}
private void initListener() {
mQuickAdapter.setOnItemClickListener((adapter, v, p) -> {
Forex forex = mForexList.get(p);
Bundle bundle = new Bundle();
bundle.putSerializable(ForexActivity.KEY_FOREX, forex);
go(ForexActivity.class, bundle);
});
}
}
@@ -0,0 +1,43 @@
package com.tophold.example.demo.kview.forex.ui;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import java.util.List;
import com.tophold.example.demo.kview.forex.ForexTab;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2018/03/22 14:38
*
* ============================================================
**/
public class KViewAdapter extends FragmentPagerAdapter {
private List<Fragment> mFragmentList;
List<ForexTab> mForexTabList;
public KViewAdapter(FragmentManager fm, List<Fragment> fragmentList, List<ForexTab> mForexTabList) {
super(fm);
this.mFragmentList = fragmentList;
this.mForexTabList = mForexTabList;
}
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
@Override
public int getCount() {
return mFragmentList.size();
}
@Override
public CharSequence getPageTitle(int position) {
return mForexTabList.get(position).tabName;
}
}
@@ -0,0 +1,197 @@
package com.tophold.example.demo.kview.forex.ui;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
import com.tophold.example.base.BaseFragment;
import com.tophold.example.R;
import com.tophold.example.demo.kview.forex.api.RetrofitManager;
import com.tophold.example.demo.kview.forex.api.XcfdService;
import com.tophold.example.demo.kview.forex.model.Forex;
import com.tophold.example.demo.kview.forex.model.WsPrice;
import com.tophold.example.demo.kview.forex.model.XcfdQuotes;
import com.tophold.trade.utils.StringUtils;
import com.tophold.example.demo.kview.forex.ForexTab;
import com.tophold.trade.view.kview.KView;
import com.tophold.trade.view.kview.KViewListener;
import com.tophold.trade.view.kview.Quotes;
public class KViewFragment extends BaseFragment {
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
public static final int DEF_COUNT = 100;
private View mRootView;
private KView mFkKvKview;
ForexTab mForexTab;
Forex mForex;
//net
List<Quotes> mQuotesList;
public KViewFragment() {
}
public static KViewFragment newInstance(ForexTab mForexTab, Forex forex) {
KViewFragment fragment = new KViewFragment();
Bundle args = new Bundle();
args.putSerializable(ARG_PARAM1, mForexTab);
args.putSerializable(ARG_PARAM2, forex);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mForexTab = (ForexTab) getArguments().getSerializable(ARG_PARAM1);
mForex = (Forex) getArguments().getSerializable(ARG_PARAM2);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mRootView = inflater.inflate(R.layout.fragment_kview, container, false);
initView();
initData();
initAdapter();
loadData(null, false);
initListener();
return mRootView;
}
private void initView() {
mFkKvKview = (KView) mRootView.findViewById(R.id.fk_kv_kview);
mFkKvKview.setDigit(5);
if (mForexTab.mForexType == ForexTab.ForexType._timeShring) {
mFkKvKview.setShowTimSharing(true);
} else {
mFkKvKview.setShowTimSharing(false);
}
if (((ForexActivity) getActivity()).isHorizontal()) {
mFkKvKview.setShowMinor(true);
} else {
mFkKvKview.setShowMinor(false);
}
}
private void initData() {
mQuotesList = new ArrayList<>();
}
private void initAdapter() {
}
private void loadData(String start_time, final boolean isLoadMore) {
Map<String, Object> map = new HashMap<>();
map.put("count", DEF_COUNT);
if (start_time != null) map.put("st", start_time);
call(RetrofitManager.getInstance().create(XcfdService.class).chartQuotes(mForex.enName, mForexTab.getType(), map), new Observer<List<XcfdQuotes>>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(List<XcfdQuotes> xcfdQuotesList) {
if (StringUtils.isEmpty(xcfdQuotesList)) {
Log.d(TAG, "onNext: 数据为空");
return;
}
adapterData(xcfdQuotesList);
drawKView(isLoadMore);
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
Toast.makeText(mContext, "" + e.getMessage(), Toast.LENGTH_SHORT).show();
}
@Override
public void onComplete() {
}
});
}
private void drawKView(boolean isLoadMore) {
if (!isLoadMore) {
mFkKvKview.setKViewData(mQuotesList, new KViewListener.MasterTouchListener() {
@Override
public void onLongTouch(Quotes preQuotes, Quotes currentQuotes) {
((ForexActivity) getActivity()).showContanier(preQuotes, currentQuotes);
}
@Override
public void onUnLongTouch() {
((ForexActivity) getActivity()).hidenContainer();
}
@Override
public void needLoadMore() {
if (StringUtils.isEmpty(mQuotesList)) {
mFkKvKview.loadMoreNoData();
return;
}
Quotes quotes = mQuotesList.get(0);
String showTime = quotes.getShowTime();
loadData(showTime, true);
}
});
} else {
mFkKvKview.loadKViewData(mQuotesList);
}
}
private void adapterData(List<XcfdQuotes> xcfdQuotesList) {
List<Quotes> quotesList = new ArrayList<>();
for (XcfdQuotes xcfdQuotes : xcfdQuotesList) {
Quotes quotes = new Quotes(xcfdQuotes.o, xcfdQuotes.h, xcfdQuotes.l, xcfdQuotes.c, xcfdQuotes.s, xcfdQuotes.e);
quotesList.add(quotes);
}
mQuotesList.addAll(0, quotesList);//注意时序问题
}
private void initListener() {
}
@Override
public boolean isBindEventBusHere() {
return true;
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onRevicePrice(WsPrice wsPrice) {
if (wsPrice == null || !wsPrice.productName.equals(mForex.enName)) return;
double currPrice = (wsPrice.askPrice + wsPrice.bidPrice) / 2.0;//均价
String currPriceStr = String.valueOf(currPrice);
Quotes quotes = new Quotes(currPriceStr, currPriceStr, currPriceStr, currPriceStr, wsPrice.lastUpdateTime, wsPrice.lastUpdateTime);
mFkKvKview.pushKViewData(quotes, mForexTab.getTypeLength() * 1000);
}
}
@@ -0,0 +1,41 @@
package com.tophold.example.demo.pie
import android.os.Bundle
import android.support.annotation.ColorInt
import android.support.v4.content.ContextCompat
import com.tophold.example.base.BaseActivity
import com.tophold.example.R
import com.tophold.trade.view.pie.PieEntrys
import kotlinx.android.synthetic.main.activity_pie_chart.*
class PieChartActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_pie_chart)
initPieChart()
loadPieChart()
}
private fun loadPieChart() {
val pieEntrysList: MutableList<PieEntrys> = ArrayList()
pieEntrysList.add(PieEntrys(10f, "80%", getColorId(R.color.trade_analyze_pie_1), false, "澳元/美元"))
pieEntrysList.add(PieEntrys(50f, "胜率 86.96%", getColorId(R.color.trade_analyze_pie_2), true, "英镑/美元"))
pieEntrysList.add(PieEntrys(1f, "60.71%", getColorId(R.color.trade_analyze_pie_3), false, "黄金"))
pieEntrysList.add(PieEntrys(1f, "76.47%", getColorId(R.color.trade_analyze_pie_4), false, "欧元/美元"))
hatab_tapc_chart.pieEntryList = pieEntrysList
}
private fun initPieChart() {
hatab_tapc_chart.basePaddingTop = 40f
hatab_tapc_chart.basePaddingBottom = 40f
}
@ColorInt
private fun getColorId(colorId: Int): Int {
return ContextCompat.getColor(mContext,colorId)
}
}
@@ -0,0 +1,145 @@
package com.tophold.example.demo.seekbar
import android.os.Bundle
import android.support.annotation.NonNull
import com.tophold.example.base.BaseActivity
import com.tophold.example.R
import kotlinx.android.synthetic.main.activity_double_thumb_seek_bar.*
import com.tophold.trade.utils.RenderUtils.dp2px
import com.tophold.trade.view.seekbar.DoubleTumb
class DoubleThumbSeekBarActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_double_thumb_seek_bar)
renderView1()
}
private fun renderView1() {
/**
* def
*/
am_dtsv_seek0.init(loadTpSeekBar())
/**
* 加载单个thumb,从左边开始且最小值从最左边开始
*/
am_dtsv_seek1.init(loadTpSeekBar(
false, -44.0, 66.0, true, 4,
true, false, 0.2, greenA = false))
/**
* 单个thumb,从右边开始且从左边开始最小值
*/
am_dtsv_seek2.init(loadTpSeekBar(
false, -44.0, 66.0, true, 4,
false, true, 0.2, greenA = true))
/**
* 双thumb同向,最小值从左边开始
*/
am_dtsv_seek3.init(loadTpSeekBar(
true, -44.0, 66.0, true, 4,
true, true, 0.2, false,
true, true, 0.33, false))
/**
* 双thumb同向,最小值从右边开始
*/
am_dtsv_seek4.init(loadTpSeekBar(
true, -44.0, 66.0, false, 4,
false, true, 0.2, true,
false, true, 0.33, true))
/**
* 交叉方式显示
*/
am_dtsv_seek5.init(loadTpSeekBar(
true, -44.0, 66.0, false, 4,
true, true, 0.2, false,
false, true, 0.33, true))
}
/**
* @param doubleThumb 是否启动双thumb
* @param min 最小值
* @param max 最大值
* @param minFromLeft 最小值是否从左边开始注意和从哪边开始滑动无关
* @param digit 格式化的小数位数
*
* @param fromLeftA 是否从左边开始滑动
* @param showTipsA 是否实时展示当前值
* @param progressA 默认进度0~1
* @param greenA thumbA的背景色
*
* @param fromLeftB 是否从左边开始滑动
* @param showTipsB 是否实时展示当前值
* @param progressB 默认进度0~1
* @param greenB thumbA的背景色
*
*/
@NonNull
fun loadTpSeekBar(doubleThumb: Boolean = false, min: Double = 0.0, max: Double = 100.0, minFromLeft: Boolean = true, digit: Int = 2,
fromLeftA: Boolean = true, showTipsA: Boolean = true, progressA: Double = 0.0, greenA: Boolean = false,
fromLeftB: Boolean = true, showTipsB: Boolean = true, progressB: Double = 0.0, greenB: Boolean = false): DoubleTumb {
val thumbAForeground = if (!greenA) {
getCompatColor(R.color.dtsb_thumb1Color)
} else {
getCompatColor(R.color.dtsb_thumb2Color)
}
val thumbADrawable = if (!greenA) {
getCompatDrawable(R.drawable.layer_seekbar_thumb_red)
} else {
getCompatDrawable(R.drawable.layer_seekbar_thumb_green)
}
val thumbA = DoubleTumb.Thumb(thumbAForeground)
thumbA.progress = progressA//默认位置,百分比值(0~1
thumbA.mFromLeft = fromLeftA//是否从左边开始滑动
thumbA.mShowTips = showTipsA
thumbA.setmThumb(thumbADrawable)
thumbA.setmThumbWidth(dp2px(mContext, 20f))
thumbA.setmThumbHight(dp2px(mContext, 25f))
var thumbB: DoubleTumb.Thumb? = null
//复杂模式显示两个thumb
if (doubleThumb) {
val thumbBForeground = if (!greenB) {
getCompatColor(R.color.dtsb_thumb11Color)
} else {
getCompatColor(R.color.dtsb_thumb22Color)
}
val thumbBDrawable = if (!greenB) {
getCompatDrawable(R.drawable.layer_seekbar_thumb_red)
} else {
getCompatDrawable(R.drawable.layer_seekbar_thumb_green)
}
thumbB = DoubleTumb.Thumb(thumbBForeground)
thumbB.progress = progressB
thumbB.mFromLeft = fromLeftB
thumbB.mShowTips = showTipsB
thumbB.setmThumb(thumbBDrawable)
thumbB.setmThumbWidth(dp2px(mContext, 20f))
thumbB.setmThumbHight(dp2px(mContext, 25f))
}
val doubleTumb = DoubleTumb(min, max,
dp2px(mContext, 4f).toFloat(),
getCompatColor(R.color.dtsb_bgColor),
thumbA,
thumbB)
//注意这里!!!
doubleTumb.mMinLeft = minFromLeft
doubleTumb.mDigit = digit
//根据总高度取设置
doubleTumb.mLineTop = dp2px(mContext, 40f)
doubleTumb.mLineBottom = dp2px(mContext, 10f)
doubleTumb.mLineLeft = dp2px(mContext, 8f)
doubleTumb.mLineRight = dp2px(mContext, 8f)
return doubleTumb
}
}
@@ -0,0 +1,75 @@
package com.tophold.example.utils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.Map;
/**
* <p>GSON工具类</p>
*
* @author
* @version $Id: GsonUtil.java
*/
public class GsonUtil {
private static Gson gson = null;
private static Gson prettyGson = null;
static {
gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd HH:mm:ss").create();
prettyGson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss")
.setPrettyPrinting()
.create();
}
/**
* 小写下划线的格式解析JSON字符串到对象
* <p>例如 is_success->isSuccess</p>
*
* @param json
* @param classOfT
* @return
*/
public static <T> T fromJsonUnderScoreStyle(String json, Class<T> classOfT) {
return gson.fromJson(json, classOfT);
}
/**
* JSON字符串转为Map<String,String>
*
* @param json
* @return
*/
@SuppressWarnings("all")
public static <T> T fronJson2Map(String json) {
return gson.fromJson(json, new TypeToken<Map<String, String>>() {
}.getType());
}
/**
* 小写下划线的格式将对象转换成JSON字符串
*
* @param src
* @return
*/
public static String toJson(Object src) {
return gson.toJson(src);
}
public static String toPrettyString(Object src) {
return prettyGson.toJson(src);
}
public static <T> T fromJson2Object(String src, Class<T> t) {
return gson.fromJson(src, t);
}
public static <T> T fromJson2Object(String src, Type typeOfT) {
return gson.fromJson(src, typeOfT);
}
}
@@ -0,0 +1,33 @@
package com.tophold.example.utils;
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
/**
* ============================================================
* : wgyscsf@163.com
* 创建日期 2018/03/22 14:55
*
* ============================================================
**/
public class NoScrollViewPager extends ViewPager {
public NoScrollViewPager(Context context) {
super(context);
}
public NoScrollViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent arg0) {
return false;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent arg0) {
return false;
}
}
@@ -0,0 +1,67 @@
package com.tophold.example.utils;
import io.reactivex.FlowableTransformer;
import io.reactivex.Observable;
import io.reactivex.ObservableTransformer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
/**
* Created by chao.qu at 2017/10/20
*
* @author quchao
*/
public class RxUtils {
/**
* 统一线程处理
*
* @param <T> 指定的泛型类型
* @return FlowableTransformer
*/
public static <T> FlowableTransformer<T, T> rxFlSchedulerHelper() {
return flowable -> flowable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
/**
* 统一线程处理
*
* @param <T> 指定的泛型类型
* @return ObservableTransformer
*/
public static <T> ObservableTransformer<T, T> rxSchedulerHelper() {
return observable -> observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
/**
* 统一线程处理
*
* @param <T> 指定的泛型类型
* @return ObservableTransformer
*/
public static <T> ObservableTransformer<T, T> rxApiSchedulerHelper() {
return observable -> observable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
/**
* 得到 Observable
*
* @param <T> 指定的泛型类型
* @return Observable
*/
private static <T> Observable<T> createData(final T t) {
return Observable.create(emitter -> {
try {
emitter.onNext(t);
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
});
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

@@ -0,0 +1,11 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape>
<size
android:height="20dp"
android:width="18dp"/>
<solid android:color="@android:color/transparent" />
</shape>
</item>
<item android:drawable="@drawable/ico_sl"/>
</layer-list>
@@ -0,0 +1,11 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape>
<size
android:height="20dp"
android:width="18dp"/>
<solid android:color="@android:color/transparent" />
</shape>
</item>
<item android:drawable="@drawable/ico_tp"/>
</layer-list>
@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.tophold.trade.view.seekbar.DoubleThumbSeekBar
android:id="@+id/am_dtsv_seek0"
android:layout_width="match_parent"
android:layout_height="54dp"
android:layout_marginLeft="4dp"
android:layout_marginTop="10dp"
android:layout_marginRight="4dp"
android:background="#fff" />
<com.tophold.trade.view.seekbar.DoubleThumbSeekBar
android:id="@+id/am_dtsv_seek1"
android:layout_width="match_parent"
android:layout_height="54dp"
android:layout_marginLeft="4dp"
android:layout_marginTop="10dp"
android:layout_marginRight="4dp"
android:background="#fff" />
<com.tophold.trade.view.seekbar.DoubleThumbSeekBar
android:id="@+id/am_dtsv_seek2"
android:layout_width="match_parent"
android:layout_height="54dp"
android:layout_marginLeft="4dp"
android:layout_marginTop="20dp"
android:layout_marginRight="4dp"
android:background="#fff" />
<com.tophold.trade.view.seekbar.DoubleThumbSeekBar
android:id="@+id/am_dtsv_seek3"
android:layout_width="match_parent"
android:layout_height="54dp"
android:layout_marginLeft="4dp"
android:layout_marginTop="20dp"
android:layout_marginRight="4dp"
android:background="#fff" />
<com.tophold.trade.view.seekbar.DoubleThumbSeekBar
android:id="@+id/am_dtsv_seek4"
android:layout_width="match_parent"
android:layout_height="54dp"
android:layout_marginLeft="4dp"
android:layout_marginTop="20dp"
android:layout_marginRight="4dp"
android:background="#fff" />
<com.tophold.trade.view.seekbar.DoubleThumbSeekBar
android:id="@+id/am_dtsv_seek5"
android:layout_width="match_parent"
android:layout_height="54dp"
android:layout_marginLeft="4dp"
android:layout_marginTop="20dp"
android:layout_marginRight="4dp"
android:background="#fff" />
</LinearLayout>
</ScrollView>
@@ -0,0 +1,205 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:id="@+id/af_tl_tablayout"
android:layout_width="match_parent"
android:layout_height="40dp"
app:tabIndicatorColor="@color/color_tb_indicator"
app:tabSelectedTextColor="@color/color_tb_selectedTxt"
app:tabTextColor="@color/color_tb_Txt"
/>
<!--按下的指标显示-->
<LinearLayout
android:id="@+id/akv_ll_container"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="#e7e3e3"
android:orientation="vertical"
android:paddingTop="4dp"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="20dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="高:"
android:textColor="#000"
android:textSize="12dp"/>
<TextView
android:id="@+id/akv_tv_h"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/string_placeHold"
android:textColor="#f00"
android:textSize="12dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="开:"
android:textColor="#000"
android:textSize="12dp"/>
<TextView
android:id="@+id/akv_tv_o"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/string_placeHold"
android:textColor="#f00"
android:textSize="12dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<TextView
android:id="@+id/akv_tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/string_placeHold"
android:textColor="#000"
android:textSize="12dp"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="20dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="低:"
android:textColor="#000"
android:textSize="12dp"/>
<TextView
android:id="@+id/akv_tv_l"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/string_placeHold"
android:textColor="#f00"
android:textSize="12dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="收:"
android:textColor="#000"
android:textSize="12dp"/>
<TextView
android:id="@+id/akv_tv_c"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/string_placeHold"
android:textColor="#f00"
android:textSize="12dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="幅:"
android:textColor="#000"
android:textSize="12dp"/>
<TextView
android:id="@+id/akv_tv_p"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/string_placeHold"
android:textColor="#f00"
android:textSize="12dp"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="#8f8d8d"/>
<com.tophold.example.utils.NoScrollViewPager
android:id="@+id/af_vp_viewpager"
android:layout_width="match_parent"
android:layout_height="300dp"/>
<TextView
android:id="@+id/af_tv_symbol"
android:layout_width="match_parent"
android:layout_height="40dp"
android:text="欧元/美元(USDCAD)"
android:gravity="center"
android:background="#a7a4a4"/>
<TextView
android:id="@+id/af_tv_blank"
android:onClick="showHorizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="4dp"
android:background="#cccccc"
android:gravity="center"
android:text="业务模块(单击这里进入横屏)"/>
</LinearLayout>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/afl_rl_listview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:background="#000"
>
<com.tophold.trade.view.fund.FundView
android:id="@+id/af_fv_fundview"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="#fff"/>
</LinearLayout>
@@ -0,0 +1,205 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:id="@+id/af_tl_tablayout"
android:layout_width="match_parent"
android:layout_height="40dp"
app:tabIndicatorColor="@color/color_tb_indicator"
app:tabSelectedTextColor="@color/color_tb_selectedTxt"
app:tabTextColor="@color/color_tb_Txt"
/>
<!--按下的指标显示-->
<LinearLayout
android:id="@+id/akv_ll_container"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="#e7e3e3"
android:orientation="vertical"
android:paddingTop="4dp"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="20dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="高:"
android:textColor="#000"
android:textSize="12dp"/>
<TextView
android:id="@+id/akv_tv_h"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/string_placeHold"
android:textColor="#f00"
android:textSize="12dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="开:"
android:textColor="#000"
android:textSize="12dp"/>
<TextView
android:id="@+id/akv_tv_o"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/string_placeHold"
android:textColor="#f00"
android:textSize="12dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<TextView
android:id="@+id/akv_tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/string_placeHold"
android:textColor="#000"
android:textSize="12dp"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="20dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="低:"
android:textColor="#000"
android:textSize="12dp"/>
<TextView
android:id="@+id/akv_tv_l"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/string_placeHold"
android:textColor="#f00"
android:textSize="12dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="收:"
android:textColor="#000"
android:textSize="12dp"/>
<TextView
android:id="@+id/akv_tv_c"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/string_placeHold"
android:textColor="#f00"
android:textSize="12dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="幅:"
android:textColor="#000"
android:textSize="12dp"/>
<TextView
android:id="@+id/akv_tv_p"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/string_placeHold"
android:textColor="#f00"
android:textSize="12dp"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="#8f8d8d"/>
<com.tophold.example.utils.NoScrollViewPager
android:id="@+id/af_vp_viewpager"
android:layout_width="match_parent"
android:layout_height="300dp"/>
<TextView
android:id="@+id/af_tv_symbol"
android:layout_width="match_parent"
android:layout_height="40dp"
android:text="欧元/美元(USDCAD)"
android:gravity="center"
android:background="#a7a4a4"/>
<TextView
android:id="@+id/af_tv_blank"
android:onClick="showHorizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="4dp"
android:background="#cccccc"
android:gravity="center"
android:text="业务模块(单击这里进入横屏)"/>
</LinearLayout>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#d9cfcf">
<android.support.v7.widget.RecyclerView
android:id="@+id/afl_rl_listview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:onClick="kViewVertical"
android:text="K线图竖屏(本地数据)" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:onClick="kViewHorizontal"
android:text="K线图横屏(本地数据)" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:onClick="kViewEvaluation"
android:text="外汇Demo(可能因为API限制显示不了)"
android:textAllCaps="false" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:onClick="btnDemo"
android:text="数字货币demo(火币API)"
android:textAllCaps="false" />
</LinearLayout>
@@ -0,0 +1,172 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!--按下的指标显示-->
<LinearLayout
android:id="@+id/akv_ll_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#e7e3e3"
android:orientation="vertical"
android:paddingTop="4dp"
android:visibility="invisible">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="20dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="高:"
android:textColor="#000"
android:textSize="12dp"/>
<TextView
android:id="@+id/akv_tv_h"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/string_placeHold"
android:textColor="#f00"
android:textSize="12dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="开:"
android:textColor="#000"
android:textSize="12dp"/>
<TextView
android:id="@+id/akv_tv_o"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/string_placeHold"
android:textColor="#f00"
android:textSize="12dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<TextView
android:id="@+id/akv_tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/string_placeHold"
android:textColor="#000"
android:textSize="12dp"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="20dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="低:"
android:textColor="#000"
android:textSize="12dp"/>
<TextView
android:id="@+id/akv_tv_l"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/string_placeHold"
android:textColor="#f00"
android:textSize="12dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="收:"
android:textColor="#000"
android:textSize="12dp"/>
<TextView
android:id="@+id/akv_tv_c"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/string_placeHold"
android:textColor="#f00"
android:textSize="12dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="幅:"
android:textColor="#000"
android:textSize="12dp"/>
<TextView
android:id="@+id/akv_tv_p"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/string_placeHold"
android:textColor="#f00"
android:textSize="12dp"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
<com.tophold.trade.view.kview.KView
android:id="@+id/akv_kv_kview"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<Button
android:onClick="showCandle"
android:layout_width="match_parent"
android:layout_height="30dp"
android:text="点击展示蜡烛图"/>
</LinearLayout>
@@ -0,0 +1,192 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!--按下的指标显示-->
<LinearLayout
android:id="@+id/akv_ll_container"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="20dp"
android:background="#e7e3e3"
android:orientation="vertical"
android:paddingTop="4dp"
android:visibility="invisible">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="25dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="高:"
android:textColor="#000"
android:textSize="12dp"/>
<TextView
android:id="@+id/akv_tv_h"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/string_placeHold"
android:textColor="#f00"
android:textSize="12dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="开:"
android:textColor="#000"
android:textSize="12dp"/>
<TextView
android:id="@+id/akv_tv_o"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/string_placeHold"
android:textColor="#f00"
android:textSize="12dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<TextView
android:id="@+id/akv_tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/string_placeHold"
android:textColor="#000"
android:textSize="12dp"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="25dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="低:"
android:textColor="#000"
android:textSize="12dp"/>
<TextView
android:id="@+id/akv_tv_l"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/string_placeHold"
android:textColor="#f00"
android:textSize="12dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="收:"
android:textColor="#000"
android:textSize="12dp"/>
<TextView
android:id="@+id/akv_tv_c"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/string_placeHold"
android:textColor="#f00"
android:textSize="12dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="幅:"
android:textColor="#000"
android:textSize="12dp"/>
<TextView
android:id="@+id/akv_tv_p"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/string_placeHold"
android:textColor="#f00"
android:textSize="12dp"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
<com.tophold.trade.view.kview.KView
android:id="@+id/akv_kv_kview"
android:layout_width="match_parent"
android:layout_height="300dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_marginTop="100dp"
android:orientation="horizontal">
<Button
android:id="@+id/akv_btn_showCandle"
android:layout_width="0dp"
android:layout_height="45dp"
android:layout_weight="1"
android:onClick="showCandle"
android:text="点击展示蜡烛图"/>
<View
android:layout_width="10dp"
android:layout_height="match_parent"/>
<Button
android:id="@+id/akv_btn_showMinnor"
android:layout_width="0dp"
android:layout_height="45dp"
android:layout_weight="1"
android:onClick="showMinor"
android:text="点击不显示副图"/>
</LinearLayout>
</LinearLayout>
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/version"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:gravity="center"
android:padding="16dp"
android:text="verisonName:1.4,versionCode(git head):"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="100dp"
android:onClick="fundView"
android:text="去看自定义基金View Demo"
android:textAllCaps="false"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:textAllCaps="false"
android:onClick="kViewDemo"
android:text="去看KView Demo"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:onClick="onPieTest"
android:text="去看PieChart饼图 Demo"
android:textAllCaps="false"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:onClick="onSeekBarTest"
android:text="去看DoubleThumbSeekBar Demo"
android:textAllCaps="false" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:onClick="onRx"
android:text="去看Rx"
android:textAllCaps="false" />
</LinearLayout>
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#dedbdb"
android:gravity="center">
<com.tophold.trade.view.pie.PieChartView
android:id="@+id/hatab_tapc_chart"
android:layout_width="match_parent"
android:layout_height="215dp"
android:background="#fff"/>
</LinearLayout>
@@ -0,0 +1,9 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.tophold.trade.view.kview.KView
android:id="@+id/fk_kv_kview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
@@ -0,0 +1,9 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.tophold.trade.view.kview.KView
android:id="@+id/fk_kv_kview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="45dp"
android:background="#d9cfcf">
<TextView
android:id="@+id/iafl_tv_znName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="4dp"
android:gravity="center_vertical"
android:text="黄金"/>
<TextView
android:id="@+id/iafl_tv_enName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/iafl_tv_znName"
android:layout_marginLeft="10dp"
android:gravity="center_vertical"
android:text="GOLD"/>
<TextView
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_alignParentBottom="true"
android:background="#ffffff"/>
</RelativeLayout>
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="45dp"
android:background="#fff">
<TextView
android:id="@+id/iafl_tv_znName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="4dp"
android:gravity="center_vertical"
android:textSize="18sp"
android:text="黄金"/>
<TextView
android:id="@+id/iafl_tv_enName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/iafl_tv_znName"
android:layout_marginLeft="10dp"
android:textSize="10sp"
android:gravity="center_vertical"
android:text="GOLD"/>
<TextView
android:id="@+id/iafl_tv_part"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerInParent="true"
android:layout_marginRight="10dp"
android:text="主区"/>
<TextView
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_alignParentBottom="true"
android:background="#8b8989"/>
</RelativeLayout>
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
<!--具体业务-->
<color name="color_tb_indicator">#ef1e13</color>
<color name="color_tb_selectedTxt">#ef1e13</color>
<color name="color_tb_Txt">#8a8a8b</color>
<!--交易分析四个颜色-->
<color name="trade_analyze_pie_1">#f5b5ff</color>
<color name="trade_analyze_pie_2">#aaacff</color>
<color name="trade_analyze_pie_3">#96e0ff</color>
<color name="trade_analyze_pie_4">#ffcf97</color>
<!--seekbar-->
<color name="dtsb_bgColor">#cecfce</color>
<color name="dtsb_thumb1Color">#ed5f53</color>
<color name="dtsb_thumb11Color">#f59e97</color>
<color name="dtsb_thumb2Color">#63a65e</color>
<color name="dtsb_thumb22Color">#9cfa95</color>
</resources>
@@ -0,0 +1,4 @@
<resources>
<string name="app_name">FinancialCustomerView</string>
<string name="string_placeHold">--</string>
</resources>
@@ -0,0 +1,16 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme_Fullscreen" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="android:windowNoTitle">true</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowIsTranslucent">true</item>
</style>
</resources>
@@ -0,0 +1,17 @@
package com.tophold.example;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}