如何在 android studio 中使用 MpAndroidChart 库实现实时烛台图?

问题描述

From copying and pasting:我设法实现了从 API (ETH/USDT) 获取实时数据的烛台图。

该应用实际上正在读取数据并且正在显示烛台,但我现在的两个问题是:

  1. 我必须手动刷新活动才能加载数据。我想知道我是否可以实现一个监听器,它可以实时加载数据,而不必每秒手动刷新。

  2. 我想看到我的烛台形成和收盘,就像元交易者应用程序所做的那样。例如在 1 分钟图上:从一分钟开始,我想看到烛台建筑做一些运动(也许我应该称之为动画?)直到它在 1 分钟处关闭

我可能在想,如果我能解决 1 号提到的问题,2 号会做出相应的反应,但不确定。

MainActivity.java:看起来像:

 private String URL = "https://openapi.dragonex.im/api/v1/market/kline/?symbol_id=......................................";
private final String[] KLINE = {"1m","5m","15m","30m","1h","1d"};
private int[] KL_TYPE = {1,2,3,4,5,6};
private int[] KL_INTERVAL = {1,15,30,60,1440};//单位: Min
private final long M1 = 60 * 1000L;//1 Min的毫秒数

//5日均线、10日均线
private String Kline5_10 = "<font color=#B230ED>MA5:%s</font> <font color=#EFBB40>MA10:%s</font>";

private LoadingDialog loadingDialog;
private Gson gson = new GsonBuilder().create();

private DecimalFormat format4p = new DecimalFormat("0.0000");//格式化数字,保留小数点后4位
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm",Locale.getDefault());

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//认竖屏
    initView();
    loadData();
}

private void initView() {
    ivBack = findViewById(R.id.iv_klBack);
    ivOri = findViewById(R.id.iv_klOrientation);

    tabLayout = findViewById(R.id.tl_kl);
    clHl = findViewById(R.id.cl_klHighlight);

    tvOpen = findViewById(R.id.tv_klOpen);
    tvClose = findViewById(R.id.tv_klClose);
    tvHigh = findViewById(R.id.tv_klHigh);
    tvLow = findViewById(R.id.tv_klLow);
    tvVol = findViewById(R.id.tv_klVol);
    tvLine = findViewById(R.id.tv_klLineInfo);

    cc = findViewById(R.id.cc_kl);
    bc = findViewById(R.id.bc_kl);

    ivBack.setonClickListener(this);
    ivOri.setonClickListener(this);

    for (int i = 0; i < KLINE.length; i++) {
        TextView v = (TextView) LayoutInflater.from(this).inflate(R.layout.item_tab_kline,null);
        v.setText(KLINE[i]);
        tabLayout.addTab(tabLayout.newTab().setCustomView(v),i == index);
    }
    tabLayout.addOnTabSelectedListener(this);

    initChart();
}

