I'm using the MPAndroidChart library in my app to render a line chart, and am facing an issue while adding more data dynamically to the chart. I tried going through the existing StackOverflow questions and also the GitHub issues for this library, but couldn't find a fix for my issue.
I'm using the LineChart API. In my use case i need to first show 15 data points on the chart, and once the user scrolls to the boundary, I'm fetching 15 more data points from the backend and resetting the data object with the set of 30 data points.
My logic for checking if the user reached the boundary is:
#Override
onChartTranslate {
.
.
if (!isLoading) {
if (lineChart.getHighestVisibleX() == lineChart.getXChartMax()) {
isLoading = true;
fetchMoreData();
}
}
.
.
}
Below method fetches data from the backend, and then updates the chart
fetchMoreData {
dataObservable.subscribe(newData -> updateUI(newData))
}
the update chart method with additional data
updateUI(newData) {
//from the data received, create new dataset,
//create a new lineData Object, and add this dataSet
//set the lineData object on the chart using
lineChart.setData(newLineDataSet)
//view port updates which consist of below
lineChart.setVisibleXRangeMinimum(4);
lineChart.setVisibleXRangeMaximum(4);
lineChart.setAutoScaleMinMaxEnabled(true);
lineChart.moveViewToX(previousBoundary)
lineChart.highlightValue(previouslyHighlightedValue)
isLoading = false
}
Things work fine for me, when i scroll slowly beyond the boundary, i.e., backend call to fetch additional data is fired and I get the new 15 data points, and I replace the existing lineData object with the new lineData object which has (old + new) data points.
I'm facing an issue if I scroll too quick, that the fetchMoreData is getting called multiple times (2/3 times), and the chart draws more data.
On debugging this I noticed that the lineChart.getHighestVisibleX() method is returning an incorrect value sometimes causing this to happen.
For e.g., the first call is triggered when the highestVisibleX equals the maxX value of 14. This updates the chart with new data points (now 30), and during redrawing of the chart, the onTranslate method gets called and the highestVisibleX somehow returns 29 (same value as maxX for the new data) even though the highest visible x-value is 14.
Could someone please help me with this issue?
Related
I'm making an API call getData(forPage: Int): Response which returns a page-worth of data (10 items max) and thereIsMoreData: Boolean.
The recyclerView is implemented that by scrolling, the scroll listener automatically fetches more data using that API call:
val scrollListener = object : MyScrollListener() {
override fun loadMoreItems() {
apiFunctionForLoading(currentPage + 1)
}
}
The problem is that with longer screen devices that have more space for items (let's say 20), the RV receives 10 items and then doesn't allow scrolling, because there's no more items to scroll to. Without scrolling, more data cannot be loaded.
My naive solution:
load first set of data
if thereIsMoreData == true I load another page of data
now I have more data than the screen can display at once hence allowing scroll
Is there a more ellegant solution?
Android has this Paging Library now which is about displaying chunks of data and fetching more when needed. I haven't used it and it looks like it might be a bit of work, but maybe it's worth a look?
Codepath has a tutorial on using it and I think their stuff is pretty good and easy to follow, so maybe check that out too. They also have this older tutorial that's closer to what you're doing (handling it yourself) so there's that too.
I guess in general, you'd want your adapter to return an "infinite" number for getItemCount() (like Integer.MAX_VALUE). And then in your onBindViewHolder(holder, position) method you'd either set the item at position, or if you don't have that item yet you load in the next page until you get it.
That way your initial page will always have the right amount of content, because it will be full of ViewHolders that have asked for data - if there's more than 10, then item 11 will have triggered the API call. But actually handling the callback and all the updating is the tricky part! If you have that working already then great, but it's what the Paging library was built to take care of for you (or at least make it easier!)
An elegant way would be to check whether the view can actually scroll down:
recyclerView.canScrollVertically(1)
1 means downwards -> returns true if it is possible tro scroll down.
So if it returns false, your page is not fully filled yet.
I want to display live graphs of incoming bluetooth sensor data in a list of cards.
I have tried using the AppendData() function for each series that holds data for the the various sensors. The series are stored in a list.
I first initialize the list of series with some series containing dummy data:
private List<PointsGraphSeries<DataPoint>> dataSeriesList = Arrays.asList(initSeries, initSeries, initSeries, initSeries, initSeries, initSeries);
Then, in onBindViewHolder() I update each of the series with the new reading:
public void onBindViewHolder(#NonNull ProductCardViewHolder holder, int position) {
....
dataSeriesList.get(position).appendData(newReading, true, 40);
....
//I set the series to the GraphView just once, like so:
if (holder.graph.getSeries().size() == 0)
holder.graph.addSeries(dataSeriesList.get(position));
...
}
I would expect that I would have 6 different graphs that each display the values for one sensor, since I am appending to a different series in the list for each position.
Instead, what I get are 6 identical graphs that display all 6 of the different sensor values for each point on the x-axis.
I have confirmed using logcat that each graph is only displaying one series, this means that each series somehow contains all of the data. I don't understand why, since I am clearly appending my readings to 6 seperate series.
I have a node in Firebase getting continually updated with information from a logfile. The node is lines/ and each child of lines/ is from a post() so it has a unique ID.
When a client first loads, I want to be able to grab the last X number of entries. I expect I'll do this with once(). From then on, however, I want to use an on() with child_added so that I get all new data. However, child_added gets all data stored in the Firebase and, after the initial setup, only want the new stuff.
I see that I can add a limitToLast() on the on(), but, if I say limitToLast(1) and a flood of entries come in, will my app still get all the new entries? Is there some other way to do this?
You need to include a timestamp property and run a query.
// Get the current timestamp
var now = new Date().getTime();
// Create a query that orders by the timestamp
var query = ref.orderByChild('timestamp').startAt(now);
// Listen for the new children added from that point in time
query.on('child_added', function (snap) {
console.log(snap.val()
});
// When you add this new item it will fire off the query above
ref.push({
title: "hello",
timestamp: Firebase.ServerValue.TIMESTAMP
});
The Firebase SDK has methods for ordering, orderByChild() and methods for creating a range startAt(). When you combine the two you can limit what comes back from Firebase.
I think there is a problem in #David East's solution. He is using the local timestamp which may cause problem if the time is not accurate in client device. Here is my suggested solution (iOS Swift):
Using observeSingleEvent to get the complete data set
Then returned it in reversed order by reversed()
Get the last timestamp by for example data[0].timestamp
Using queryStarting for timestamp
self._dbref.queryOrdered(byChild: "timestamp").queryStarting(atValue: timestamp+1)
.observe(.childAdded, with: {
snapshot in
print(snapshot.value)
})
You have the right idea. child_added should be called only for the new nodes. Without source code it's hard to tell why you get all the data in your child_added event.
You can check the chat demo app to see how they load new chat messages. The use case sounds similar.
https://github.com/firebase/firechat/blob/master/src/js/firechat.js#L347
Here's temporary but quick solution:
// define a boolean
var bool = false;
// fetch the last child nodes from firebase database
ref.limitToLast(1).on("child_added", function(snap) {
if (bool) {
// all the existing child nodes are restricted to enter this area
doSomething(snap.val())
} else {
// set the boolean true to doSomething with newly added child nodes
bool = true;
}
});
Disadvantage: It will load all the child nodes.
Advantage: It will not process existing child nodes but just the newly added child nodes.
limitToLast(1) will do the work.
I have a group of objects within a game that is a sensor(let's call them collectively object1). When it collides with my main object (object2), the score increments by 10. However, after each subsequent restart of the scene(not the whole game), each collision between object1 and object2 duplicates itself, then triplicates and so forth.
So for the first running of the scene, object1 collides with object2 once(I know this because I have a print statement every time the 2 objects collide). The second time it collides twice, the third time three times and so forth. I presume I am not removing a certain feature of the sensor but I cannot figure out what it is. How do I correctly remove the objects if this is the problem?
My code for the removal of object1 on collision:
local function onCollision( self,event )
if(event.object2.name == "bonus")then--if we hit a bonus ball
event.object2:removeSelf()--removes the bonus ball object from the scene
print("bonus collided")
display.remove(event.object2)
game.addToScore(10)--adds a value of 10 to the score
scoreText.text = game.returnScore()
Runtime:removeEventListener("enterFrame", event.object2)
else
composer.gotoScene("restart")
Runtime:removeEventListener("touch", onObjectTouch)
end
end
I studied your code and in short this is the problem
ball.collision = onCollision
It's located under scene:show and therefore a new eventListener will be created each time that you to the scene. And it's not being removed under scene:hide.
Basically half of your code doesn't really do anything. For exampel you remove the same item twice, add Runtime listeners to objects (not functions) and you're trying to remove thise Runtime listeners. For example this tries to remove a Runtime event listener on a display object that doesn't even exist:
Runtime:removeEventListener( "collision", circle )
What you need to do is:
Create all objects and listeners under scene:create
Use local collision handling (i.e. not Runtime listeners): https://docs.coronalabs.com/daily/guide/physics/collisionDetection/index.html#local-collision-handling
Rewrite your code so that you have functions and listeners outside of the scene:create
I have implmented pagination and it display 5 records per page. Now suppose I am on page 3 and click 3'rd element then 3'rd element of page-1 is selected.
I am not able to figure out problem as I always create new list object while setting data.
I used below code
temp = new ArrayList();
this.someListAdapter = new SomeListAdapter(this, R.layout.row_facet,temp);
setListAdapter(this.someListAdapter );
Below is signature of SomeListAdapter class.
public class SomeListAdapter extends ArrayAdapter<VoNeighborhood> {
}
Please help....
There really aren't enough details here about how you do pagination with your ListView.
So I might guess you're overriding onListItemClick and using the position variable it sends you, but you then don't take into account the page you're on?
Alternatively, just don't use pagination as you have an infinite canvas to scroll your list within — I don't think I've recall seeing an Android app so far that uses pagination!