Blog

Back to Blog

Getting Started with ShinobiCharts for Android™ – A Photo Histogrammer App

Posted on 21 Jan 2013 Written by Patrick Armstrong

In the run up to Christmas we released our ShinobiCharts for Android Community Technology Preview (CTP). The sample apps included with the download were intentionally kept very simple but we thought now would be a good time to put together a slightly more involved example. In this post I’ll create a simple app that analyses the RGB components of a picture taken with your Android device’s camera. And you’ll see just how easy it is to integrate our charts into your app!

What do I need?

You’ll need a copy of the ShinobiCharts for Android CTP. Included in the download bundle is a README and a Getting Started guide which details everything you need to start developing on Android and walks you through importing the ShinobiCharts library. We highly recommend using the Eclipse IDE with the Android Developer Tools (ADT) plugin. That’s what I’ll be using for this blog post.

Ideally you should have an Android device, with a camera, running Android 3.0 (or above) to try the app out. You can use the Android Emulator but the app makes a lot more sense hooked up to a real camera!

Finally, you should have a basic knowledge of Android app development. While I’ve kept things simple you should be familiar with concepts like Activities and Fragments. There’s lots of great information on the Android Developer site.

App Outline

Our app will allow the user to take a photo with their device. Once taken, the image will be analyzed to create an RGB Histogram which will be shown on screen. We’ll also let the user show or hide the red, green and blue lines in the chart.

AppOutline

We’ll structure the app as two distinct Activities to correspond to the two different screens – the CameraActivity and the AnalysisActivity. We’ll also break the AnalysisActivity down into two Fragments – the HistogramFragment where the chart will sit and the SettingsFragment where the controls will be.

Creating a New Project

Nice design Pat… now show me the code!

Ok, ok, but first we need to set up our project. With the ADT plugin installed, creating a new Android Application Project results in some very helpful boiler plate code being produced. A wizard guides you through some general project settings – remember the minimum SDK supported by ShinobiCharts for Android is API 11: Android 3.0 so our app won’t support anything lower than this. I’ve got a Nexus 7 running Android 4.2 so I set the Target and Compile with SDKs to API 17.

The wizard can create a blank Activity for you to get you started. We want the CameraActivity to display first when the app is launched so rename MainActivity to CameraActivity (and let Eclipse do the work!).

We also have to reference the ShinobiCharts for Android library project in order to start using it (see the Getting Started guide in the CTP download bundle).

Taking a Photo: CameraActivity

CameraActivity

Office Desk Still Life… the Nexus 7’s front-facing camera gives you a mirror image preview by default!

So now our project’s all set, let’s code! The first thing we need to do is get an image from the camera. One option would be to use an existing camera app on the device but instead we’re going to make our own, basic camera app.

The Android Developer site has a couple of great articles (here and here) on this very topic so I won’t regurgitate them here. Our CameraActivity is heavily based on the Building a Camera App section with a few tweaks and modifications to suit our needs. To keep things simple I decided to take out anything to do with recording videos.

The basic idea is to grab hold of the device’s camera, create a CameraPreview (so you can see what you’re taking) and plonk it in a suitable element in our UI. On top of that we place a button to allow the user to take a photo.

Once the photo has been taken it’s stored as a .jpg on the device so it can be used by another Activity (i.e. the AnalysisActivity) or even another app.

Like I said I won’t go into detail but there are a few things I should point out:

  • Remember to declare the permissions and features required by the app in the AndroidManifest.xml file
  • For simplicity I’ve kept the CameraActivity locked in the landscape position
  • I’ve not bothered with the Action Bar (or the auto-generated onCreateOptionsMenu())
  • I’ve supplied a cameraId of 0 to the Camera.open() method – without it the first back-facing camera on the device is opened… the Nexus 7 only has a front-facing camera so you’d get back null!
  • Using a FrameLayout instead of a LinearLayout for the outer container in the XML layout means the capture Button gets placed on top of the live camera preview images
  • Pressing the Back button from the AnalysisActivity should let the user take another photo – so I moved the code for getting the camera instance and creating the CameraPreview from onCreate() to onResume()
  • For simplicity I didn’t bother creating an output media file Uri for the photos that are taken – I stuck to a File and used its absolute path, as you’ll see later
  • Photos get saved to a directory called PhotoHistogrammerApp on the device’s external storage public directory so you know where to delete all the silly photos you take!
  • Finally, I overrode onStop() to handle removing the CameraPreview when the user navigates away from the CameraActivity –doing it here instead of the onPause() method avoids a white flash as the preview is removed
    @Override
    protected void onStop() {
        super.onStop();

        // Remove the CameraPreview from its holder in our layout
        FrameLayout previewHolder = (FrameLayout) findViewById(R.id.camera_preview);
        previewHolder.removeView(preview);
        preview = null;
    }

