├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── kong │ │ └── qingwei │ │ └── combinedchartdemo │ │ ├── activity │ │ ├── MainActivity.java │ │ └── MyBarChartRenderer.java │ │ ├── adapter │ │ └── MyPopupWindowAdapter.java │ │ ├── bean │ │ └── CombinedChartEntity.java │ │ ├── listener │ │ ├── CoupleChartGestureListener.java │ │ ├── OnPopupWindowItemClickListener.java │ │ └── OnValueSelectedListener.java │ │ ├── net │ │ └── CombinedChartConnector.java │ │ ├── utils │ │ ├── MyChartHighlighter.java │ │ ├── MyCustomXAxisValueFormatter.java │ │ ├── TimeUtil.java │ │ └── ToastUtil.java │ │ └── view │ │ ├── MyBarChart.java │ │ ├── MyCombinedChart.java │ │ ├── MyPopupWindow.java │ │ └── MyTabLayout.java │ └── res │ ├── layout │ ├── activity_main.xml │ ├── item_button_tab.xml │ ├── item_popup_window.xml │ ├── item_text_tab.xml │ └── popupwindow.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | CombinedChartDemo -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 23 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.8 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #模拟自选股(持续更新) 2 | 3 | ## **博客:**[CSDN](http://blog.csdn.net/q4878802/article/details/51397447) ## 4 | 5 | ![这里写图片描述](http://img.blog.csdn.net/20160517180651091) 6 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.3" 6 | 7 | defaultConfig { 8 | applicationId "kong.qingwei.combinedchartdemo" 9 | minSdkVersion 15 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | 21 | repositories { 22 | maven { url "https://jitpack.io" } 23 | } 24 | } 25 | 26 | dependencies { 27 | compile fileTree(include: ['*.jar'], dir: 'libs') 28 | testCompile 'junit:junit:4.12' 29 | compile 'com.android.support:appcompat-v7:23.3.0' 30 | compile 'com.google.code.gson:gson:2.6.2' 31 | compile 'org.greenrobot:eventbus:3.0.0' 32 | compile 'com.squareup.okhttp:okhttp:2.6.0' 33 | compile 'com.github.PhilJay:MPAndroidChart:v2.2.4' 34 | compile 'com.android.support:design:23.3.0' 35 | } 36 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\Android\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/kong/qingwei/combinedchartdemo/activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | package kong.qingwei.combinedchartdemo.activity; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | import android.widget.LinearLayout; 7 | import android.widget.TextView; 8 | 9 | import com.github.mikephil.charting.charts.Chart; 10 | 11 | import org.greenrobot.eventbus.EventBus; 12 | import org.greenrobot.eventbus.Subscribe; 13 | import org.greenrobot.eventbus.ThreadMode; 14 | 15 | import kong.qingwei.combinedchartdemo.R; 16 | import kong.qingwei.combinedchartdemo.bean.CombinedChartEntity; 17 | import kong.qingwei.combinedchartdemo.listener.CoupleChartGestureListener; 18 | import kong.qingwei.combinedchartdemo.listener.OnValueSelectedListener; 19 | import kong.qingwei.combinedchartdemo.net.CombinedChartConnector; 20 | import kong.qingwei.combinedchartdemo.view.MyBarChart; 21 | import kong.qingwei.combinedchartdemo.view.MyCombinedChart; 22 | 23 | public class MainActivity extends AppCompatActivity implements OnValueSelectedListener { 24 | 25 | private MyCombinedChart mCombinedChart; 26 | private LinearLayout mDetails; 27 | private TextView mOpen; 28 | private TextView mClose; 29 | private TextView mHigh; 30 | private TextView mLow; 31 | private MyBarChart mBarChart; 32 | 33 | @Override 34 | protected void onCreate(Bundle savedInstanceState) { 35 | super.onCreate(savedInstanceState); 36 | setContentView(R.layout.activity_main); 37 | // 注册EventBus 38 | EventBus.getDefault().register(this); 39 | // 初始化控件 40 | mCombinedChart = (MyCombinedChart) findViewById(R.id.combinedChart); 41 | mBarChart = (MyBarChart) findViewById(R.id.barChart); 42 | 43 | // 将K线控的滑动事件传递给交易量控件 44 | mCombinedChart.setOnChartGestureListener(new CoupleChartGestureListener(mCombinedChart, new Chart[]{mBarChart})); 45 | // 将交易量控件的滑动事件传递给K线控件 46 | mBarChart.setOnChartGestureListener(new CoupleChartGestureListener(mBarChart, new Chart[]{mCombinedChart})); 47 | 48 | 49 | mCombinedChart.setOnValueSelectedListener(this); 50 | // 获取数据 51 | CombinedChartConnector.getInstance().connect(); 52 | 53 | 54 | mDetails = (LinearLayout) findViewById(R.id.details); 55 | mOpen = (TextView) findViewById(R.id.open); 56 | mClose = (TextView) findViewById(R.id.close); 57 | mHigh = (TextView) findViewById(R.id.high); 58 | mLow = (TextView) findViewById(R.id.low); 59 | 60 | } 61 | 62 | @Override 63 | protected void onDestroy() { 64 | // 反注册EventBus 65 | EventBus.getDefault().unregister(this); 66 | super.onDestroy(); 67 | } 68 | 69 | /** 70 | * 获取K线数据 71 | * 72 | * @param empty K线数据 73 | */ 74 | @Subscribe(threadMode = ThreadMode.MAIN) 75 | public void onEvent(CombinedChartEntity empty) { 76 | // 填充数据 77 | mCombinedChart.setData(empty); 78 | mBarChart.setData(empty); 79 | } 80 | 81 | @Override 82 | public void start() { 83 | mDetails.setVisibility(View.VISIBLE); 84 | } 85 | 86 | @Override 87 | public void data(long open, long close, long high, long low) { 88 | // Log.i("data", "open = " + open + " close = " + close + " high = " + high + " low = " + low); 89 | mOpen.setText(String.valueOf(open)); 90 | mClose.setText(String.valueOf(close)); 91 | mHigh.setText(String.valueOf(high)); 92 | mLow.setText(String.valueOf(low)); 93 | } 94 | 95 | @Override 96 | public void end() { 97 | mDetails.setVisibility(View.GONE); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /app/src/main/java/kong/qingwei/combinedchartdemo/activity/MyBarChartRenderer.java: -------------------------------------------------------------------------------- 1 | package kong.qingwei.combinedchartdemo.activity; 2 | 3 | import android.graphics.Canvas; 4 | 5 | import com.github.mikephil.charting.animation.ChartAnimator; 6 | import com.github.mikephil.charting.highlight.Highlight; 7 | import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; 8 | import com.github.mikephil.charting.renderer.BarChartRenderer; 9 | import com.github.mikephil.charting.utils.ViewPortHandler; 10 | 11 | /** 12 | * Created by kongqw on 2016/5/25. 13 | */ 14 | public class MyBarChartRenderer extends BarChartRenderer { 15 | public MyBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) { 16 | super(chart, animator, viewPortHandler); 17 | } 18 | 19 | @Override 20 | public void drawHighlighted(Canvas c, Highlight[] indices) { 21 | // super.drawHighlighted(c, indices); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/kong/qingwei/combinedchartdemo/adapter/MyPopupWindowAdapter.java: -------------------------------------------------------------------------------- 1 | package kong.qingwei.combinedchartdemo.adapter; 2 | 3 | 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.TextView; 9 | 10 | import kong.qingwei.combinedchartdemo.R; 11 | import kong.qingwei.combinedchartdemo.listener.OnPopupWindowItemClickListener; 12 | 13 | /** 14 | * Created by kqw on 2016/5/17. 15 | * PopupWindow的数据适配器 16 | */ 17 | public class MyPopupWindowAdapter extends RecyclerView.Adapter { 18 | 19 | private OnPopupWindowItemClickListener mOnPopupWindowItemClickListener; 20 | private String[] mItems = {"5分钟", "15分钟", "30分钟", "60分钟"}; 21 | 22 | public static class ViewHolder extends RecyclerView.ViewHolder { 23 | public TextView mTextView; 24 | public View view; 25 | 26 | public ViewHolder(View v) { 27 | super(v); 28 | view = v; 29 | mTextView = (TextView) v.findViewById(R.id.text_item); 30 | } 31 | } 32 | 33 | public MyPopupWindowAdapter() { 34 | } 35 | 36 | @Override 37 | public MyPopupWindowAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 38 | return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_popup_window, parent, false)); 39 | } 40 | 41 | @Override 42 | public void onBindViewHolder(ViewHolder holder, int position) { 43 | final int finalPosition = position; 44 | holder.mTextView.setText(mItems[position]); 45 | holder.view.setOnClickListener(new View.OnClickListener() { 46 | @Override 47 | public void onClick(View v) { 48 | if (null != mOnPopupWindowItemClickListener) 49 | mOnPopupWindowItemClickListener.click(finalPosition); 50 | } 51 | }); 52 | } 53 | 54 | @Override 55 | public int getItemCount() { 56 | return null == mItems ? 0 : mItems.length; 57 | } 58 | 59 | /** 60 | * 添加条目点击的监听 61 | * 62 | * @param listener 回调接口 63 | */ 64 | public void setOnPopupWindowItemClickListener(OnPopupWindowItemClickListener listener) { 65 | mOnPopupWindowItemClickListener = listener; 66 | } 67 | 68 | /** 69 | * 获取指定位置的Item内容 70 | * 71 | * @param position 位置 72 | * @return 内容 73 | */ 74 | public String getContent(int position) { 75 | return mItems[position]; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/java/kong/qingwei/combinedchartdemo/bean/CombinedChartEntity.java: -------------------------------------------------------------------------------- 1 | package kong.qingwei.combinedchartdemo.bean; 2 | import java.util.List; 3 | 4 | /** 5 | * Created by kqw on 2016/5/13. 6 | * CombinedChartEntity 7 | */ 8 | public class CombinedChartEntity { 9 | private Object err_msg; 10 | private int err_no; 11 | private List> data; 12 | 13 | public void setErr_msg(Object err_msg) { 14 | this.err_msg = err_msg; 15 | } 16 | 17 | public void setErr_no(int err_no) { 18 | this.err_no = err_no; 19 | } 20 | 21 | public void setData(List> data) { 22 | this.data = data; 23 | } 24 | 25 | public Object getErr_msg() { 26 | return err_msg; 27 | } 28 | 29 | public int getErr_no() { 30 | return err_no; 31 | } 32 | 33 | public List> getData() { 34 | return data; 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return "LKLineDataGson{" + 40 | "err_msg=" + err_msg + 41 | ", err_no=" + err_no + 42 | ", data=" + data + 43 | '}'; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/kong/qingwei/combinedchartdemo/listener/CoupleChartGestureListener.java: -------------------------------------------------------------------------------- 1 | package kong.qingwei.combinedchartdemo.listener; 2 | 3 | import android.graphics.Matrix; 4 | import android.util.Log; 5 | import android.view.MotionEvent; 6 | import android.view.View; 7 | 8 | import com.github.mikephil.charting.charts.BarLineChartBase; 9 | import com.github.mikephil.charting.charts.Chart; 10 | import com.github.mikephil.charting.highlight.Highlight; 11 | import com.github.mikephil.charting.listener.ChartTouchListener; 12 | import com.github.mikephil.charting.listener.OnChartGestureListener; 13 | 14 | /** 15 | * Created by kqw on 2016/5/25. 16 | * CoupleChartGestureListener 17 | */ 18 | public class CoupleChartGestureListener implements OnChartGestureListener { 19 | 20 | private Chart srcChart; 21 | private Chart[] dstCharts; 22 | 23 | 24 | public CoupleChartGestureListener(Chart srcChart, Chart[] dstCharts) { 25 | this.srcChart = srcChart; 26 | this.dstCharts = dstCharts; 27 | } 28 | 29 | @Override 30 | public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { 31 | 32 | } 33 | 34 | @Override 35 | public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { 36 | 37 | } 38 | 39 | @Override 40 | public void onChartLongPressed(MotionEvent me) { 41 | 42 | } 43 | 44 | @Override 45 | public void onChartDoubleTapped(MotionEvent me) { 46 | 47 | } 48 | 49 | @Override 50 | public void onChartSingleTapped(MotionEvent me) { 51 | for (Chart dstChart : dstCharts) { 52 | if (dstChart.getVisibility() == View.VISIBLE) { 53 | Highlight h = ((BarLineChartBase)dstChart).getHighlightByTouchPoint(me.getX(), me.getY()); 54 | ((BarLineChartBase)dstChart).highlightTouch(h); 55 | } 56 | } 57 | } 58 | 59 | @Override 60 | public void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) { 61 | 62 | } 63 | 64 | @Override 65 | public void onChartScale(MotionEvent me, float scaleX, float scaleY) { 66 | syncCharts(); 67 | 68 | } 69 | 70 | @Override 71 | public void onChartTranslate(MotionEvent me, float dX, float dY) { 72 | syncCharts(); 73 | 74 | } 75 | 76 | public void syncCharts() { 77 | Matrix srcMatrix; 78 | float[] srcVals = new float[9]; 79 | Matrix dstMatrix; 80 | float[] dstVals = new float[9]; 81 | 82 | // get src chart translation matrix: 83 | srcMatrix = srcChart.getViewPortHandler().getMatrixTouch(); 84 | srcMatrix.getValues(srcVals); 85 | 86 | // apply X axis scaling and position to dst charts: 87 | for (Chart dstChart : dstCharts) { 88 | if (dstChart.getVisibility() == View.VISIBLE) { 89 | dstMatrix = dstChart.getViewPortHandler().getMatrixTouch(); 90 | dstMatrix.getValues(dstVals); 91 | dstVals[Matrix.MSCALE_X] = srcVals[Matrix.MSCALE_X]; 92 | dstVals[Matrix.MTRANS_X] = srcVals[Matrix.MTRANS_X]; 93 | dstMatrix.setValues(dstVals); 94 | dstChart.getViewPortHandler().refresh(dstMatrix, dstChart, true); 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /app/src/main/java/kong/qingwei/combinedchartdemo/listener/OnPopupWindowItemClickListener.java: -------------------------------------------------------------------------------- 1 | package kong.qingwei.combinedchartdemo.listener; 2 | 3 | /** 4 | * Created by kqw on 2016/5/17. 5 | * OnPopupWindowItemClickListener 6 | */ 7 | public interface OnPopupWindowItemClickListener { 8 | public void click(int position); 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/kong/qingwei/combinedchartdemo/listener/OnValueSelectedListener.java: -------------------------------------------------------------------------------- 1 | package kong.qingwei.combinedchartdemo.listener; 2 | 3 | /** 4 | * Created by kqw on 2016/5/16. 5 | * OnValueSelectedListener 6 | */ 7 | public interface OnValueSelectedListener { 8 | public void start(); 9 | 10 | public void data(long open, long close, long high, long low); 11 | 12 | public void end(); 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/kong/qingwei/combinedchartdemo/net/CombinedChartConnector.java: -------------------------------------------------------------------------------- 1 | package kong.qingwei.combinedchartdemo.net; 2 | 3 | import android.util.Log; 4 | 5 | import com.google.gson.Gson; 6 | import com.squareup.okhttp.Call; 7 | import com.squareup.okhttp.Callback; 8 | import com.squareup.okhttp.OkHttpClient; 9 | import com.squareup.okhttp.Request; 10 | import com.squareup.okhttp.Response; 11 | 12 | import org.greenrobot.eventbus.EventBus; 13 | 14 | import java.io.IOException; 15 | 16 | import kong.qingwei.combinedchartdemo.bean.CombinedChartEntity; 17 | 18 | /** 19 | * Created by kongqw on 2015/11/28. 20 | * 获取K线数据的类 21 | */ 22 | public class CombinedChartConnector { 23 | 24 | private static CombinedChartConnector mConnector; 25 | private static OkHttpClient mOkHttpClient; 26 | private static Call mCall; 27 | private static Gson mGson; 28 | 29 | private CombinedChartConnector() { 30 | 31 | } 32 | 33 | public static CombinedChartConnector getInstance() { 34 | if (null == mConnector || null == mGson || null == mOkHttpClient) { 35 | mConnector = new CombinedChartConnector(); 36 | mGson = new Gson(); 37 | //创建okHttpClient对象 38 | mOkHttpClient = new OkHttpClient(); 39 | } 40 | return mConnector; 41 | } 42 | 43 | /** 44 | * 发起请求 45 | */ 46 | public void connect() { 47 | //创建一个Request 48 | Request request = new Request.Builder() 49 | .url("https://price.api.open-nodes.org/v1/ticker/line?symbol=huobibtccny&interval=1d&offset=-600&limit=600") 50 | .build(); 51 | //new call 52 | mCall = mOkHttpClient.newCall(request); 53 | //请求加入调度 54 | mCall.enqueue(new Callback() { 55 | @Override 56 | public void onFailure(Request request, IOException e) { 57 | 58 | } 59 | 60 | @Override 61 | public void onResponse(Response response) throws IOException { 62 | String htmlStr = response.body().string(); 63 | Log.i("CombinedChartConnector","htmlStr = " + htmlStr); 64 | CombinedChartEntity empty = mGson.fromJson(htmlStr,CombinedChartEntity.class); 65 | EventBus.getDefault().post(empty); 66 | } 67 | }); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/kong/qingwei/combinedchartdemo/utils/MyChartHighlighter.java: -------------------------------------------------------------------------------- 1 | package kong.qingwei.combinedchartdemo.utils; 2 | 3 | import com.github.mikephil.charting.highlight.ChartHighlighter; 4 | import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; 5 | 6 | /** 7 | * Created by kqw on 2016/5/16. 8 | * 自定义MyChartHighlighter 9 | */ 10 | public class MyChartHighlighter extends ChartHighlighter { 11 | public MyChartHighlighter(BarLineScatterCandleBubbleDataProvider chart) { 12 | super(chart); 13 | } 14 | 15 | /** 16 | * 通过像素获取index 17 | * 18 | * @param x 像素 19 | * @return index 20 | */ 21 | @Override 22 | public int getXIndex(float x) { 23 | return super.getXIndex(x); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/kong/qingwei/combinedchartdemo/utils/MyCustomXAxisValueFormatter.java: -------------------------------------------------------------------------------- 1 | package kong.qingwei.combinedchartdemo.utils; 2 | 3 | import com.github.mikephil.charting.formatter.XAxisValueFormatter; 4 | import com.github.mikephil.charting.utils.ViewPortHandler; 5 | 6 | /** 7 | * Created by kqw on 2016/5/16. 8 | * 格式化横轴时间 9 | */ 10 | public class MyCustomXAxisValueFormatter implements XAxisValueFormatter { 11 | 12 | @Override 13 | public String getXValue(String original, int index, ViewPortHandler viewPortHandler) { 14 | long time = 0L; 15 | try { 16 | time = Long.parseLong(original); 17 | } catch (NumberFormatException e) { 18 | e.printStackTrace(); 19 | } 20 | return TimeUtil.dateFormat(time); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/kong/qingwei/combinedchartdemo/utils/TimeUtil.java: -------------------------------------------------------------------------------- 1 | package kong.qingwei.combinedchartdemo.utils; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Date; 5 | 6 | /** 7 | * Created by kqw on 2016/5/16. 8 | * TimeUtil 9 | */ 10 | public final class TimeUtil { 11 | 12 | /** 13 | * 格式化时间 14 | * 15 | * @param seconds 秒 16 | * @return yy/MM/dd 17 | */ 18 | public static String dateFormat(long seconds) { 19 | try { 20 | Date date = new Date(seconds * 1000); 21 | SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd"); 22 | return format.format(date); 23 | } catch (Exception e) { 24 | e.printStackTrace(); 25 | return ""; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/kong/qingwei/combinedchartdemo/utils/ToastUtil.java: -------------------------------------------------------------------------------- 1 | package kong.qingwei.combinedchartdemo.utils; 2 | 3 | import android.content.Context; 4 | import android.widget.Toast; 5 | 6 | /** 7 | * Created by kqw on 2016/5/17. 8 | * Toast工具 9 | */ 10 | public final class ToastUtil { 11 | 12 | private static Toast mToast; 13 | 14 | // 工具类私有化 15 | private ToastUtil() { 16 | } 17 | 18 | // 单例模式 显示Toast 19 | public static void show(Context context, String text) { 20 | if (null == mToast) { 21 | mToast = Toast.makeText(context, "", Toast.LENGTH_SHORT); 22 | } 23 | try { 24 | mToast.setText(text); 25 | mToast.show(); 26 | } catch (Exception e) { 27 | e.printStackTrace(); 28 | } 29 | 30 | } 31 | 32 | // 关闭Toast 33 | public static void cancel() { 34 | if (null != mToast) 35 | mToast.cancel(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/kong/qingwei/combinedchartdemo/view/MyBarChart.java: -------------------------------------------------------------------------------- 1 | package kong.qingwei.combinedchartdemo.view; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | import android.util.AttributeSet; 6 | 7 | import com.github.mikephil.charting.charts.BarChart; 8 | import com.github.mikephil.charting.components.XAxis; 9 | import com.github.mikephil.charting.components.YAxis; 10 | import com.github.mikephil.charting.data.BarData; 11 | import com.github.mikephil.charting.data.BarDataSet; 12 | import com.github.mikephil.charting.data.BarEntry; 13 | import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; 14 | 15 | import java.util.ArrayList; 16 | 17 | import kong.qingwei.combinedchartdemo.bean.CombinedChartEntity; 18 | import kong.qingwei.combinedchartdemo.utils.MyCustomXAxisValueFormatter; 19 | 20 | /** 21 | * Created by kqw on 2016/5/25. 22 | * 柱形图 23 | */ 24 | public class MyBarChart extends BarChart { 25 | public MyBarChart(Context context, AttributeSet attrs) { 26 | super(context, attrs); 27 | initChart(); 28 | 29 | 30 | } 31 | 32 | private void initChart() { 33 | setDescription(""); 34 | setBackgroundColor(Color.WHITE); 35 | setDrawGridBackground(false); 36 | setDrawBarShadow(false); 37 | 38 | // 取消Y轴缩放动画 39 | setScaleYEnabled(false); 40 | 41 | // 自动缩放调整 42 | setAutoScaleMinMaxEnabled(true); 43 | 44 | /* 45 | * Y轴 46 | * ******************************************************************************/ 47 | YAxis left = getAxisLeft(); 48 | // 左侧Y轴坐标 49 | left.setDrawLabels(true); 50 | // 左侧Y轴 51 | left.setDrawAxisLine(true); 52 | // 横向线 53 | left.setDrawGridLines(true); 54 | // 纵轴数据显示在图形内部 55 | left.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART); 56 | // 不显示右侧Y轴 57 | getAxisRight().setEnabled(false); 58 | 59 | /* 60 | * X轴 61 | * ******************************************************************************/ 62 | XAxis xAxis = getXAxis(); 63 | xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); 64 | // 格式化X轴时间 65 | xAxis.setValueFormatter(new MyCustomXAxisValueFormatter()); 66 | 67 | 68 | setDrawBarShadow(false); 69 | setDrawValueAboveBar(true); 70 | 71 | setDescription(""); 72 | 73 | // if more than 60 entries are displayed in the chart, no values will be 74 | // drawn 75 | setMaxVisibleValueCount(60); 76 | 77 | // scaling can now only be done on x- and y-axis separately 78 | setPinchZoom(false); 79 | 80 | setDrawGridBackground(false); 81 | 82 | } 83 | 84 | 85 | /** 86 | * 填充数据 87 | * 88 | * @param entity 数据实体 89 | */ 90 | public void setData(CombinedChartEntity entity) { 91 | int count = entity.getData().size(); 92 | ArrayList xVals = new ArrayList(); 93 | for (int i = 0; i < count; i++) { 94 | xVals.add(entity.getData().get(i).get(0) + ""); 95 | } 96 | 97 | ArrayList yVals1 = new ArrayList(); 98 | 99 | for (int i = 0; i < count; i++) { 100 | float val = (float) (entity.getData().get(i).get(5)); 101 | yVals1.add(new BarEntry(val, i)); 102 | } 103 | 104 | BarDataSet set1 = new BarDataSet(yVals1, "DataSet"); 105 | set1.setBarSpacePercent(35f); 106 | 107 | ArrayList dataSets = new ArrayList(); 108 | dataSets.add(set1); 109 | 110 | BarData data = new BarData(xVals, dataSets); 111 | data.setValueTextSize(10f); 112 | // data.setValueTypeface(mTf); 113 | 114 | setData(data); 115 | 116 | // 最多显示60组数据 117 | setVisibleXRangeMaximum(60); 118 | // 最少显示30组数据 119 | setVisibleXRangeMinimum(30); 120 | // 移动到最右侧数据 121 | moveViewToX(entity.getData().size() - 1); 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /app/src/main/java/kong/qingwei/combinedchartdemo/view/MyCombinedChart.java: -------------------------------------------------------------------------------- 1 | package kong.qingwei.combinedchartdemo.view; 2 | 3 | import android.app.Service; 4 | import android.content.Context; 5 | import android.graphics.Color; 6 | import android.os.Handler; 7 | import android.os.Message; 8 | import android.os.Vibrator; 9 | import android.util.AttributeSet; 10 | import android.util.Log; 11 | import android.view.MotionEvent; 12 | import android.view.WindowManager; 13 | import android.widget.Toast; 14 | 15 | import com.github.mikephil.charting.charts.CombinedChart; 16 | import com.github.mikephil.charting.components.XAxis; 17 | import com.github.mikephil.charting.components.YAxis; 18 | import com.github.mikephil.charting.data.CandleData; 19 | import com.github.mikephil.charting.data.CandleDataSet; 20 | import com.github.mikephil.charting.data.CandleEntry; 21 | import com.github.mikephil.charting.data.CombinedData; 22 | import com.github.mikephil.charting.data.Entry; 23 | import com.github.mikephil.charting.data.LineData; 24 | import com.github.mikephil.charting.data.LineDataSet; 25 | import com.github.mikephil.charting.highlight.Highlight; 26 | import com.github.mikephil.charting.listener.ChartTouchListener; 27 | import com.github.mikephil.charting.listener.OnChartGestureListener; 28 | import com.github.mikephil.charting.listener.OnChartValueSelectedListener; 29 | 30 | import java.util.ArrayList; 31 | 32 | import kong.qingwei.combinedchartdemo.listener.OnValueSelectedListener; 33 | import kong.qingwei.combinedchartdemo.utils.MyChartHighlighter; 34 | import kong.qingwei.combinedchartdemo.utils.MyCustomXAxisValueFormatter; 35 | import kong.qingwei.combinedchartdemo.bean.CombinedChartEntity; 36 | 37 | /** 38 | * Created by kqw on 2016/5/16. 39 | * MyCombinedChart 40 | */ 41 | public class MyCombinedChart extends CombinedChart implements OnChartGestureListener, OnChartValueSelectedListener { 42 | 43 | private static final String TAG = "MyCombinedChart"; 44 | 45 | private boolean isTranslate; 46 | private final int mWidth; 47 | private final Vibrator mVibrator; 48 | private MyChartHighlighter myChartHighlighter; 49 | private CombinedChartEntity mCombinedChartEntity; 50 | private OnValueSelectedListener mOnValueSelectedListener; 51 | 52 | private Handler invalidate = new Handler() { 53 | @Override 54 | public void handleMessage(Message msg) { 55 | invalidate(); 56 | } 57 | }; 58 | 59 | public MyCombinedChart(Context context, AttributeSet attrs) { 60 | super(context, attrs); 61 | initChart(); 62 | 63 | mVibrator = (Vibrator) context.getSystemService(Service.VIBRATOR_SERVICE); 64 | 65 | WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); 66 | mWidth = windowManager.getDefaultDisplay().getWidth(); 67 | } 68 | 69 | private void initChart() { 70 | setDescription(""); 71 | setBackgroundColor(Color.WHITE); 72 | setDrawGridBackground(false); 73 | setDrawBarShadow(false); 74 | 75 | // 取消Y轴缩放动画 76 | setScaleYEnabled(false); 77 | 78 | // 自动缩放调整 79 | setAutoScaleMinMaxEnabled(true); 80 | 81 | /* 82 | * Y轴 83 | * ******************************************************************************/ 84 | YAxis left = getAxisLeft(); 85 | // 左侧Y轴坐标 86 | left.setDrawLabels(true); 87 | // 左侧Y轴 88 | left.setDrawAxisLine(true); 89 | // 横向线 90 | left.setDrawGridLines(true); 91 | // 纵轴数据显示在图形内部 92 | left.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART); 93 | // 不显示右侧Y轴 94 | getAxisRight().setEnabled(false); 95 | 96 | /* 97 | * X轴 98 | * ******************************************************************************/ 99 | XAxis xAxis = getXAxis(); 100 | xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); 101 | // 格式化X轴时间 102 | xAxis.setValueFormatter(new MyCustomXAxisValueFormatter()); 103 | 104 | /* 105 | * 图形触摸监听 106 | * *********************************************************************************/ 107 | setOnChartGestureListener(this); 108 | 109 | /* 110 | * 被选择监听(高亮监听) 111 | * **********************************************************************************/ 112 | setOnChartValueSelectedListener(this); 113 | 114 | // 用来将像素转成index 115 | myChartHighlighter = new MyChartHighlighter(this); 116 | 117 | } 118 | 119 | /** 120 | * 填充数据 121 | * 122 | * @param entity 数据实体 123 | */ 124 | public void setData(CombinedChartEntity entity) { 125 | mCombinedChartEntity = entity; 126 | ArrayList timeY = new ArrayList<>(); 127 | for (int i = 0; i < entity.getData().size(); i++) { 128 | timeY.add(entity.getData().get(i).get(0) + ""); 129 | } 130 | CombinedData data = new CombinedData(timeY); 131 | data.setData(generateCandleData(entity)); 132 | data.setData(generateLineData(entity)); 133 | // data.setData(generateBarData(empty)); 134 | // data.setData(generateBubbleData()); 135 | // data.setData(generateScatterData()); 136 | setData(data); 137 | 138 | notifyDataSetChanged(); 139 | 140 | // 最多显示60组数据 141 | setVisibleXRangeMaximum(60); 142 | // 最少显示30组数据 143 | setVisibleXRangeMinimum(30); 144 | // 移动到最右侧数据 145 | moveViewToX(entity.getData().size() - 1); 146 | /* 147 | 延迟100毫秒执行invalidate 148 | 为了解决控件使用setAutoScaleMinMaxEnabled方法后的一个小bug 149 | */ 150 | invalidate.sendEmptyMessageDelayed(0, 100); 151 | } 152 | 153 | protected CandleData generateCandleData(CombinedChartEntity entity) { 154 | CandleData d = new CandleData(); 155 | ArrayList entries = new ArrayList<>(); 156 | for (int index = 0; index < entity.getData().size(); index++) { 157 | long a = entity.getData().get(index).get(1) / 1000; 158 | long b = entity.getData().get(index).get(2) / 1000; 159 | long c = entity.getData().get(index).get(3) / 1000; 160 | long dd = entity.getData().get(index).get(4) / 1000; 161 | entries.add(new CandleEntry(index, a, b, c, dd)); 162 | } 163 | CandleDataSet set = new CandleDataSet(entries, "K线"); 164 | // 不显示横向高亮线 165 | set.setDrawHorizontalHighlightIndicator(false); 166 | set.setColor(Color.rgb(80, 80, 80)); 167 | set.setValueTextSize(10f); 168 | set.setDrawValues(false); 169 | d.addDataSet(set); 170 | return d; 171 | } 172 | 173 | private LineData generateLineData(CombinedChartEntity entity) { 174 | LineData d = new LineData(); 175 | d.addDataSet(getLineDataSet(5, entity)); 176 | d.addDataSet(getLineDataSet(10, entity)); 177 | d.addDataSet(getLineDataSet(30, entity)); 178 | return d; 179 | } 180 | 181 | private LineDataSet getLineDataSet(int ma, CombinedChartEntity empty) { 182 | 183 | ArrayList entries = new ArrayList<>(); 184 | for (int index = ma - 1; index < empty.getData().size(); index++) { 185 | long sum = 0; 186 | for (int m = 0; m < ma; m++) { 187 | sum += (empty.getData().get(index - m).get(3) / 1000); 188 | } 189 | sum /= ma; 190 | entries.add(new Entry(sum, index)); 191 | } 192 | LineDataSet set = new LineDataSet(entries, "MA " + ma); 193 | // 不显示横向高亮线 194 | set.setDrawHorizontalHighlightIndicator(false); 195 | set.setColor(5 == ma ? Color.rgb(240, 0, 70) : 10 == ma ? Color.rgb(0, 0, 70) : Color.rgb(100, 100, 255)); 196 | set.setLineWidth(1f); 197 | set.setDrawCircles(false); 198 | set.setDrawCubic(false); 199 | set.setDrawValues(false); 200 | set.setValueTextSize(10f); 201 | set.setValueTextColor(Color.rgb(240, 238, 70)); 202 | set.setAxisDependency(YAxis.AxisDependency.LEFT); 203 | return set; 204 | } 205 | 206 | /* 207 | * Gesture callbacks 208 | * Start 209 | * *******************************************************************************/ 210 | @Override 211 | public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { 212 | isTranslate = false; 213 | } 214 | 215 | @Override 216 | public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { 217 | setDragEnabled(true); 218 | getData().setHighlightEnabled(false); 219 | 220 | if (null != mOnValueSelectedListener) { 221 | mOnValueSelectedListener.end(); 222 | } 223 | } 224 | 225 | @Override 226 | public void onChartLongPressed(MotionEvent me) { 227 | if (!isTranslate) { 228 | Toast.makeText(getContext().getApplicationContext(), "长按\n震动50毫秒\n可以左右滑动 查看数据", Toast.LENGTH_SHORT).show(); 229 | // 震动50毫秒 230 | mVibrator.vibrate(50); 231 | setDragEnabled(false); 232 | getData().setHighlightEnabled(true); 233 | 234 | float x = me.getRawX(); 235 | // TODO 通过像素换算index 高亮显示 236 | int index = myChartHighlighter.getXIndex(x); 237 | highlightValue(index, 0); 238 | 239 | if (null != mOnValueSelectedListener) { 240 | mOnValueSelectedListener.start(); 241 | long open = mCombinedChartEntity.getData().get(index).get(1) / 1000; 242 | long close = mCombinedChartEntity.getData().get(index).get(2) / 1000; 243 | long high = mCombinedChartEntity.getData().get(index).get(3) / 1000; 244 | long low = mCombinedChartEntity.getData().get(index).get(4) / 1000; 245 | mOnValueSelectedListener.data(open, close, high, low); 246 | } 247 | } 248 | } 249 | 250 | @Override 251 | public void onChartDoubleTapped(MotionEvent me) { 252 | } 253 | 254 | @Override 255 | public void onChartSingleTapped(MotionEvent me) { 256 | } 257 | 258 | @Override 259 | public void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) { 260 | } 261 | 262 | @Override 263 | public void onChartScale(MotionEvent me, float scaleX, float scaleY) { 264 | } 265 | 266 | @Override 267 | public void onChartTranslate(MotionEvent me, float dX, float dY) { 268 | isTranslate = true; 269 | } 270 | /* End *******************************************************************************/ 271 | 272 | /* 273 | * Selection callbacks 274 | * Start 275 | * *******************************************************************************/ 276 | @Override 277 | public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { 278 | // TODO index转换成像素 279 | // float f = me.getRawX(); 280 | // if (f < mWidth / 2) { 281 | // Log.i(TAG, "显示在右侧"); 282 | // } else { 283 | // Log.i(TAG, "显示在左侧"); 284 | // } 285 | 286 | try { 287 | assert null != mCombinedChartEntity; 288 | long open = mCombinedChartEntity.getData().get(e.getXIndex()).get(1) / 1000; 289 | long close = mCombinedChartEntity.getData().get(e.getXIndex()).get(2) / 1000; 290 | long high = mCombinedChartEntity.getData().get(e.getXIndex()).get(3) / 1000; 291 | long low = mCombinedChartEntity.getData().get(e.getXIndex()).get(4) / 1000; 292 | if (null != mOnValueSelectedListener) { 293 | mOnValueSelectedListener.data(open, close, high, low); 294 | } 295 | } catch (Exception e1) { 296 | e1.printStackTrace(); 297 | } 298 | } 299 | 300 | @Override 301 | public void onNothingSelected() { 302 | Log.i(TAG, "onNothingSelected"); 303 | } 304 | /* End *******************************************************************************/ 305 | 306 | public void setOnValueSelectedListener(OnValueSelectedListener listener) { 307 | mOnValueSelectedListener = listener; 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /app/src/main/java/kong/qingwei/combinedchartdemo/view/MyPopupWindow.java: -------------------------------------------------------------------------------- 1 | package kong.qingwei.combinedchartdemo.view; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | import android.graphics.drawable.ColorDrawable; 6 | import android.support.v7.widget.LinearLayoutManager; 7 | import android.support.v7.widget.RecyclerView; 8 | import android.util.Log; 9 | import android.view.Gravity; 10 | import android.view.KeyEvent; 11 | import android.view.View; 12 | import android.view.WindowManager; 13 | import android.view.animation.Animation; 14 | import android.view.animation.ScaleAnimation; 15 | import android.widget.PopupWindow; 16 | 17 | import kong.qingwei.combinedchartdemo.R; 18 | import kong.qingwei.combinedchartdemo.adapter.MyPopupWindowAdapter; 19 | import kong.qingwei.combinedchartdemo.listener.OnPopupWindowItemClickListener; 20 | 21 | /** 22 | * Created by kqw on 2016/5/17. 23 | * MyPopupWindow 24 | */ 25 | public class MyPopupWindow extends PopupWindow implements View.OnKeyListener, OnPopupWindowItemClickListener { 26 | 27 | private PopupWindow mPopupWindow; 28 | private final View mContentView; 29 | private OnPopupWindowItemClickListener mOnPopupWindowItemClickListener; 30 | private MyPopupWindowAdapter mPopupWindowAdapter; 31 | 32 | public MyPopupWindow(Context context) { 33 | // 获取弹出的PopupWindow的界面 34 | mContentView = View.inflate(context, R.layout.popupwindow, null); 35 | initRecyclerView(context); 36 | // 监听点击返回键 37 | mContentView.setOnKeyListener(this); 38 | // 创建一个PopupWindow并默认获取焦点(如果没有焦点view无法监听到点击事件) 39 | mPopupWindow = new PopupWindow(mContentView, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, true); 40 | // ***给PopupWindow设置背景 41 | mPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); 42 | // 设置PopupWindow之外的其他位置消失 43 | mPopupWindow.setOutsideTouchable(true); 44 | 45 | mPopupWindow.update(); 46 | } 47 | 48 | private void initRecyclerView(Context context) { 49 | RecyclerView mRecyclerView = (RecyclerView) mContentView.findViewById(R.id.recyclerView); 50 | // 如果数据的填充不会改变RecyclerView的布局大小,那么这个设置可以提高RecyclerView的性能 51 | mRecyclerView.setHasFixedSize(true); 52 | // 设置这个RecyclerView是线性布局 53 | RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(context); 54 | mRecyclerView.setLayoutManager(mLayoutManager); 55 | // 给RecyclerView添加一个适配器显示数据 56 | mPopupWindowAdapter = new MyPopupWindowAdapter(); 57 | // 添加PopupWindow条目点击的监听 58 | mPopupWindowAdapter.setOnPopupWindowItemClickListener(this); 59 | mRecyclerView.setAdapter(mPopupWindowAdapter); 60 | } 61 | 62 | 63 | /** 64 | * 设置动画 65 | */ 66 | public void startAnimation() { 67 | // 要想动画能够正常播放 必须给PopupWindow设置背景 68 | mPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); 69 | // 设置动画从没有弹出到本身大小 70 | ScaleAnimation animation = new ScaleAnimation(1, 1, 0, 1, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0f); 71 | // 动画时间 72 | animation.setDuration(200); 73 | // 动画弹出 74 | mContentView.startAnimation(animation); 75 | } 76 | 77 | /** 78 | * 显示PopupWindow 79 | * 80 | * @param parent 父控件 81 | */ 82 | public void showPopupWindow(View parent) { 83 | int[] location = new int[2]; 84 | // 测量被点击的view在窗体中的位置 将位置保存在一个int数组中 便于设置PopupWindow显示的位置 85 | parent.getLocationInWindow(location); 86 | // 将PopupWindow显示在指定位置上 87 | // mPopupWindow.showAtLocation(parent, Gravity.BOTTOM | Gravity.LEFT, location[0], location[1]); 88 | mPopupWindow.showAtLocation(parent, Gravity.TOP | Gravity.START, location[0], location[1] + parent.getHeight()); 89 | } 90 | 91 | /** 92 | * 关闭PopupWindow 93 | */ 94 | public void dismiss() { 95 | if (null != mPopupWindow && mPopupWindow.isShowing()) { 96 | mPopupWindow.dismiss(); 97 | } 98 | } 99 | 100 | /** 101 | * 开关 102 | */ 103 | public void toggle(View parent) { 104 | if (mPopupWindow.isShowing()) { 105 | dismiss(); 106 | } else { 107 | showPopupWindow(parent); 108 | } 109 | } 110 | 111 | @Override 112 | public boolean onKey(View v, int keyCode, KeyEvent event) { 113 | switch (keyCode) { 114 | case KeyEvent.KEYCODE_BACK: 115 | // 按返回键关闭PopupWindow 116 | dismiss(); 117 | return true; 118 | default: 119 | return false; 120 | } 121 | } 122 | 123 | @Override 124 | public void click(int position) { 125 | mOnPopupWindowItemClickListener.click(position); 126 | dismiss(); 127 | Log.i("MyPopupWindow","position = " + position); 128 | } 129 | 130 | /** 131 | * 添加条目点击的监听 132 | * 133 | * @param listener 回调接口 134 | */ 135 | public void setOnPopupWindowItemClickListener(OnPopupWindowItemClickListener listener) { 136 | mOnPopupWindowItemClickListener = listener; 137 | } 138 | 139 | public String getContent(int position) { 140 | return mPopupWindowAdapter.getContent(position); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /app/src/main/java/kong/qingwei/combinedchartdemo/view/MyTabLayout.java: -------------------------------------------------------------------------------- 1 | package kong.qingwei.combinedchartdemo.view; 2 | 3 | import android.content.Context; 4 | import android.support.design.widget.TabLayout; 5 | import android.util.AttributeSet; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.widget.Button; 9 | import android.widget.TextView; 10 | 11 | import kong.qingwei.combinedchartdemo.R; 12 | import kong.qingwei.combinedchartdemo.listener.OnPopupWindowItemClickListener; 13 | import kong.qingwei.combinedchartdemo.utils.ToastUtil; 14 | 15 | 16 | /** 17 | * Created by kqw on 2016/5/17. 18 | * 模拟自选股导航栏 19 | */ 20 | public class MyTabLayout extends TabLayout implements View.OnClickListener, OnPopupWindowItemClickListener, TabLayout.OnTabSelectedListener { 21 | 22 | private final String TAG = this.getClass().getSimpleName(); 23 | 24 | private String[] mTitles = new String[]{ 25 | "分时", 26 | "日K", 27 | "周K", 28 | "月K"}; 29 | private MyPopupWindow myPopupWindow; 30 | private Button mButton; 31 | 32 | public MyTabLayout(Context context, AttributeSet attrs) { 33 | super(context, attrs); 34 | initMyTabLayout(context); 35 | } 36 | 37 | private void initMyTabLayout(Context context) { 38 | for (int i = 0; i < 5; i++) { 39 | Tab tab = newTab(); 40 | tab.setCustomView(getTabView(i)); 41 | addTab(tab); 42 | } 43 | setOnTabSelectedListener(this); 44 | // PopupWindow 45 | myPopupWindow = new MyPopupWindow(context); 46 | myPopupWindow.setOnPopupWindowItemClickListener(this); 47 | } 48 | 49 | public View getTabView(int position) { 50 | View v = null; 51 | switch (position) { 52 | case 0: 53 | case 1: 54 | case 2: 55 | case 3: 56 | v = LayoutInflater.from(getContext().getApplicationContext()).inflate(R.layout.item_text_tab, null); 57 | final TextView tv = (TextView) v.findViewById(R.id.textView); 58 | tv.setText(mTitles[position]); 59 | 60 | break; 61 | case 4: 62 | v = LayoutInflater.from(getContext().getApplicationContext()).inflate(R.layout.item_button_tab, null); 63 | mButton = (Button) v.findViewById(R.id.button); 64 | mButton.setOnClickListener(this); 65 | break; 66 | } 67 | return v; 68 | } 69 | 70 | @Override 71 | public void onClick(View v) { 72 | switch (v.getId()) { 73 | case R.id.button: 74 | myPopupWindow.toggle(v); 75 | break; 76 | } 77 | } 78 | 79 | @Override 80 | public void click(int position) { 81 | mButton.setText(myPopupWindow.getContent(position)); 82 | getTabAt(getTabCount() - 1).select(); 83 | // TODO 请求数据 84 | switch (position) { 85 | case 0: 86 | ToastUtil.show(getContext().getApplicationContext(), "TODO 请求【5分钟】数据"); 87 | break; 88 | case 1: 89 | ToastUtil.show(getContext().getApplicationContext(), "TODO 请求【15分钟】数据"); 90 | break; 91 | case 2: 92 | ToastUtil.show(getContext().getApplicationContext(), "TODO 请求【30分钟】数据"); 93 | break; 94 | case 3: 95 | ToastUtil.show(getContext().getApplicationContext(), "TODO 请求【60分钟】数据"); 96 | break; 97 | } 98 | } 99 | 100 | @Override 101 | public void onTabSelected(Tab tab) { 102 | // TODO 请求数据 103 | switch (tab.getPosition()) { 104 | case 0: 105 | ToastUtil.show(getContext().getApplicationContext(), "TODO 请求【分时】数据"); 106 | break; 107 | case 1: 108 | ToastUtil.show(getContext().getApplicationContext(), "TODO 请求【日K】数据"); 109 | break; 110 | case 2: 111 | ToastUtil.show(getContext().getApplicationContext(), "TODO 请求【周K】数据"); 112 | break; 113 | case 3: 114 | ToastUtil.show(getContext().getApplicationContext(), "TODO 请求【月K】数据"); 115 | break; 116 | case 4: 117 | myPopupWindow.showPopupWindow(mButton); 118 | break; 119 | } 120 | } 121 | 122 | @Override 123 | public void onTabUnselected(Tab tab) { 124 | if (getTabCount() - 1 == tab.getPosition()) { 125 | mButton.setText("分钟"); 126 | } 127 | } 128 | 129 | @Override 130 | public void onTabReselected(Tab tab) { 131 | 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | 17 | 18 | 26 | 27 | 34 | 35 | 42 | 43 | 50 | 51 | 58 | 59 | 66 | 67 | 74 | 75 | 82 | 83 | 90 | 91 | 92 | 93 | 94 | 95 | 101 | 102 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_button_tab.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 |