I saw many questions about this, and tried to solve the problem, but after one hour of googling and a lots of trial & error, I still can't fix it. I hope some of you catch the problem.
This is what I get:
java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.ComparableTimSort.mergeHi(ComparableTimSort.java:835)
at java.util.ComparableTimSort.mergeAt(ComparableTimSort.java:453)
at java.util.ComparableTimSort.mergeForceCollapse(ComparableTimSort.java:392)
at java.util.ComparableTimSort.sort(ComparableTimSort.java:191)
at java.util.ComparableTimSort.sort(ComparableTimSort.java:146)
at java.util.Arrays.sort(Arrays.java:472)
at java.util.Collections.sort(Collections.java:155)
...
And this is my comparator:
#Override
public int compareTo(Object o) {
if(this == o){
return 0;
}
CollectionItem item = (CollectionItem) o;
Card card1 = CardCache.getInstance().getCard(cardId);
Card card2 = CardCache.getInstance().getCard(item.getCardId());
if (card1.getSet() < card2.getSet()) {
return -1;
} else {
if (card1.getSet() == card2.getSet()) {
if (card1.getRarity() < card2.getRarity()) {
return 1;
} else {
if (card1.getId() == card2.getId()) {
if (cardType > item.getCardType()) {
return 1;
} else {
if (cardType == item.getCardType()) {
return 0;
}
return -1;
}
}
return -1;
}
}
return 1;
}
}
Any idea?
The exception message is actually pretty descriptive. The contract it mentions is transitivity: if A > B and B > C then for any A, B and C: A > C. I checked it with paper and pencil and your code seems to have few holes:
if (card1.getRarity() < card2.getRarity()) {
return 1;
you do not return -1 if card1.getRarity() > card2.getRarity().
if (card1.getId() == card2.getId()) {
//...
}
return -1;
You return -1 if ids aren't equal. You should return -1 or 1 depending on which id was bigger.
Take a look at this. Apart from being much more readable, I think it should actually work:
if (card1.getSet() > card2.getSet()) {
return 1;
}
if (card1.getSet() < card2.getSet()) {
return -1;
};
if (card1.getRarity() < card2.getRarity()) {
return 1;
}
if (card1.getRarity() > card2.getRarity()) {
return -1;
}
if (card1.getId() > card2.getId()) {
return 1;
}
if (card1.getId() < card2.getId()) {
return -1;
}
return cardType - item.getCardType(); //watch out for overflow!
You can use the following class to pinpoint transitivity bugs in your Comparators:
/**
* #author Gili Tzabari
*/
public final class Comparators
{
/**
* Verify that a comparator is transitive.
*
* #param <T> the type being compared
* #param comparator the comparator to test
* #param elements the elements to test against
* #throws AssertionError if the comparator is not transitive
*/
public static <T> void verifyTransitivity(Comparator<T> comparator, Collection<T> elements)
{
for (T first: elements)
{
for (T second: elements)
{
int result1 = comparator.compare(first, second);
int result2 = comparator.compare(second, first);
if (result1 != -result2)
{
// Uncomment the following line to step through the failed case
//comparator.compare(first, second);
throw new AssertionError("compare(" + first + ", " + second + ") == " + result1 +
" but swapping the parameters returns " + result2);
}
}
}
for (T first: elements)
{
for (T second: elements)
{
int firstGreaterThanSecond = comparator.compare(first, second);
if (firstGreaterThanSecond <= 0)
continue;
for (T third: elements)
{
int secondGreaterThanThird = comparator.compare(second, third);
if (secondGreaterThanThird <= 0)
continue;
int firstGreaterThanThird = comparator.compare(first, third);
if (firstGreaterThanThird <= 0)
{
// Uncomment the following line to step through the failed case
//comparator.compare(first, third);
throw new AssertionError("compare(" + first + ", " + second + ") > 0, " +
"compare(" + second + ", " + third + ") > 0, but compare(" + first + ", " + third + ") == " +
firstGreaterThanThird);
}
}
}
}
}
/**
* Prevent construction.
*/
private Comparators()
{
}
}
Simply invoke Comparators.verifyTransitivity(myComparator, myCollection) in front of the code that fails.
It also has something to do with the version of JDK.
If it does well in JDK6, maybe it will have the problem in JDK 7 described by you, because the implementation method in jdk 7 has been changed.
Look at this:
Description: The sorting algorithm used by java.util.Arrays.sort and (indirectly) by java.util.Collections.sort has been replaced. The new sort implementation may throw an IllegalArgumentException if it detects a Comparable that violates the Comparable contract. The previous implementation silently ignored such a situation. If the previous behavior is desired, you can use the new system property, java.util.Arrays.useLegacyMergeSort, to restore previous mergesort behaviour.
I don't know the exact reason. However, if you add the code before you use sort. It will be OK.
System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
Consider the following case:
First, o1.compareTo(o2) is called. card1.getSet() == card2.getSet() happens to be true and so is card1.getRarity() < card2.getRarity(), so you return 1.
Then, o2.compareTo(o1) is called. Again, card1.getSet() == card2.getSet() is true. Then, you skip to the following else, then card1.getId() == card2.getId() happens to be true, and so is cardType > item.getCardType(). You return 1 again.
From that, o1 > o2, and o2 > o1. You broke the contract.
if (card1.getRarity() < card2.getRarity()) {
return 1;
However, if card2.getRarity() is less than card1.getRarity() you might not return -1.
You similarly miss other cases. I would do this, you can change around depending on your intent:
public int compareTo(Object o) {
if(this == o){
return 0;
}
CollectionItem item = (CollectionItem) o;
Card card1 = CardCache.getInstance().getCard(cardId);
Card card2 = CardCache.getInstance().getCard(item.getCardId());
int comp=card1.getSet() - card2.getSet();
if (comp!=0){
return comp;
}
comp=card1.getRarity() - card2.getRarity();
if (comp!=0){
return comp;
}
comp=card1.getSet() - card2.getSet();
if (comp!=0){
return comp;
}
comp=card1.getId() - card2.getId();
if (comp!=0){
return comp;
}
comp=card1.getCardType() - card2.getCardType();
return comp;
}
}
I had the same symptom. For me it turned out that another thread was modifying the compared objects while the sorting was happening in a Stream. To resolve the issue, I mapped the objects to immutable temporary objects, collected the Stream to a temporary Collection and did the sorting on that.
The origin of this exception is a wrong Comparator implementation. By checking the docs, we must implement the compare(o1, o2) method as an equivalence relation by following the rules:
if a.equals(b) is true then compare(a, b) is 0
if a.compare(b) > 0 then b.compare(a) < 0 is true
if a.compare(b) > 0 and b.compare(c) > 0 then a.compare(c) > 0 is true
You may check your code to realize where your implementation is offending one or more of Comparator contract rules. If it is hard to find it by a static analysis, you can use the data which cast the exception to check the rules.
If you try to run this code you will meet the kind this exception:
public static void main(String[] args) {
Random random = new Random();
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 50000; i++) {
list.add(random.nextInt());
}
list.sort((x, y) -> {
int c = random.nextInt(3);
if (c == 0) {
return 0;
}
if (c == 1) {
return 1;
}
return -1;
});
}
Exception in thread "main" java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.TimSort.mergeLo(TimSort.java:777)
at java.util.TimSort.mergeAt(TimSort.java:514)
at java.util.TimSort.mergeCollapse(TimSort.java:441)
at java.util.TimSort.sort(TimSort.java:245)
at java.util.Arrays.sort(Arrays.java:1512)
at java.util.ArrayList.sort(ArrayList.java:1462)
at Test.main(Test.java:14)
The reason is when implementing the Comparator, it may meet the case of A > B and B > C and C > A and the sort method will be run around to be broken. Java prevent this case by throw exception this case:
class TimSort<T> {
.
.
.
else if (len1 == 0) {
throw new IllegalArgumentException(
"Comparison method violates its general contract!");
.
.
.
In conclusion, to handle this issue. You have to make sure the comparator will not meet the case of A > B and B > C and C > A.
I got the same error with a class like the following StockPickBean. Called from this code:
List<StockPickBean> beansListcatMap.getValue();
beansList.sort(StockPickBean.Comparators.VALUE);
public class StockPickBean implements Comparable<StockPickBean> {
private double value;
public double getValue() { return value; }
public void setValue(double value) { this.value = value; }
#Override
public int compareTo(StockPickBean view) {
return Comparators.VALUE.compare(this,view); //return
Comparators.SYMBOL.compare(this,view);
}
public static class Comparators {
public static Comparator<StockPickBean> VALUE = (val1, val2) ->
(int)
(val1.value - val2.value);
}
}
After getting the same error:
java.lang.IllegalArgumentException: Comparison method violates its general contract!
I changed this line:
public static Comparator<StockPickBean> VALUE = (val1, val2) -> (int)
(val1.value - val2.value);
to:
public static Comparator<StockPickBean> VALUE = (StockPickBean spb1,
StockPickBean spb2) -> Double.compare(spb2.value,spb1.value);
That fixes the error.
I ran into a similar problem where I was trying to sort a n x 2 2D array named contests which is a 2D array of simple integers. This was working for most of the times but threw a runtime error for one input:-
Arrays.sort(contests, (row1, row2) -> {
if (row1[0] < row2[0]) {
return 1;
} else return -1;
});
Error:-
Exception in thread "main" java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.base/java.util.TimSort.mergeHi(TimSort.java:903)
at java.base/java.util.TimSort.mergeAt(TimSort.java:520)
at java.base/java.util.TimSort.mergeForceCollapse(TimSort.java:461)
at java.base/java.util.TimSort.sort(TimSort.java:254)
at java.base/java.util.Arrays.sort(Arrays.java:1441)
at com.hackerrank.Solution.luckBalance(Solution.java:15)
at com.hackerrank.Solution.main(Solution.java:49)
Looking at the answers above I tried adding a condition for equals and I don't know why but it worked. Hopefully we must explicitly specify what should be returned for all cases (greater than, equals and less than):
Arrays.sort(contests, (row1, row2) -> {
if (row1[0] < row2[0]) {
return 1;
}
if(row1[0] == row2[0]) return 0;
return -1;
});
A variation of Gili's answer to check if the comparator satisfies the requirements described in the compare method's javadoc - with a focus on completeness and readability, e.g. by naming the variables the same as in the javadoc. Note that this is O(n^3), only use it when debugging, maybe just on a subset of your elements, in order to be fast enough to finish at all.
public static <T> void verifyComparator(Comparator<T> comparator, Collection<T> elements) {
for (T x : elements) {
for (T y : elements) {
for (T z : elements) {
int x_y = comparator.compare(x, y);
int y_x = comparator.compare(y, x);
int y_z = comparator.compare(y, z);
int x_z = comparator.compare(x, z);
// javadoc: The implementor must ensure that sgn(compare(x, y)) == -sgn(compare(y, x))
if (Math.signum(x_y) == -Math.signum(y_x)) { // ok
} else {
System.err.println("not holding: sgn(compare(x, y)) == -sgn(compare(y, x))" //
+ " | x_y: " + x_y + ", y_x: " + y_x + ", x: " + x + ", y: " + y);
}
// javadoc: The implementor must also ensure that the relation is transitive:
// ((compare(x, y)>0) && (compare(y, z)>0)) implies compare(x, z)>0.
if (x_y > 0 && y_z > 0) {
if (x_z > 0) { // ok
} else {
System.err.println("not holding: ((compare(x, y)>0) && (compare(y, z)>0)) implies compare(x, z)>0" //
+ " | x_y: " + x_y + ", y_z: " + y_z + ", x_z: " + x_z + ", x: " + x + ", y: " + y + ", z: " + z);
}
}
// javadoc: Finally, the implementor must ensure that:
// compare(x, y)==0 implies that sgn(compare(x, z))==sgn(compare(y, z)) for all z.
if (x_y == 0) {
if (Math.signum(x_z) == Math.signum(y_z)) { // ok
} else {
System.err.println("not holding: compare(x, y)==0 implies that sgn(compare(x, z))==sgn(compare(y, z)) for all z" //
+ " | x_y: " + x_y + ", x_z: " + x_z + ", y_z: " + y_z + ", x: " + x + ", y: " + y + ", z: " + z);
}
}
}
}
}
}
I had to sort on several criterion (date, and, if same date; other things...). What was working on Eclipse with an older version of Java, did not worked any more on Android : comparison method violates contract ...
After reading on StackOverflow, I wrote a separate function that I called from compare() if the dates are the same. This function calculates the priority, according to the criteria, and returns -1, 0, or 1 to compare(). It seems to work now.
What about doing something simpler like this:
int result = card1.getSet().compareTo(card2.getSet())
if (result == 0) {
result = card1.getRarity().compareTo(card2.getRarity())
}
if (result == 0) {
result = card1.getId().compareTo(card2.getId())
}
if (result == 0) {
result = card1.getCardType().compareTo(card2.getCardType())
}
return result;
You just need to order the comparisons in order of preference.
I tried to increase++ a number (i) each time the distance between the camera and my face is between 40 cm and 80 cm. Unfortunately when it happen the textview shows (The number of occurrence is: 50). Help me please
public void update(final Messsage msg) {
for(int i = 0; i < 50; ++i) {
if (msg.getDistanceToFace() > 40 && msg.getDistanceToFace() < 80) {
textView.setText("The number of occurrence is: " + i);
textView.setTextColor(Color.GREEN);
}
}
}
you should probably add a break after you set the text, the way you coded it will set a text for all the iteration of i but you will only see the last one (50)
If you only want to increase the number when the face is a certain length away, shouldn't you do something more on the lines of this?
public void update(final Messsage msg) {
int i = 0;
if (msg.getDistanceToFace() > 40 && msg.getDistanceToFace() < 80) {
i++;
textView.setText("The number of occurrence is: " + i);
textView.setTextColor(Color.GREEN);
}
}
Maybe you'll need to have that i a global variable or an argument to the function, not sure without having more of the code.
If you don't want the i to increase really fast (because it increments each time the update function is called and the phone is close to the face), a simple code to do that would be something like
public void update(final Messsage msg) {
boolean isCloseToFace = false;
int i = 0;
if (msg.getDistanceToFace() > 40 && msg.getDistanceToFace() < 80) {
if (!isCloseToFace) {
i++;
isCloseToFace = true;
}
textView.setText("The number of occurrence is: " + i);
textView.setTextColor(Color.GREEN);
} else {
isCloseToFace = false;
}
}
I try do indoor navigation android application using Estimote beacons. Here is the code which I used to get distance between android device and beacons. This code sections runs approximately in every 1 second.
I need to execute this in every 100 milliseconds.
beaconManager.setRangingListener(new BeaconManager.RangingListener() {
#Override public void onBeaconsDiscovered(Region region, final List<Beacon> beacons) {
runOnUiThread(new Runnable() {
#Override public void run() {
long time= System.currentTimeMillis();
Log.i("###################### ", " #################");
Log.i("Time Class ", " Time value in millisecinds "+time);
toolbar.setSubtitle("Found beacons: " + beacons.size());
ArrayList<Beacon> newBeacons = new ArrayList<>();
for (int x=0; x<beacons.size();x++) {
int major= beacons.get(x).getMajor();
int minor = beacons.get(x).getMinor();
if (major==3&&minor==3) {
newBeacons.add(beacons.get(x));
Dsi[0] = Utils.computeAccuracy(beacons.get(x));
}
else if (major==4&&minor==4) {
newBeacons.add(beacons.get(x));
Dsi[1] = Utils.computeAccuracy(beacons.get(x));
}
else if (major==2&&minor==2) {
newBeacons.add(beacons.get(x));
Dsi[2] = Utils.computeAccuracy(beacons.get(x));
}
}
double[][] positions = new double[][] { { -3.4, 0.8}, { 0, 7.5 }, { 6.7, 6.7 } };
double[] distances = new double[] { Dsi[0], Dsi[1], Dsi[2] };
TrilaterationFunction trilaterationFunction = new TrilaterationFunction(positions, distances);
LinearLeastSquaresSolver lSolver = new LinearLeastSquaresSolver(trilaterationFunction);
NonLinearLeastSquaresSolver nlSolver = new NonLinearLeastSquaresSolver(trilaterationFunction, new LevenbergMarquardtOptimizer());
double[] expectedPosition = new double[] { 3.3, 15.0 };
RealVector x = lSolver.solve();
Optimum optimum = nlSolver.solve();
testResults(expectedPosition, 1, optimum, x);
adapter.replaceWith(newBeacons);
time= System.currentTimeMillis();
Log.i("Time Class ", " Time value in millisecinds "+time);
Log.i("###################### ", " #################");
}
});
}
});
How do I do that?
I got the answer for this problem. Here is the answer and explanation for the Estimote Beacon users and lovers.
There is a method called setForegroundScanPeriod in the Estimote Android SDK. This method implemented in BeaconManager class. This can be used to increase or decrease scanning time period.
This is the method definition
setForegroundScanPeriod(long scanPeriodMillis,long waitTimeMillis)
First parameter for the change the scan period and second parameter for the waiting time between two scanning period. Values of all parameters are taken as milliseconds. Example as follows.
setForegroundScanPeriod(200,5)
If you called like this, Then code section is executing in every 200 milliseconds with 5 milliseconds time interval.
Default values of the method is 1s for the scan time period and 0s for the waiting period. 200ms is minimum scan time period and 0ms is minimum waiting period. (if you reduce the scanning period scanning period, Broadcasting interval of your Estimote Beacon should be reduced. Broadcasting interval should less than scan time period and also minimum Broadcasting interval is 100ms)
Reducing Broadcast interval is negatively effect for Battery life of Beacon and Reducing scan time period is negatively effect for Battery life of Android device.
Here is the full example which I used to scan Beacons.
BeaconManager beaconManager = new BeaconManager(this);
beaconManager.setForegroundScanPeriod(200,0);
beaconManager.setRangingListener(new BeaconManager.RangingListener() {
#Override public void onBeaconsDiscovered(Region region, final List<Beacon> beacons) {
runOnUiThread(new Runnable() {
#Override public void run() {
toolbar.setSubtitle("Found beacons: " + beacons.size());
ArrayList<Beacon> newBeacons = new ArrayList<>();
for (int x=0; x<beacons.size();x++) {
int major= beacons.get(x).getMajor();
int minor = beacons.get(x).getMinor();
if (major==3&&minor==3) {
newBeacons.add(beacons.get(x));
Dsi[0] = Utils.computeAccuracy(beacons.get(x));
}
else if (major==4&&minor==4) {
newBeacons.add(beacons.get(x));
Dsi[1] = Utils.computeAccuracy(beacons.get(x));
}
else if (major==2&&minor==2) {
newBeacons.add(beacons.get(x));
Dsi[2] = Utils.computeAccuracy(beacons.get(x));
}
}
adapter.replaceWith(newBeacons);
}
});
}
});
I have a Realm database of sensor values with the following constructor:
public DataEntry(float x, float y, float z, long timestamp, int xIndex, String xValue) {
this.x = x;
this.y = y;
this.z = z;
this.xIndex = xIndex;
this.timestamp = timestamp;
this.xValue = xValue;
}
I would like this database to be a fixed size of a 100 points. I have this working in a LinkedList approach where I delete the first entry and add one to the end resulting in a fixed size then I use MPChartLib to plot. This is what I would like to mimic using a Realm database.
My approach is to first delete the first entry using the code below:
private void removeFirst(){
RealmResults<DataEntry> result1 = mRealm.where(DataEntry.class).findAll();
mRealm.beginTransaction();
Log.i(TAG, "remove the first element of the database ");
DataEntry first =result1.first();
first.removeFromRealm();
Log.i(TAG, "shift the indexes of the database by one " + result1.size());
for (int i = 0; i < result1.size(); i++) {
DataEntry u = result1.get(i);
int index = u.getxIndex();
u.setxIndex(index - 1);
String xValue = "" +index;
u.setxValue(xValue);
}
mRealm.commitTransaction();
}
Then I add a new data point from acquired data
#Subscribe
public void onSensorUpdatedEvent(SensorUpdatedEvent event) {
if (event.getSensor().getId() == this.sensor.getId()) {
// Log.i(TAG, "remove the first element of the database ");
removeFirst();
mRealm.beginTransaction();
DataEntry entry = mRealm.createObject(DataEntry.class);
// Log.i(TAG, "in database update event with index = " + nextIndex);
entry.setxIndex(nextIndex);
entry.setxValue("" + nextIndex);
nextIndex++;
entry.setAndroidDevice(mAndroidId);
entry.setTimestamp(event.getDataPoint().getTimestamp());
currT.setText(precsion.format(event.getDataPoint().getTimestamp()));
if (event.getDataPoint().getValues().length > 0) {
entry.setX(event.getDataPoint().getValues()[0]);
currX.setText(precsion.format(event.getDataPoint().getValues()[0]));
} else {
entry.setX(0.0f);
}
if (event.getDataPoint().getValues().length > 1) {
entry.setY(event.getDataPoint().getValues()[1]);
currY.setText(precsion.format(event.getDataPoint().getValues()[1]));
} else {
entry.setY(0.0f);
}
if (event.getDataPoint().getValues().length > 2) {
entry.setZ(event.getDataPoint().getValues()[2]);
currZ.setText(precsion.format(event.getDataPoint().getValues()[2]));
} else {
entry.setZ(0.0f);
}
deltaT.setText(precsion.format((event.getDataPoint().getTimestamp() - lastT) / scaleT));
lastT = event.getDataPoint().getTimestamp();
entry.setAccuracy(event.getDataPoint().getAccuracy());
mRealm.commitTransaction();
}
}
MPChartLib uses the xIndex to establish position along the x axis so I change these values as part of the removeFirst class. My results in MpChartLib do not give me the desired result. I may be using it beyond its application or I may not be using the Realm in the the correct manner.
What sort of makes sense is to start with 100 blank database entries then fill them one at a time until I've reached 100 then "shift" entries such that entry 1 becomes entry 0, entry 99 becomes 98 and I update entry 99 with the new data.
While I think this might work it seems very inefficient to shift values of 100 fixed database points.
I would rather prefer to delete the first one and add a new one at the end like I can do with a LinkedList.
Kind of lost as to how to proceed. Any assistance is greatly appreciated.
RealmQuery.findAll() won't give you a stable order of the results. You need to sort the results based on some fields to achieve that.
I think what you can do is combining using #PrimaryKey and RealmQuery.max().
public class DataEntry {
#PrimaryKey
private long id;
// ...
}
private void addEntry(DataEntry entry){
realm.beginTransaction();
RealmResults<DataEntry> results = realm.where(DataEntry.class).findAllSorted("id");
if (results.count.size() >= 100) {
// Remove the first entry
results.get(0).removeFromRealm();
}
// NOTE: Consider the integer overflow and the empty results here!!!!
entry.setId(restuls.max("id").longValue() + 1;
realm.copyToRealm(entry);
realm.commitTransaction();
}
The id won't be in range [1-100] but i believe you can do some math to achieve that.
Using the Primary key and sorting as beeender recommends works. I modified the timestamp as the primary key and included a string version of the timestamp which MPChartLib seems to require.
So my DataEntry Realm database
#RealmClass
public class DataEntry extends RealmObject {
private String sTimestamp;
private String androidDevice;
private float x;
private float y;
private float z;
private int accuracy;
#PrimaryKey
private long timestamp;
// no arguments constructor for Realm database
public DataEntry() {
}
public DataEntry(float x, float y, float z, long timestamp, String sTimestamp) {
this.x = x;
this.y = y;
this.z = z;
this.timestamp = timestamp;
this.sTimestamp = sTimestamp;
}
And the code for updating the database:
#Subscribe
public void onSensorUpdatedEvent(SensorUpdatedEvent event) {
if (event.getSensor().getId() == this.sensor.getId()) {
mRealm.beginTransaction();
RealmResults<DataEntry> results = mRealm.
where(DataEntry.class).findAllSorted("timestamp");
if (results.size() == 1) {
startTime = results.get(0).getTimestamp();
// Log.i(TAG, "first data received: startTime " + startTime);
}
// Log.i(TAG, "check size of the database " +results.size());
if (results.size() >= 100) {
// Log.i(TAG, "remove the first element of the database ");
results.get(0).removeFromRealm();
}
// Log.i(TAG, "create element for the database ");
DataEntry entry = mRealm.createObject(DataEntry.class);
entry.setAndroidDevice(mAndroidId);
long mTime = (long) (event.getDataPoint().getTimestamp()-startTime );
// Log.i(TAG, "time set at = " +mTime);
entry.setTimestamp(mTime);
// currT.setText(precsion.format(mTime));
entry.setsTimestamp(String.valueOf(mTime));
if (event.getDataPoint().getValues().length > 0) {
entry.setX(event.getDataPoint().getValues()[0]);
currX.setText(precsion.format(event.getDataPoint().getValues()[0]));
} else {
entry.setX(0.0f);
}
if (event.getDataPoint().getValues().length > 1) {
entry.setY(event.getDataPoint().getValues()[1]);
currY.setText(precsion.format(event.getDataPoint().getValues()[1]));
} else {
entry.setY(0.0f);
}
if (event.getDataPoint().getValues().length > 2) {
entry.setZ(event.getDataPoint().getValues()[2]);
currZ.setText(precsion.format(event.getDataPoint().getValues()[2]));
} else {
entry.setZ(0.0f);
}
deltaT.setText(precsion.format((event.getDataPoint().getTimestamp() - lastT) / scaleT));
totalT = ((event.getDataPoint().getTimestamp() - lastT ) / scaleT) * results.size()/1000;
totalTText.setText(String.valueOf(totalT));
lastT = event.getDataPoint().getTimestamp();
entry.setAccuracy(event.getDataPoint().getAccuracy());
// Log.i(TAG, "copy element to the database ");
mRealm.copyToRealm(entry);
// Log.i(TAG, " database has size = " + results.size());
mRealm.commitTransaction();
}
}
Then set the data into MPChartLib format
private void setData() {
// Log.i(TAG, "In setData perform database query , sort by timestamp");
RealmResults<DataEntry> result1 = mRealm.where(DataEntry.class).findAllSorted("timestamp");
// Log.i(TAG, "set Database size is = " + result1.size());
// Log.i(TAG, "Extract x data ");
RealmLineDataSet<DataEntry> set1 = new RealmLineDataSet<>(result1, "x");
set1.setDrawCubic(false);
set1.setLabel("Realm X");
set1.setDrawCircleHole(false);
set1.setColor(ColorTemplate.rgb("#FF5722"));
set1.setCircleColor(ColorTemplate.rgb("#FF5722"));
set1.setLineWidth(1.8f);
set1.setCircleSize(3.6f);
// Log.i(TAG, "Extract y data ");
RealmLineDataSet<DataEntry> set2 = new RealmLineDataSet<>(result1, "y");
set1.setDrawCubic(false);
set1.setLabel("Realm Y");
set1.setDrawCircleHole(false);
set1.setColor(ColorTemplate.rgb("#FF5722"));
set1.setCircleColor(ColorTemplate.rgb("#FF5722"));
set1.setLineWidth(1.8f);
set1.setCircleSize(3.6f);
// Log.i(TAG, "Extract z data ");
RealmLineDataSet<DataEntry> set3 = new RealmLineDataSet<>(result1, "z");
set1.setDrawCubic(false);
set1.setLabel("Realm Z");
set1.setDrawCircleHole(false);
set1.setColor(ColorTemplate.rgb("#FF5722"));
set1.setCircleColor(ColorTemplate.rgb("#FF5722"));
set1.setLineWidth(1.8f);
set1.setCircleSize(3.6f);
// Log.i(TAG, "add datasets ");
ArrayList<ILineDataSet> dataSets = new ArrayList<>();
dataSets.add(set1);
dataSets.add(set2);
dataSets.add(set3);
// Log.i(TAG, "Create Line Data for graphing " );
RealmLineData lineData = new RealmLineData(result1, "sTimestamp", dataSets);
// Log.i(TAG, "Set Data on Chart ");
mChart.setData(lineData);
// Log.i(TAG, "Re-draw ");
mChart.invalidate();
}
I'm ok with not having x-axis labels as they are huge numbers. my plan is to shut off the axis labels.