Analyzing the Image: AnalysisActivity

We’ve captured an image, hurray! Now we need to analyze it and draw a pretty chart.

The ADT plugin once again comes to the rescue by letting us create a new Android Activity with a load of boiler plate code already added. Note that the AndroidManifest.xml file gets updated to declare that our app makes use of the AnalysisActivity.

AnalysisActivityFragments

The AnalysisActivity contains two fragments, a SettingsFragment and a HistogramFragment, which sit side-by-side. We can statically declare these fragments in the AnalysisActivity’s XML layout like so.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    tools:context=".AnalysisActivity" >

    <!-- This fragment allows us to interact with the Chart -->
    <fragment
        android:id="@+id/settings"
        android:name="com.shinobicontrols.sample.photohistogrammer.SettingsFragment"
        android:layout_width="0dip"
        android:layout_height="match_parent"
        android:layout_weight="1" />

    <!-- This fragment contains the actual Chart which displays the RGB information -->
    <fragment
        android:id="@+id/histogram"
        android:name="com.shinobicontrols.sample.photohistogrammer.HistogramFragment"
        android:layout_width="0dip"
        android:layout_height="match_parent"
        android:layout_weight="3" />

</LinearLayout>

In the AnalysisActivity.java file we declare a public String constant called EXTRA_PICTURE_FILE. This is just a name to identify an extra bit of information (the absolute path to the picture file) that will be added to the Intent that starts this Activity, via the putExtra() method. We’ll see it used later on.

For the moment the only other code in the AnalysisActivity is the auto-generated onCreate() method that sets its View as the inflated layout from the XML layout file.

Controlling the App: SettingsFragment

With this structure in place we can really get going!

Let’s put together a simple control panel to let the user change some settings in the app. Unfortunately the ADT plugin doesn’t have a Create New Fragment wizard so you have to create the layout and code files yourself (but it’s not hard!).

SettingsFragment

Layout XML

The layout of the SettingsFragment is just a number of UI widgets arranged vertically.

<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="@color/sc_blackColor">

    <ImageView
        android:id="@+id/picture_holder"
        android:layout_width="wrap_content"
        android:layout_height="0dip"
        android:layout_weight="1"
        android:paddingLeft="5dip"
        android:paddingRight="5dip"
        android:background="@color/sc_blackColor"
        android:contentDescription="@string/imageDescription" />
    
    <TextView android:id="@+id/redLabel"
        android:text="@string/redToggleButtonLabelText"
        style="@style/ToggleButtonLabelStyle" />
    
    <ToggleButton android:id="@+id/redButton"
        style="@style/ToggleButtonStyle" />
    
    <TextView android:id="@+id/greenLabel"
        android:text="@string/greenToggleButtonLabelText"
        style="@style/ToggleButtonLabelStyle" />
    
    <ToggleButton android:id="@+id/greenButton"
        style="@style/ToggleButtonStyle" />
    
    <TextView android:id="@+id/blueLabel"
        android:text="@string/blueToggleButtonLabelText"
        style="@style/ToggleButtonLabelStyle" />
    
    <ToggleButton android:id="@+id/blueButton"
        style="@style/ToggleButtonStyle" />
    
    <Button android:id="@+id/discard_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/discard_button_text" />

</LinearLayout>

The ImageView at the top is where we’ll put the photo that’s just been taken so we can see what’s being analyzed. Below that are three ToggleButtons, to switch the lines on and off in the chart, with a TextView above each to act as a label. Finally, there’s a Button to discard the photo that’s been analyzed and take the user back to the CameraActivity (pressing the Back button will just return to the CameraActivity without deleting the photo).

