Using Flot's Bar Graph in an Android WebView with Highlighting
- by Nicholi
The issue is unhighlighting bars which are no longer selected in a bar graph plotted by flot in a WebView on Android. Got no other issues drawing the actual graphs (which look beautiful for something so simple btw). I am not extremely knowledgeable in terms of javascript and web design/development but it seems little should have been needed, if it would just work!! :( I believe I'm following the Flot API correctly, if not someone please scream and yell at me. It seems to work just fine in a non-mobile browser at least.
Hoping someone has done this before, but if not I've got the minimal necessary code to poke at your droids if inquiring minds would like to test. I've tested on two Nexus Ones (both 2.2.1), and have tried targeting with Andriod 1.5 and 2.2 SDKs (my intention is to target 1.5 if possible). I've been attempting to hack away at this for far too long on my own now.
What happens:
1. Graph loads fine with bars. All bars unhighlighted.
2. Select a bar in graph, gets highlighted fine (and a tooltip is placed).
3. Select a different bar in graph, old bar is unhighlighted, old tooltip removed, new bar highlighted and tooltip placed (still no problems).
4. Click in the vast darkness of the graph which should then unhighlight the last bar... but it doesn't.
I've tried disabling flot's autohighlight and manually doing it as well to no avail. Looking into flot itself and only getting down to drawOverlay() where the issue seems to begin... An even more disturbing bug(?) appears if the fill bar option is enabled in the graph, but I'd rather just forget about that for now. Also grabbed the latest version of flot from their svn (r290), but made no different from last public release (v0.6). As a complete guess I'm thinking it's an issue with WebKit's javascript implementation (or something specific to Nexus Ones, which wouldn't be so bad), but if there is any ugly hack to just get it to work I'm all ears.
I've thrown the graph data directly into the html/js, rather than deal with showing all the code involved in the Java-javascript handler and callbacks.
The simple html placed in 'assets/flot/test/' with jquery.js and jquery.flot.js:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script src="jquery.js"></script>
<script src="jquery.flot.js"></script>
<script id="source" language="javascript" type="text/javascript">
var lastItem = null;
var plot = null;
$(document).ready(function () {
//window.testhandler.loadGraph();
// bind plotclick here
$("#graphHolder").bind("plotclick", function (event, pos, item) {
if (item)
{
var lastPoint = null;
if (lastItem != null)
lastPoint = lastItem.datapoint;
if (!pointEquals(lastPoint, item.datapoint))
{
//if (lastItem != null)
// plot.unhighlight(lastItem.series, lastItem.datapoint);
lastItem = item;
$("#tooltip").remove();
//plot.highlight(item.series, item.datapoint);
showTooltip(item.pageX, item.pageY, item.datapoint[1]);
}
}
else if (lastItem != null)
{
plot.unhighlight(lastItem.series, lastItem.datapoint); // not unhighlighting anything
//plot.unhighlight(); // doesn't work either, supposed to unhighlight everything
lastItem = null;
$("#tooltip").remove();
}
});
GotGraph();
});
/**
* Show a tooltip above bar in graph
* @param {int} x Left coordinate of div
* @param {int} y Top coordinate of div
* @param {String} contents text to place in div
*/
function showTooltip(x, y, contents) {
$('<div id="tooltip">' + contents + '</div>').css( {
position: 'absolute',
display: 'none',
top: y,
left: x,
border: '1px solid #fdd',
padding: '2px',
'background-color': '#fee',
opacity: 0.80
}).appendTo("body").fadeIn(200);
}
/**
* Draw the graph. This is a callback which will be called by Java
*
* @param {Object} seriesData
* @param {Object} seriesOptions
*/
function GotGraph() { //seriesData, seriesOptions) {
var seriesData = [{
"bars":{"lineWidth":2,"show":true,"barWidth":86400000,"align":"center","fill":false},
"data":[[1288569600000,10],[1288656000000,5],[1288742400000,12],[1288828800000,20],[1288915200000,14],[1289001600000,3],[1289174400000,22],[1289260800000,20],[1289347200000,10],[1289433600000,5],[1289520000000,12],[1289606400000,20],[1289692800000,14],[1289779200000,35]]}];
var seriesOptions = {
"xaxis":{"twelveHourClock":false,"minTickSize":[1,"day"],"tickSize":[1,"day"],"timeformat":"%d","mode":"time"},
"yaxis":{"min":0},
"grid":{"clickable":true,"autoHighlight":true,"hoverable":false}};
plot = $.plot($("#graphHolder"), seriesData, seriesOptions);
}
function pointEquals(point1, point2) {
if (point1 != null && point2 != null &&
typeof(point1) == typeof(point2) &&
point1.length == point2.length)
{
var i;
for (i=0;i<point1.length;i++) {
if (point1[i] != point2[i]) {
return false;
}
}
return true;
}
return false;
}
</script>
</head>
<body>
<div id="graphHolder" STYLE="height:200px;width:400px"></div>
</body>
</html>
The minimal amount of code necessary in onCreate in startup activity:
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
WebView mytestView = new WebView(this);
mytestView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
setContentView(mytestView);
mytestView.setBackgroundColor(0);
mytestView.getSettings().setJavaScriptEnabled(true);
mytestView.setClickable(true);
mytestView.setFocusable(false);
mytestView.setFocusableInTouchMode(false);
mytestView.loadUrl("file:///android_asset/flot/test/stats_graph.html");
}