private void initChart() {
    int black = getColorById(R.color.black3B);
    int gray = getColorById(R.color.gray8B);
    int red = getColorById(R.color.redEB);
    int green = getColorById(R.color.green4C);
    int highlightColor = getColorById(R.color.brown);
    float highlightWidth = 0.5F;//高亮线的线宽
    float sp8 = sp2px(8);
    //K线
    cc.setNoDataTextColor(gray);//无数据时提示文字的颜色
    cc.setDescription(null);//取消描述
    cc.getLegend().setEnabled(false);//取消图例
    cc.setDragDecelerationEnabled(false);//不允许甩动惯性滑动  和moveView方法有冲突 设置为false
    cc.setMinOffset(0);//设置外边缘偏移量
    cc.setExtraBottomOffset(6);//设置底部外边缘偏移量 便于显示X轴

    cc.setScaleEnabled(false);//不可缩放
    cc.setAutoScaleMinMaxEnabled(true);//自适应最大最小值
    cc.setDrawOrder(new CombinedChart.DrawOrder[]{CombinedChart.DrawOrder.CANDLE,CombinedChart.DrawOrder.LINE});
    Transformer trans = cc.getTransformer(YAxis.AxisDependency.LEFT);
    //自定义X轴标签位置
    cc.setXAxisRenderer(new InBoundXAxisRenderer(cc.getViewPortHandler(),cc.getXAxis(),trans,10));
    //自定义Y轴标签位置
    cc.setRendererLeftYAxis(new InBoundYAxisRenderer(cc.getViewPortHandler(),cc.getAxisLeft(),trans));
    //自定义渲染器 重绘高亮
    cc.setRenderer(new HighlightCombinedRenderer(cc,cc.getAnimator(),cc.getViewPortHandler(),sp8));

    //X轴
    XAxis xac = cc.getXAxis();
    xac.setPosition(XAxis.XAxisPosition.BottOM);
    xac.setGridColor(black);//网格线颜色
    xac.setTextColor(gray);//标签颜色
    xac.setTextSize(8);//标签字体大小
    xac.setAxisLineColor(black);//轴线颜色
    xac.disableAxisLineDashedLine();//取消轴线虚线设置
    xac.setAvoidFirstLastClipping(true);//避免首尾端标签被裁剪
    xac.setLabelCount(2,true);//强制显示2个标签
    xac.setValueFormatter(new IAxisValueFormatter() {//转换X轴的数字为文字
        @Override
        public String getFormattedValue(float value,AxisBase axis) {
            int v = (int) value;
            if (!xValues.containsKey(v) && xValues.containsKey(v - 1)) {
                v = v - 1;
            }
            String x = xValues.get(v);
            return TextUtils.isEmpty(x) ? "" : x;
        }
    });

    //左Y轴
    YAxis yac = cc.getAxisLeft();
    yac.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART);//标签显示在内侧
    yac.setGridColor(black);
    yac.setTextColor(gray);
    yac.setTextSize(8);
    yac.setLabelCount(5,true);
    yac.enableGridDashedLine(5,0);//横向网格线设置为虚线
    yac.setValueFormatter(new IAxisValueFormatter() {
        @Override
        public String getFormattedValue(float value,AxisBase axis) {//只显示部分标签
            int index = getIndexY(value,axis.getAxisMinimum(),axis.getAxisMaximum());
            return index == 0 || index == 2 ? format4p.format(value) : "";//不显示标签不能返回null
        }
    });
    //右Y轴
    cc.getAxisRight().setEnabled(false);

    //蜡烛图
    candleSet = new CandleDataSet(new ArrayList<CandleEntry>(),"Kline");
    candleSet.setAxisDependency(YAxis.AxisDependency.LEFT);
    candleSet.setDrawHorizontalHighlightIndicator(false);
    candleSet.setHighlightlinewidth(highlightWidth);
    candleSet.setHighLightColor(highlightColor);
    candleSet.setShadowWidth(0.7f);
    candleSet.setIncreasingColor(red);//上涨为红色
    candleSet.setIncreasingPaintStyle(Paint.Style.FILL);
    candleSet.setDecreasingColor(green);//下跌为绿色
    candleSet.setDecreasingPaintStyle(Paint.Style.stroke);
    candleSet.setNeutralColor(red);
    candleSet.setShadowColorSameAsCandle(true);
    candleSet.setDrawValues(false);
    candleSet.setHighlightEnabled(false);
    //均线
    lineset5 = new LineDataSet(new ArrayList<Entry>(),"MA5");
    lineset5.setAxisDependency(YAxis.AxisDependency.LEFT);
    lineset5.setColor(getColorById(R.color.purple));
    lineset5.setDrawCircles(false);
    lineset5.setDrawValues(false);
    lineset5.setHighlightEnabled(false);
    lineset10 = new LineDataSet(new ArrayList<Entry>(),"MA10");
    lineset10.setAxisDependency(YAxis.AxisDependency.LEFT);
    lineset10.setColor(getColorById(R.color.yellow));
    lineset10.setDrawCircles(false);
    lineset10.setDrawValues(false);
    lineset10.setHighlightEnabled(false);
    //分时线
    linesetMin = new LineDataSet(new ArrayList<Entry>(),"Minutes");
    linesetMin.setAxisDependency(YAxis.AxisDependency.LEFT);
    linesetMin.setColor(Color.WHITE);
    linesetMin.setDrawCircles(false);
    linesetMin.setDrawValues(false);
    linesetMin.setDrawFilled(true);
    linesetMin.setHighlightEnabled(false);
    linesetMin.setFillColor(gray);
    linesetMin.setFillAlpha(60);


    //成交量
    bc.setNoDataTextColor(gray);
    bc.setDescription(null);
    bc.getLegend().setEnabled(false);
    bc.setDragDecelerationEnabled(false);//不允许甩动惯性滑动
    bc.setMinOffset(0);//设置外边缘偏移量

    bc.setScaleEnabled(false);//不可缩放
    bc.setAutoScaleMinMaxEnabled(true);//自适应最大最小值
    //自定义Y轴标签位置
    bc.setRendererLeftYAxis(new InBoundYAxisRenderer(bc.getViewPortHandler(),bc.getAxisLeft(),bc.getTransformer(YAxis.AxisDependency.LEFT)));
    //设置渲染器控制颜色、偏移,以及高亮
    bc.setRenderer(new OffsetBarRenderer(bc,bc.getAnimator(),bc.getViewPortHandler(),barOffset)
            .setHighlightWidthSize(highlightWidth,sp8));

    bc.getXAxis().setEnabled(false);
    YAxis yab = bc.getAxisLeft();
    yab.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART);//标签显示在内侧
    yab.setDrawAxisLine(false);
    yab.setGridColor(black);
    yab.setTextColor(gray);
    yab.setTextSize(8);
    yab.setLabelCount(2,true);
    yab.setAxisMinimum(0);
    yab.setValueFormatter(new IAxisValueFormatter() {
        @Override
        public String getFormattedValue(float value,AxisBase axis) {
            return value == 0 ? "" : value + "";
        }
    });
    bc.getAxisRight().setEnabled(false);

    barSet = new BarDataSet(new ArrayList<BarEntry>(),"VOL");
    barSet.setHighLightColor(highlightColor);
    barSet.setColors(red,green);
    barSet.setDrawValues(false);
    barSet.setHighlightEnabled(false);

    ccGesture = new CoupleChartGestureListener(this,cc,bc) {//设置成全局变量,后续要用到
        @Override
        public void chartDoubleTapped(MotionEvent me) {
            doubleTapped();
        }
    };
    cc.setonChartGestureListener(ccGesture);//设置手势联动监听
    bcGesture = new CoupleChartGestureListener(this,bc,cc) {
        @Override
        public void chartDoubleTapped(MotionEvent me) {
            doubleTapped();
        }
    };
    bc.setonChartGestureListener(bcGesture);

    cc.setonChartValueSelectedListener(new CoupleChartValueSelectedListener(this,bc));//设置高亮联动监听
    bc.setonChartValueSelectedListener(new CoupleChartValueSelectedListener(this,cc));
    cc.setonTouchListener(new ChartFingerTouchListener(cc,this));//手指长按滑动高亮
    bc.setonTouchListener(new ChartFingerTouchListener(bc,this));
}