We declare strings and custom colors in the corresponding resource files in the values folder and refer out to them here. We can also use colors defined in the themes supplied with ShinobiControls for Android. Note all the ShinobiControls attributes have been prefixed with sc_ to help avoid clashes with your own code.

Java Code

The SettingsFragment class extends android.app.Fragment. We override the onCreateView() method to inflate the XML layout we just specified. Unlike the other lifecycle methods in the Fragment class you don’t need to call the super class’ implementation of this method.

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the XML layout
        View settingsFragmentView = inflater.inflate(R.layout.fragment_settings, container, false);

        // ...

        return settingsFragmentView;
    }

By giving the UI widgets an id in the XML layout, finding them in code is easy! Here’s how we set an OnCheckChangeListener to the red line’s ToggleButton.

        ToggleButton redToggleButton = (ToggleButton) settingsFragmentView
                .findViewById(R.id.redButton);
        redToggleButton.setOnCheckedChangeListener(new OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                // we fill this in shortly
            }
        });

For the discard Button we want to set an OnClickListener in a similar way. When the Button is clicked we want to find the photo file and try to delete it. Then we want to take the user back to where they came from.

We said earlier we’d use the String constant EXTRA_PICTURE_FILE to get the absolute path to the picture file from the Intent that started the AnalysisActivity. Well, here’s how.

    private String getPictureFilePathFromIntent() {
        // Get the picture file path string from the Activity's Intent
        return getActivity().getIntent().getStringExtra(AnalysisActivity.EXTRA_PICTURE_FILE);
    }

So that means our OnClickListener’s onClick() method looks like:

            @Override
            public void onClick(View v) {
                // Delete the picture file
                String pictureFilePath = getPictureFilePathFromIntent();
                File pictureFile = new File(pictureFilePath);
                if (!pictureFile.delete()) {
                    Log.d(SettingsFragment.TAG,
                            String.format("Could not delete image file: %s", pictureFilePath));
                }

                // Finish this Activity, causing us to go back to the
                // CameraActivity
                getActivity().finish();
            }

Calling finish() on the Fragment’s host Activity is pretty much the same as pressing the back button. Here they’re sent back to the CameraActivity ready to take another photo.

Next let’s override the onStart() method to add the photo to the ImageView in our layout. Doing it here means the photo will still be displayed if the user goes off to another app and then returns.

    private Bitmap picture;

    @Override
    public void onStart() {
        super.onStart();

        // Get the picture file from the
        File pictureFile = new File(getPictureFilePathFromIntent());
        if (pictureFile.exists()) {
            // Decode the picture from the file
            picture = BitmapFactory.decodeFile(pictureFile.getAbsolutePath());

            // We'll host the picture in an ImageView
            ImageView pictureHolder = (ImageView) getActivity()
                    .findViewById(R.id.picture_holder);
            pictureHolder.setImageBitmap(picture);
        }
    }

To avoid OutOfMemoryErrors you’ll also want to override the onStop() method to reset the ImageView’s ImageBitmap to null and to recycle the Bitmap object.

    @Override
    public void onStop() {
        super.onStop();

        // To avoid OutOfMemoryErrors we set the ImageBitmap of the
        // ImageView to null...
        ImageView pictureHolder = (ImageView) getActivity().findViewById(R.id.picture_holder);
        pictureHolder.setImageBitmap(null);

        // ... and recycle the picture Bitmap when the fragment is paused
        if (picture != null) {
            picture.recycle();
        }
        picture = null;
    }

Last, but by no means least, we need a way for the SettingsFragment to tell the chart in the HistogramFragment to show or hide each line when a ToggleButton is pressed. Fragments should not communicate directly with each other; they should always go through their host Activity. So we define a callback interface on the Fragment which any host Activity should implement.

public interface OnToggleButtonCheckedChangeListener {
        void onToggleButtonCheckedChange(int seriesId, boolean isChecked);
    }

The Fragment can then have a member that holds a reference to the Activity’s implementation of the callback interface, setting this in the onAttach() method as follows.

    private OnToggleButtonCheckedChangeListener toggleButtonClickedListener;

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            toggleButtonClickedListener = (OnToggleButtonCheckedChangeListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                    + " must implement "
                    + OnToggleButtonCheckedChangeListener.class.getSimpleName());
        }
    }

