Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

My Android AChartEngine is already working, but how to make it look good?

I guess the title gives away most of my questions, but let's detail and give a bit of background:

I have an Android app focused mainly for tablets that will be displaying a few different real-time data in TimeCharts. So I already have a service to communicate with the data source that grabs the data, parse it and add the values to singletons TimeSeries. When the user navigates in and out of fragments the fragment simply adds the correct TimeSeries and keeps calling mChartView.repaint(); every 500ms

All that is operational and working nicely across tabs, rotation, notification, etc.

Because it's a tablet app I want to achieve two things: 1) maximize viewing area; 2) make it look good.

1) I already removed the zoom buttons (for some reason if I try to add them it gives me NullException) with mRenderer.setZoomButtonsVisible(false); but there's still a huge bottom margin taking a lot of screen space. I tried setting a negative border, and it works, but the labels don't move with this border and I end up with the X axis crossing over the labels or even passing them. How can I move the labels down with the border, or maybe put them on the top of the graph?

2) a) the app is user configurable to use either Holo_Dark or Holo_light. So I'm using a transparent background on the renderer (see snippet below) to show that nice background shading that holo does. How to a change the axis text color? I found the mRenderer.setAxesColor(color) but it only change the line, not the text.

    int color = Color.argb(0, 255, 255, 255); // Transparent colour
    mRenderer.setBackgroundColor(color);
    mRenderer.setMarginsColor(color);

b) the Y axis is crossing on top of it's labels, how can I offset them a bit to the left so the text is readable? (it's not with setMargins())

c) when the user zoom and pan around the chart, the chart stops updating/re-zooming the latest values on the screen and the user have to keep panning to see the latest values. I added a 'Reset Zoom' button on the ActionBar, but I can't make it work. What would be the code to make the values update again.

d) if my TimeSeires have a range of 10min (2 values per second) how can I make it only show the last 1min (for example) and if the user want's previous values he can pan back? (and the use button on c) re-start)

at the moment my rendered is setup as:

    int color = Color.argb(0, 255, 255, 255); // Transparent colour
    mRenderer.setBackgroundColor(color);
    mRenderer.setMarginsColor(color);
    mRenderer.setAxisTitleTextSize(16); // 16
    mRenderer.setChartTitleTextSize(20); // 20
    mRenderer.setLabelsTextSize(15); // 15
    mRenderer.setLegendTextSize(15); // 15
    mRenderer.setPointSize(0); // 10
    mRenderer.setMargins(new int[] { 20, 30, 15, 0 });      
    mRenderer.setZoomButtonsVisible(false);
    mRenderer.setShowAxes(true);
    mRenderer.setShowLegend(true);
    mRenderer.setShowGridX(true);
    mRenderer.setShowLabels(true);

and each rendered added to the Multiple renderer goes like:

    renderer.setDisplayChartValues(false);
    mRenderer.addSeriesRenderer(renderer);

thanks a lot for all help!

Editing just to include my final code for others to see:

I've ended up ditching the legend completely and implementing my own legend floating above the chart, so the layout is a FrameLayout with a ChartView and a TableLayout.

I implemented an abstract CurvesFragment Class that is extended by a few classes.

I configure the chart during my fragment OnCreateView as:

    mChartView = ChartFactory.getTimeChartView(getActivity().getApplicationContext(), mDataset, mRenderer, "HH:mm:ss"); // X axis in a time scale
    // Setup chart renderer =============================
    mRenderer.setLabelsTextSize(15); // Text size
    mRenderer.setMargins(new int[] { 0, Utils.convertToPx(5, getActivity().getApplicationContext()), 0, 0 }); // 5dp on the left to show that little line
    mRenderer.setZoomButtonsVisible(false); // bye bye zoom
    mRenderer.setShowAxes(true); // show both axes
    mRenderer.setShowLegend(false); // bye bye legend
    mRenderer.setShowGridX(true); // X grid helps identify values
    mRenderer.setShowLabels(true); // See the values
    mRenderer.setYLabelsAlign(Align.LEFT); // put the Y labels on the left of the axis
    if (Utils.getCurrentTheme(getActivity().getApplicationContext()) == android.R.style.Theme_Holo) {
        mRenderer.setXLabelsColor(getResources().getColor(android.R.color.primary_text_dark));
        mRenderer.setYLabelsColor(0, getResources().getColor(android.R.color.primary_text_dark));
        mRenderer.setMarginsColor(getResources().getColor(android.R.color.background_dark));
        legend.setBackgroundResource(R.drawable.border_dark);
    } else {
        // same as above but for Holo_light
    }
  // And from here proceed to add the TimeCharts with using
  renderer.setDisplayChartValues(false);

On my action bar I included buttons for Pause/Resume and ResetZoom. the Reset Zoom is simple:

        mRenderer.setXAxisMax(-Double.MAX_VALUE);
        mRenderer.setXAxisMin(Double.MAX_VALUE);
        mRenderer.setYAxisMax(-Double.MAX_VALUE);
        mRenderer.setYAxisMin(Double.MAX_VALUE);
        mChartView.repaint();

There's a background service that holds references to the TimeSeries that is always reading fresh data from the WiFi and adding them to the series. That way, in the fragment there's a runnable executing every 700ms that calls repaint() as:

// Chart rendering control ========================
private Runnable updateDataSet = new Runnable() {

    public void run() {
        if (!chartPaused) {

            double max = mRenderer.getXAxisMax(); // get renderer maximum value
            double min = mRenderer.getXAxisMin(); // get renderer minimum value

            // Check if the user scrolled/zoomed/panned the graph or if it's on 'auto'
            if (!((max == Double.MAX_VALUE || max == -Double.MAX_VALUE) && (min == Double.MAX_VALUE || min == -Double.MAX_VALUE))) {

                double newMax = mDataset.getSeriesAt(0).getMaxX(); // new max is the latest value from our series
                double newMin = newMax - Math.abs(max - min); // new min value is the X range the user zoomed into
                mRenderer.setXAxisMax(newMax); // set the new values
                mRenderer.setXAxisMin(newMin);
            }

            // Logic that updates the TableLayout with the legend is placed here

            mChartView.repaint();
        }

        getView().postDelayed(updateDataSet, 700);
    }
};

and there's a ZoomListener and a PanListener that pauses the chart every time the user touches it. That way the user can zoom and pan around, but whenever it resumes the chart updating it will only scroll forward to the earliest data without changing the zoom level.

I guess the final result is pretty solid and looks nice on both Holo_Light and Holo_Dark.

Thanks a LOT for Dan for the answers and for all other devs that created the engine.

happy coding!

like image 436
Budius Avatar asked Jul 03 '12 10:07

Budius


1 Answers

Question 1. You can try to play with these API calls:

mRenderer.setShowLegend(false);

or

mRenderer.setMargins(new int[] {top, left, bottom, right});

Maybe decrease the bottom value?

Question 2. a)

mRenderer.setXLabelsColor();
mRenderer.setYLabelsColor();

b)

mRenderer.setXLabelsAlign();

c)

I think you can do the following, when the series get new values:

mRenderer.setXAxisMin();
mRenderer.setXAxisMax();

Provide the x min and x max values as the range to be displayed and call mChartView.repaint(); after that.

d) Same as for c)

As a suggestion, please try to split such long questions in several smaller ones and you may get answers faster. There aren't many ACE users to know how to answer all questions. Even the author of this library has to think about some answers :)

like image 118
Dan D. Avatar answered Nov 07 '22 22:11

Dan D.