/**
 * 计算value是当前Y轴的第几个
 */
private int getIndexY(float value,float min,float max) {
    float piece = (max - min) / 4;
    return Math.round((value - min) / piece);
}

protected void loadData() {
    clearChart();
    toLeft = true;
    getData("0");
}

private void clearChart() {
    if (dataList == null) {
        dataList = new ArrayList<>();
    } else {
        dataList.clear();
    }
    if (xValues == null) {
        xValues = new HashMap<>();
    } else {
        xValues.clear();
    }
    cc.setNoDataText("加载中...");
    cc.clear();
    bc.setNoDataText("加载中...");
    bc.clear();
}

private void getData(String time) {
    String url = String.format(URL,time,toLeft ? "2" : "1",KL_TYPE[index]);
    OkHttpUtil.getJSON(url,this);
}

@Override
public void onTabSelected(TabLayout.Tab tab) {
    index = tab.getPosition();
    loadData();
}

@Override
public void onTabReselected(TabLayout.Tab tab) {
    onTabSelected(tab);
}

@Override
public void onTabUnselected(TabLayout.Tab tab) {}

@Override
public void onResponse(String url,String json) {
    Log.e("loge","Kline: " + json);
    KlineEntity kl = gson.fromJson(json,KlineEntity.class);
    if (kl.isOk()) {
        int size = xValues.size();
        List<List<String>> lists = kl.getData().getLists();
        if (lists.size() <= 0) {
            dismissLoading();
            return;
        }
        if (lists.size() == 1) {
            long time = Long.parseLong(lists.get(0).get(6)) * 1000;
            String x = sdf.format(new Date(time));
            if (!xValues.containsValue(x)) {
                handleData(lists,size);
            }
        } else {
            handleData(lists,size);
        }
    }
    dismissLoading();
}

/**
 * size是指追加数据之前,已有的数据个数
 */