Now we can call the onToggleButtonCheckedChange() method on the OnToggleButtonCheckedChangeListener when the ToggleButtons are pressed.

        redToggleButton.setOnCheckedChangeListener(new OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                toggleButtonClickedListener.onToggleButtonCheckedChange(R.id.red,
                        isChecked);
            }
        });

Once we’ve created the HistogramFragment we’ll go back and implement this interface in the AnalysisActivity.

HistogramFragment

Phew! Finally we get to set up our nifty chart.

HistogramFragment

Layout XML

The XML layout for this Fragment just contains a Chart.

<com.shinobicontrols.chart.Chart xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:shinobi="http://schemas.android.com/apk/res-auto/com.shinobicontrols"
    android:id="@+id/histogram_chart"
    style="@style/sc_Midnight"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <!-- Define the X Axis for the Chart -->
    <com.shinobicontrols.chart.axis.XAxis
        shinobi:sc_dock="sc_bottom"
        shinobi:sc_dockSize="50dip"
        shinobi:sc_numberFormat="0" >

        <com.shinobicontrols.chart.axis.MajorGridLine shinobi:sc_showLine="true" />
    </com.shinobicontrols.chart.axis.XAxis>

    <!-- Define the Y Axis for the Chart -->
    <com.shinobicontrols.chart.axis.YAxis
        shinobi:sc_dock="sc_left"
        shinobi:sc_dockSize="50dip"
        shinobi:sc_numberFormat="0" >

        <com.shinobicontrols.chart.axis.MajorGridLine shinobi:sc_showLine="true" />
    </com.shinobicontrols.chart.axis.YAxis>

    <!-- Define the Line series; giving the elements an id makes it easy to  -->
    <!-- obtain a reference to them in code. -->
    <com.shinobicontrols.chart.series.Line
        android:id="@+id/red"
        shinobi:sc_lineColour="@color/red"
        shinobi:sc_lineWidth="2"
        shinobi:sc_showFill="false"
        shinobi:sc_showLine="true" />

    <com.shinobicontrols.chart.series.Line
        android:id="@+id/green"
        shinobi:sc_lineColour="@color/green"
        shinobi:sc_lineWidth="2"
        shinobi:sc_showFill="false"
        shinobi:sc_showLine="true" />

    <com.shinobicontrols.chart.series.Line
        android:id="@+id/blue"
        shinobi:sc_lineColour="@color/blue"
        shinobi:sc_lineWidth="2"
        shinobi:sc_showFill="false"
        shinobi:sc_showLine="true" />

</com.shinobicontrols.chart.Chart>

In our Chart we’ve defined an XAxis docked along the bottom and a YAxis docked to the left. We’ve also specified that MajorGridLines should be displayed. Then, we define three Line series, one for each color component, setting their color to the custom colors we declared in our colors.xml file. Also note we’ve specified that the whole Chart takes on our Midnight style by using its style attribute.

Java Code

