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
+63
View File
@@ -0,0 +1,63 @@
#---------以下为通用配置BEGIN------
# 操作系统忽略文件
# OSX
*.DS_Store
# Windows thumbnail db
Thumbs.db
## AndroidStudio IDE忽略文件
# Intellij
*.iml
.idea/
# Android Studio captures folder
captures/
# Android Studio Navigation editor temp files
.navigation/
## gradle忽略文件
# Gradle files
.gradle/
gradle/
gradlew
gradlew.bat
## sdk配置文件
# Local configuration file (sdk path, etc)
local.properties
## 编译生成的文件
# Generated files
bin/
gen/
out/
build/
*.dex
## *.apk
*.ap_
*.class
## 日志文件
# Log Files
*.log
#---------以上为通用配置END--------
#--------- 根据需求个性化配置---------
# Keystore files
# *.jks
## 第三方
# bugly
*.txt
# note
# .ignore不起作用解决方案
# git rm -r --cached .
# git add .
# git commit -m "update .ignore"
+50
View File
@@ -0,0 +1,50 @@
language: android
jdk: oraclejdk8
env:
global:
- ANDROID_API=28
- EMULATOR_API=26
- ANDROID_BUILD_TOOLS=27.0.3
- ADB_INSTALL_TIMEOUT=5 # minutes
android:
components:
- tools
- platform-tools
- build-tools-$ANDROID_BUILD_TOOLS
- android-$ANDROID_API
- android-$EMULATOR_API
- extra-google-m2repository
- extra-android-m2repository
- sys-img-armeabi-v7a-addon-google_apis-google-$ANDROID_API_LEVEL
- sys-img-armeabi-v7a-addon-google_apis-google-$EMULATOR_API_LEVEL
licenses:
- android-sdk-preview-license-.+
- android-sdk-license-.+
- google-gdk-license-.+
before_install:
- yes | sdkmanager "platforms;android-27"
# Emulator Management: Create, Start and Wait
#before_script:
# - echo no | android create avd --force -n test -t android-$$ANDROID_API --abi armeabi-v7a
# - emulator -avd test -no-skin -no-audio -no-window &
# - android-wait-for-emulator
# - adb shell input keyevent 82 &
script:
- "./gradlew clean build check -PdisablePreDex --stacktrace"
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
cache:
directories:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
- $HOME/.android/build-cache
+66
View File
@@ -0,0 +1,66 @@
> ### Notice
> *Before you continue, this is the* **ANDROID** *library. If you have an* **iOS** *device, please go here instead*:
>
> https://github.com/danielgindi/Charts
>
> They might tell you to come back here, if they do, listen to them and ignore this notice.
# How to contribute
Bug-fixes and features often come from users of the MPAndroidChart library and improve it greatly. We want to keep it as easy as possible to contribute changes that improve the experience for users all around the world. There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things.
## Creating Issues
There are two main issue templates, one for bugs and another for feature requests. Please use them! You're issue will be much easier to understand, and bugs easier to fix, if you follow the templates. If your issue doesn't fit into those, just use the generic template.
Search existing [issues] to see if your bug has already been reported or if a feature request already exists. Don't forget to remove `is:open` so you see all the issues! If you find that one already exists, use reactions to show how much you care!
## Making Pull Requests
Careful! If you fail to follow these guidlines, you're pull request may be closed, *even if it's really awesome*.
0. **Search** open [pull requests] AND existing [issues] to make sure what you want to do isn't already being worked on or already has an open pull request.
1. **Fork** the repository
1. **Create** a new branch based on `master`, and name it according to your changes
1. **Add** your commits, they MUST follow the [Commit Style](#commit-style) below
1. **Test** your changes by actually running the example app, or create a new example
1. **Create** a pull request, following the auto-generated template
1. ???
1. Profit :money_with_wings:
You are encouraged to use [GitHub Desktop] to inspect your code changes before committing them. It can reveal small changes that might have gone unnoticed, and would be requested for removal before merging.
Check out [#3975](https://github.com/PhilJay/MPAndroidChart/pull/3975) for an example of a good-made-better pull request.
## Commit Style
* **Make commits of logical units**
Don't load your commits with tons of changes, this makes it hard to follow what is happening. However, if you have done a lot of work, and there are commits and merges all over the place, squash them down into fewer commits.
* **Conform to the code style**
It's easy, just look around!
* **Write good commit messages**
You may prefer [Tim Pope's style], you might like the [commitizen-friendly] way. Regardless of the color you pick, you MUST stay within the lines!
```
The commit title CANNOT exceed 50 characters
The body of the message comes after an empty new line, and describes the
changes more thoroughly. If the change is obvious and self-explanatory
from the title, you can omit the body. You should describe all changes
if many were made, or maybe some trickery that only code wizards can
understand.
Be polite and wrap your lines to 72 characters, but if you prefer going
to 100 characters then I guess we can't stop you.
```
## Final Notes
Thanks for reading the contributing file! Have some cake! :cake:
[issues]: https://github.com/PhilJay/MPAndroidChart/issues
[pull requests]: https://github.com/PhilJay/MPAndroidChart/pulls
[GitHub Desktop]: https://desktop.github.com/
[Tim Pope's style]: https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
[commitizen-friendly]: https://github.com/commitizen/cz-cli
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+59
View File
@@ -0,0 +1,59 @@
## FinancialCustomerView
* 本项目会对金融交易软件中存在的各种View进行模仿绘制
* 提供详细的实现思路
* 收集整理相关算法、文档以及专业资料
* 开源库不依赖第三方,继承系统View实现
* 提供完整Demo
* [参考文档](https://github.com/Tophold/FinancialCustomerView/tree/master/%E8%B5%84%E6%96%99%E4%B8%8E%E6%96%87%E6%A1%A3)
## 号外
1. [MPAndroidChartWrapper](https://github.com/scsfwgy/MPAndroidChartWrapper)【开发完善中...】高度封装MPAndroidChart,提供开箱即用的MPAndroidChart。
两大原则:1.不修改MPAndroidChart源码,只继承封装原有功能实现扩展。2. 开箱即用。目标:可商用。
2. 商用k线图**推荐**使用[MPAndroidChartWrapper](https://github.com/scsfwgy/MPAndroidChartWrapper),让您几行代码实现一个商用级别k线图。
#### 目录
1. ``com.tophold.trade.view.fund.FundView`` 仿蚂蚁金服基金走势图
2. ``com.tophold.trade.view.kview.KView`` k线走势图
3. ``com.tophold.trade.view.pie.PieChartView`` 饼图
4. ``com.tophold.trade.view.seekbar.DoubleThumbSeekBar`` 双Thumb自定义进度条
#### com.tophold.trade.view.fund.FundView
* 仿蚂蚁金服基金走势图,支持长按查看指定数据,可以据此扩展出各种简单的资金走势图。KView的思路来自于此。
![](https://github.com/scsfwgy/FinancialCustomerView/raw/master/img/v1.1_img_nopress.png)
![](https://github.com/scsfwgy/FinancialCustomerView/raw/master/img/v1.1_img_press.png)
#### com.tophold.trade.view.kview.KView
* k线走势图,支持主图+副图+量图
* 主图支持缩放、长按十字、滑动等各种手势操作;支持显示最小、最大值;指标支持:ma、boll、ma+boll;图形类别支持分时图、蜡烛图
* 副图支持指标:macd、kdj、rsi
* 量图支持:量、ma(5,10)
*
![](https://raw.githubusercontent.com/Tophold/FinancialCustomerView/master/img/vol_huobi_eos.png)
![](https://raw.githubusercontent.com/Tophold/FinancialCustomerView/master/img/vol_huobi_eos_press.png)
#### com.tophold.trade.view.pie.PieChartView
* 饼图,加载动画、特定指示图
![](https://camo.githubusercontent.com/d345e80888d09007764b575932a7ede0ba368953/687474703a2f2f6f373175686f6b67662e626b742e636c6f7564646e2e636f6d2f31353331383135393530313831372e6a7067)
#### com.tophold.trade.view.seekbar.DoubleThumbSeekBar
* 双Thumb自定义进度条,可以高度定制化
* 支持单、双thumb
* 支持从左、右开始最小值(和滑动方向无关)
* 两个thumb可以任意设置开始方向以及起始位置
* 支持数据回调
* 具体使用参看Demo
![](https://raw.githubusercontent.com/Tophold/FinancialCustomerView/master/img/DoubleThumbSeekBar.png)
#### 其它
* [天厚投资](https://www.tophold.com/)
* [MPAndroidChart](https://github.com/PhilJay/MPAndroidChart)
+39
View File
@@ -0,0 +1,39 @@
# FinancialCustomerView
## Introduce
> This project will simulate the various views in financial transaction software, provide detailed implementation ideas, and collect relevant algorithms, documents and professional materials.
## Significance
> May our developers to say, have very mature third party framework, why return to oneself to realize?
* Knowing how to write and draw, it is more convenient to extend the third library graphics library.
* Understand the principle of implementation, if the third parties can not achieve, you can write one in a leisurely way.
* There are many kinds of algorithms and various indicators in financial transactions, but there are few related information on the Internet. I hope can give a little help to financial related developers.
* Knowing it, knowing it is so.
## Demo
* impression drawing[impression drawing](https://github.com/scsfwgy/FinancialCustomerView/blob/master/%E8%B5%84%E6%96%99%E4%B8%8E%E6%96%87%E6%A1%A3/%E6%95%88%E6%9E%9C%E5%9B%BE%E5%92%8C%E8%BF%9B%E5%BA%A6.md)
* demo apk:[demo_apk](https://github.com/scsfwgy/FinancialCustomerView/tree/master/apk)
## Reference material and blogs
* reference material and blogs[reference material and blogs](https://github.com/scsfwgy/FinancialCustomerView/tree/master/%E8%B5%84%E6%96%99%E4%B8%8E%E6%96%87%E6%A1%A3)
## Schedule
* CompletedThe FundView has developed in the master branch.
* CompletedThe TimeSharingView has developed in the master branch.
* CompletedThe CandleViewincluding MA、BOLLhas developed in the master branch.
* CompletedThe code refactoring has completed in the master branch. The code is refactoring in the later period.
* being developedThe MinorView is being developed in the feature_minor branch.
* planVerify the accuracy of the data (proofread data with On-line app).
* planTest availability using open API in a real environment.
* planSimulating real useswitching timesharing、5m、15m、30m、1h、1d、1w、1m。
## Contribution
* new branchPR。
## Famous third party graphic library
* MPAndroidChart[https://github.com/PhilJay/MPAndroidChart](https://github.com/PhilJay/MPAndroidChart)
* AndroidCharts[https://github.com/HackPlan/AndroidCharts](https://github.com/HackPlan/AndroidCharts)
* Android-Charts[https://github.com/limccn/Android-Charts](https://github.com/limccn/Android-Charts)
+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);
/**
* 定制,所有的画笔以及其它属性都已经暴露出来,有了更加大的定时灵活性。更多参数可以直接查看源码...
*/
//常规set、get...
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

Some files were not shown because too many files have changed in this diff Show More