private void handleData(List<List<String>> lists,int size) {
    if (toLeft) {
        dataList.addAll(0,lists);//添加到左侧
    } else {
        dataList.addAll(lists);
    }

    configData();
    if (xValues.size() > 0) {
        int x = xValues.size() - (toLeft ? size : 0);
        //如果设置了惯性甩动 move方法将会无效
        if (!toLeft && size > 0) {
            cc.moveViewToAnimated(x,YAxis.AxisDependency.LEFT,200);
            bc.moveViewToAnimated(x + barOffset,200);
        } else {
            cc.moveViewToX(x);
            bc.moveViewToX(x + barOffset);
        }
        cc.notifyDataSetChanged();
        bc.notifyDataSetChanged();
    }
}

private void configData() {
    if (dataList.size() == 0) {
        cc.setNoDataText("暂无相关数据");
        cc.clear();
        bc.setNoDataText("暂无相关数据");
        bc.clear();
    } else {
        if (combinedData == null) {
            combinedData = new CombinedData();
        }
        xValues.clear();
        List<CandleEntry> candleValues = candleSet.getValues();
        candleValues.clear();
        List<Entry> ma5Values = lineset5.getValues();
        ma5Values.clear();
        List<Entry> ma10Values = lineset10.getValues();
        ma10Values.clear();
        List<Entry> minValues = linesetMin.getValues();
        minValues.clear();
        List<BarEntry> barValues = barSet.getValues();
        barValues.clear();
        for (int i = 0; i < dataList.size(); i++) {
            List<String> k = dataList.get(i);
            Date d = new Date(Long.parseLong(k.get(6)) * 1000);//毫秒
            String x = sdf.format(d);//显示日期
            if (xValues.containsValue(x)) {//x重复
                dataList.remove(i);
                i--;
            } else {
                xValues.put(i,x);
                float open = Float.parseFloat(k.get(4));
                float close = Float.parseFloat(k.get(1));
                candleValues.add(new CandleEntry(i,Float.parseFloat(k.get(2)),Float.parseFloat(k.get(3)),open,close,x));
                minValues.add(new Entry(i,x));
                barValues.add(new BarEntry(i,Float.parseFloat(k.get(8)),close >= open ? 0 : 1));
                if (i >=4) {
                    ma5Values.add(new Entry(i,getMA(i,5)));
                    if (i >= 9) {
                        ma10Values.add(new Entry(i,10)));
                    }
                }
            }
        }
        candleSet.setValues(candleValues);
        lineset5.setValues(ma5Values);
        lineset10.setValues(ma10Values);
        linesetMin.setValues(minValues);
        if (tabLayout.getSelectedTabPosition() == 0) {
            combinedData.removeDataSet(candleSet);//分时图时移除蜡烛图
            combinedData.setData(new LineData(linesetMin));
        } else {
            combinedData.setData(new CandleData(candleSet));
            combinedData.setData(new LineData(lineset5,lineset10));
        }

        cc.setData(combinedData);
        float xMax = xValues.size() - 0.5F;//认X轴最大值是 xValues.size() - 1
        cc.getXAxis().setAxisMaximum(xMax);//使最后一个显示完整

        barSet.setValues(barValues);
        BarData barData = new BarData(barSet);
        barData.setBarWidth(1 - candleSet.getBarSpace() * 2);//使Candle和Bar宽度一致
        bc.setData(barData);
        bc.getXAxis().setAxisMaximum(xMax + barOffset);//保持边缘对齐

        cc.setVisiblexrange(range,range);//设置显示X轴个数的上下限,竖屏固定52个
        bc.setVisiblexrange(range,range);
    }
}

private void dismissLoading() {
    if (loadingDialog != null) {
        loadingDialog.dismiss();
    }
}

@Override
public void onFailure(String url,String error) {
    dismissLoading();
    cc.setNoDataText("加载失败 点击标签重试");
    cc.invalidate();
    bc.setNoDataText("加载失败");
    bc.invalidate();
}

private float getMA(int index,int maxCount) {
    int count = 1;
    float sum = Float.parseFloat(dataList.get(index).get(1));
    while (count < maxCount) {
        if (--index < 0) {
            break;
        }
        sum += Float.parseFloat(dataList.get(index).get(1));
        count++;
    }
    return sum / count;
}

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.iv_klBack://后退键
            onBackpressed();
            break;
        case R.id.iv_klOrientation://切换横竖屏
            highVisX = cc.getHighestVisibleX();
            setRequestedOrientation(isPort() ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE :
                    ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            break;
    }
}

/**
 * 滑动到边缘后加载更多
 */