Once again we override the onCreateView() method and in it inflate the XML layout. From here we can get a reference to the Chart object, get the absolute path to the picture file from the Intent and set about analyzing it.

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the HistogramFragment from the XML layout file
        View histogramFragmentView = inflater
                .inflate(R.layout.fragment_histogram, container, false);

        // Get a reference to the Chart
        chart = (Chart) histogramFragmentView.findViewById(R.id.histogram_chart);

        // Get the path of the picture file from the Intent
        String pictureFilePath = getActivity().getIntent().getStringExtra(
                AnalysisActivity.EXTRA_PICTURE_FILE);

        // Create a File for the picture file
        File pictureFile = new File(pictureFilePath);

        if (pictureFile.exists()) {
            // Analyse the RGB components of the image
            int[] redValues = new int[HistogramFragment.TOTAL_COLOUR_COMPONENTS];
            int[] greenValues = new int[HistogramFragment.TOTAL_COLOUR_COMPONENTS];
            int[] blueValues = new int[HistogramFragment.TOTAL_COLOUR_COMPONENTS];
            analyseRgb(pictureFile, redValues, greenValues, blueValues);

            // Create data points from the analysed values...
            int r, g, b, max = 0;
            final ArrayList<XYDataSeriesItem> redItems = new
                    ArrayList<XYDataSeriesItem>(HistogramFragment.TOTAL_COLOUR_COMPONENTS);
            final ArrayList<XYDataSeriesItem> greenItems = new
                    ArrayList<XYDataSeriesItem>(HistogramFragment.TOTAL_COLOUR_COMPONENTS);
            final ArrayList<XYDataSeriesItem> blueItems = new
                    ArrayList<XYDataSeriesItem>(HistogramFragment.TOTAL_COLOUR_COMPONENTS);

            // ... finding the max Y value as we go
            for (int i = 0; i < HistogramFragment.TOTAL_COLOUR_COMPONENTS; i++) {
                redItems.add(new XYDataSeriesItemImpl(i, r = redValues[i]));
                greenItems.add(new XYDataSeriesItemImpl(i, g = greenValues[i]));
                blueItems.add(new XYDataSeriesItemImpl(i, b = blueValues[i]));

                if (r > max) {
                    max = r;
                }
                if (g > max) {
                    max = g;
                }
                if (b > max) {
                    max = b;
                }
            }

            // Add the data points to the chart
            addDataToChart(redItems, greenItems, blueItems);

            // Get the X and Y axes
            XAxis xAxis = chart.getAxis(Dock.BOTTOM);
            YAxis yAxis = chart.getAxis(Dock.LEFT);

            // Set the display range for the X Axis: 0 to 255
            xAxis.getDisplayRange()
                    .setMin(0)
                    .setMax(HistogramFragment.TOTAL_COLOUR_COMPONENTS - 1)
                    .notifyHasChanged();

            // Set the display range for the Y Axis: 0 to max
            yAxis.getDisplayRange()
                    .setMin(0)
                    .setMax(max)
                    .notifyHasChanged();
        }

        // Return the inflated view
        return histogramFragmentView;
    }

    private void addDataToChart(ArrayList<XYDataSeriesItem> redItems,
            ArrayList<XYDataSeriesItem> greenItems, ArrayList<XYDataSeriesItem> blueItems) {
        // Find each line series
        Line red = chart.getSeries(R.id.red);
        Line green = chart.getSeries(R.id.green);
        Line blue = chart.getSeries(R.id.blue);

        // Get the DataSeries for each line series
        XYDataSeries redDataSeries = red.getDataSeries();
        XYDataSeries greenDataSeries = green.getDataSeries();
        XYDataSeries blueDataSeries = blue.getDataSeries();

        // Add the data points to the series
        redDataSeries.addAll(redItems);
        blueDataSeries.addAll(blueItems);
        greenDataSeries.addAll(greenItems);
    }

    private void analyseRgb(File pictureFile, int[] redValues, int[] greenValues, int[] blueValues) {
        // Decode the picture file into a Bitmap
        Bitmap image = BitmapFactory.decodeFile(pictureFile.getAbsolutePath());

        int imageWidth = image.getWidth();
        int imageHeight = image.getHeight();

        if (image != null) {
            // Get an array of all the pixels in the image
            int[] pixels = new int[imageWidth * imageHeight];
            image.getPixels(pixels, 0, imageWidth, 0, 0, imageWidth, imageHeight);

            // For each pixel work out the red, green and blue components of its
            // colour and add one to the count for the component value
            for (int x = 0; x < imageWidth; x++) {
                for (int y = 0; y < imageHeight; y++) {
                    int color = pixels[y * imageWidth + x];
                    redValues[Color.red(color)]++;
                    greenValues[Color.green(color)]++;
                    blueValues[Color.blue(color)]++;
                }
            }
        }
    }

I’ll leave you to go through the RGB analysis code in your own time but essentially it’s just keeping a count of the amount of each color component present in each pixel.

The code to get this into the Chart is really quite simple! Once we got the totals for each color component, we need to create some data points that relate the amount of color (0 to 255) to the number of pixels with that amount. Here, I’ve used the simple concrete implementation, XYDataSeriesImpl, provided with the ShinobiCharts library but you could just as easily create your own implementation of the XYDataSeriesItem interface if you wanted.

We stuff these data points into ArrayLists, one for each color component. Then we get each Line Series from the chart:

        Line red = chart.getSeries(R.id.red);
        Line green = chart.getSeries(R.id.green);
        Line blue = chart.getSeries(R.id.blue);

And each of their corresponding DataSeries:

        XYDataSeries redDataSeries = red.getDataSeries();
        XYDataSeries greenDataSeries = green.getDataSeries();
        XYDataSeries blueDataSeries = blue.getDataSeries();

And add the data points to them:

        redDataSeries.addAll(redItems);
        blueDataSeries.addAll(blueItems);
        greenDataSeries.addAll(greenItems);

To set the range on each Axis do the following:

            // Get the X and Y axes
            XAxis xAxis = chart.getAxis(Dock.BOTTOM);
            YAxis yAxis = chart.getAxis(Dock.LEFT);

            // Set the display range for the X Axis: 0 to 255
            xAxis.getDisplayRange()
                    .setMin(0)
                    .setMax(HistogramFragment.TOTAL_COLOUR_COMPONENTS - 1)
                    .notifyHasChanged();

            // Set the display range for the Y Axis: 0 to max
            yAxis.getDisplayRange()
                    .setMin(0)
                    .setMax(max)
                    .notifyHasChanged();

(alternatively you can use the addAll() method that lets you specify a min and max)

As you can see it’s very straightforward and you get a lovely looking chart that responds to all the usual touch gestures out of the box!

There are two other methods in this Fragment that let the host Activity tell it to show or hide a particular line series.

    public void hideLineSeries(int lineSeriesId) {
        // Find the line series
        Line lineSeries = chart.getSeries(lineSeriesId);

        // Make the line disappear; don't forget to call notifyHasChanged() to
        // make the changes have an effect
        lineSeries.getSettings()
                .setShowLine(false)
                .notifyHasChanged();
    }

    public void showLineSeries(int lineSeriesId) {
        // Find the line series
        Line series = chart.getSeries(lineSeriesId);

        // Make the line appear; don't forget to call notifyHasChanged() to
        // make the changes have an effect
        series.getSettings()
                .setShowLine(true)
                .notifyHasChanged();
    }

Simply grab the Line series’ Settings objects, adjust some values and call notifyHasChanged(). You can chain a number of settings changes together so they are all done at once but don’t forget to call notifyHasChanged() to make them take effect.

Hooking It All Up

We’re nearly there! All that’s left to do is to make the CameraActivity to start the AnalysisActivity when a picture’s been taken and to get the AnalysisActivity to pass on the message when the ToggleButtons are pressed.

For the first job, we create an Intent in the PictureCallback after we have saved the photo file.

            // Create an Intent to explicitly start the AnalysisActivity
            Intent analyseIntent = new Intent(CameraActivity.this, AnalysisActivity.class);

            // Include the path of the picture file in the Intent's extras
            analyseIntent.putExtra(AnalysisActivity.EXTRA_PICTURE_FILE,
                    pictureFile.getAbsolutePath());

            // Start the AnalysisActivity
            startActivity(analyseIntent);

Here we’re explicitly telling it to start the AnalysisActivity, adding the absolute path to the photo file to the Intent’s extras.

For the last job we just need the AnalysisActivity to implement the callback interface defined in the SettingsFragment:

public class AnalysisActivity extends Activity implements OnToggleButtonCheckedChangeListener {

    @Override
    public void onToggleButtonCheckedChange(int lineSeriesId, boolean isChecked) {
        // Get a reference to the HistogramFragment which contains the chart
        HistogramFragment histogramFragment =
                (HistogramFragment) getFragmentManager().findFragmentById(R.id.histogram);

        // Depending on the state of the toggle button, tell the
        // HistogramFragment to show or hide the line
        // series
        if (isChecked) {
            histogramFragment.showLineSeries(lineSeriesId);
        }
        else {
            histogramFragment.hideLineSeries(lineSeriesId);
        }
    }

    ...
}

Depending on whether the ToggleButton is checked or unchecked we tell the HistogramFragment to show or hide the particular Line series.

You should now be able to run the app on your Android device. Be sure to have a good play with the chart, pinching and panning to your heart’s content!

AnalysisActivity

Feel free to download the full source code for this demo and if you have any questions or comments please do get in touch using the form below.

Thanks for reading!

Back to Blog