@Override
public void edgeLoad(float x,boolean left) {
    int v = (int) x;
    if (!left && !xValues.containsKey(v) && xValues.containsKey(v - 1)) {
        v = v - 1;
    }
    String time = xValues.get(v);
    if (!TextUtils.isEmpty(time)) {
        try {
            long t = sdf.parse(time).getTime();
            if (!left) {//向右获取数据时判断时间间隔
                long interval = KL_INTERVAL[tabLayout.getSelectedTabPosition()] * M1;
                if (System.currentTimeMillis() - t < interval) {//不会有新数据
                    return;
                }
            }
            loadingDialog = LoadingDialog.newInstance();
            loadingDialog.show(this);
            toLeft = left;
            getData(t * 1000000L + "");
        } catch (ParseException e) {
            e.printstacktrace();
        }
    }
}

/**
 * 双击图表
 */
private void doubleTapped() {
    if (isPort()) {
        highVisX = cc.getHighestVisibleX();
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    }
}

@Override
public void valueSelected(Entry e) {
    float x = e.getX();
    clHl.setVisibility(View.VISIBLE);
    CandleEntry candle = candleSet.getEntryForXValue(x,0);
    if (candle != null) {
        tvOpen.setText(format4p.format(candle.getopen()));
        tvOpen.setSelected(candle.getopen() < candle.getClose());
        tvClose.setText(format4p.format(candle.getClose()));
        tvClose.setSelected(candle.getopen() >= candle.getClose());

        tvHigh.setText(format4p.format(candle.getHigh()));
        tvHigh.setSelected(false);
        tvLow.setText(format4p.format(candle.getLow()));
        tvLow.setSelected(true);
    }
    BarEntry bar = barSet.getEntryForXValue(x,0);
    if (bar != null) {
        tvVol.setText(format4p.format(bar.getY()));
    }

    if (tabLayout.getSelectedTabPosition() != 0) {
        Entry line5 = lineset5.getEntryForXValue(x,0);
        Entry line10 = lineset10.getEntryForXValue(x,0);
        if (line5 != null && line10 != null) {
            tvLine.setVisibility(View.VISIBLE);
            String line = String.format(Kline5_10,format4p.format(line5.getY()),format4p.format(line10.getY()));
            tvLine.setText(fromHtml(line));
        }
    }
}

@Override
public void nothingSelected() {
    clHl.setVisibility(View.GONE);
    tvLine.setVisibility(View.GONE);
}

@Override
public void enableHighlight() {
    if (!barSet.isHighlightEnabled()) {
        candleSet.setHighlightEnabled(true);
        linesetMin.setHighlightEnabled(true);
        barSet.setHighlightEnabled(true);
    }
}

@Override
public void disableHighlight() {
    if (barSet.isHighlightEnabled()) {
        candleSet.setHighlightEnabled(false);
        linesetMin.setHighlightEnabled(false);
        barSet.setHighlightEnabled(false);
        if (ccGesture != null) {
            ccGesture.setHighlight(true);
        }
        if (bcGesture != null) {
            bcGesture.setHighlight(true);
        }
    }
}

@Override
public void onBackpressed() {
    if (isPort()) {
        super.onBackpressed();
    } else {
        highVisX = cc.getHighestVisibleX();
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    }
}

@Override
protected void onRestart() {
    super.onRestart();
    float rightX = cc.getHighestVisibleX();
    if (rightX == cc.getXChartMax()) {//停留在最右端
        edgeLoad(rightX,false);
    }
}

/**
 * 横竖屏切换
 */
@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    setContentView(R.layout.activity_main);
    initView();
    range = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT ? 52 : 86;//竖屏显示52个 横屏显示86个
    configData();
    if (xValues.size() > 0) {
        cc.post(new Runnable() {
            @Override
            public void run() {
                float x = highVisX - range;
                cc.moveViewToX(x);
                bc.moveViewToX(x + barOffset);
                cc.notifyDataSetChanged();
                bc.notifyDataSetChanged();
            }
        });
    }
}

/**
 * 当前是否是竖屏
 */
public boolean isPort() {
    return getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
}


public int getColorById(int colorId) {
    return ContextCompat.getColor(this,colorId);
}

public int sp2px(float spValue) {
    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,spValue,getResources().getdisplayMetrics());
}

public static Spanned fromHtml(String source) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        return Html.fromHtml(source,Html.FROM_HTML_MODE_LEGACY);
    } else {
        return Html.fromHtml(source);
    }
}

}

嗯,肯定会从这些代码助手中获得概念并检测可以在何处实施解决方案。

如果你需要看我有的其他课程,请告知。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)