Im having troubles with Android SortedList in RecyclerView, mainly with the remove method:
public void replaceAll(List userFertList, List defaultFertList){
restartIndexes(userFertList, defaultFertList);
mComparator.swapLists(Utils.fertiliserListToNameList(userFertList));
List<Fertiliser> combinedList = Utils.combineFertLists(userFertList, defaultFertList);
mSortedList.beginBatchedUpdates();
for (int i = mSortedList.size() -1; i > -1 ; i--) {
final Fertiliser fertiliser = mSortedList.get(i);
if(!combinedList.contains(fertiliser)){
if(!mSortedList.remove(fertiliser)){
throw new RuntimeException();
};
}
}
mSortedList.addAll(combinedList);
mSortedList.endBatchedUpdates();
}
The above code is executed when filtering the list. All of the objects that are not present in the new list are removed. However the call to remove objects sometimes fail. I know the object is present, because it's taken from the SortedList itself.
My research hinted me there's something wrong with my Comparator compare method:
#Override
public int compare(Fertiliser fertiliser, Fertiliser t1) {
if(fertiliser == t1){
return 0;
}
if(mUserFertNames.contains(fertiliser.getName()) != mUserFertNames.contains(t1.getName())){
return mUserFertNames.contains(fertiliser.getName()) ? -1 : 1;
} else {
return fertiliser.getName().compareToIgnoreCase(t1.getName());
}
}
Im sorting by two criteria (one that checks if the object is present in a list and by name).
So my thinking is, because SortedList uses the Comparator to locate the element, my Comparator gives false results, and the list cannot find the item:
The called method from the SortedList:
private int findIndexOf(T item, T[] mData, int left, int right, int reason) {
while (left < right) {
final int middle = (left + right) / 2;
T myItem = mData[middle];
final int cmp = mCallback.compare(myItem, item);
if (cmp < 0) {
left = middle + 1;
} else if (cmp == 0) {
if (mCallback.areItemsTheSame(myItem, item)) {
return middle;
} else {
int exact = linearEqualitySearch(item, middle, left, right);
if (reason == INSERTION) {
return exact == INVALID_POSITION ? middle : exact;
} else {
return exact;
}
}
} else {
right = middle;
}
}
return reason == INSERTION ? left : INVALID_POSITION;
}
However i coudn't find a solution. Can you help me?
P.S. When i examined the error, both objects were not in the list (so they were compared by names only).
Try .removeItemAt(i) instead of .remove(fertiliser). This worked for me while list was filtered.
Related
Actually i'm developing an inventory app where i scan some EAN codes then i insert the quantity of an item and then i add it to an ArrayList in recyclerView.
For now i had no problem as i've made the inventory part where the items had to have different lines for each in recyclerView but now i have to make the order part and here if an item exist yet in ArrayList i have to sum it's quantity and put it to top of recyclerView.
I was trying to make something like a for loop when i'm going to add a new item and check if it's exist in ArrayList, if it's exist i was going to sum that item quantity with old one but the problem was that sometimes the app was going to crash and that the item wasn't going on top of recyclerView.
Do you have any suggestion on how can i do it?
for (ItemModel item : itemModel) {
if (item.getCodiceArticolo()
.equals(code.getText()
.toString())) {
item.setQta(String.valueOf(
Integer.parseInt(item.getQta()) + 1));
}
}
I was trying to make something like that.
Try this code:
ItemModel matchedItem = null;
int matchedItemIndex = -1;
for (ItemModel item : itemModel) {
if (item.getCodiceArticolo()
.equals(code.getText()
.toString())) {
item.setQta(String.valueOf(
Integer.parseInt(item.getQta()) + 1));
matchedItem = item;
matchedItemIndex = itemModel.indexOf(item);
break;
}
}
if (matchedItemIndex > -1) {
itemModel.remove(matchedItem);
itemModel.add(
0,
matchedItem);
notifyItemMoved(index,
0);
}
You do not adderror log to your list, so I guess your program crashes because sometimes there is a value that has no quantity (there is no valid number) and therefore can not parse the number, so in this case you just write that there is one item in list that has not been there yet.
for (ItemModel item : itemModel) {
if (item.getCodiceArticolo()
.equals(code.getText()
.toString())) {
try {
item.setQta(String.valueOf(
Integer.parseInt(item.getQta()) + 1));
}
catch (Exception ex) {
item.setQta(String.valueOf(1));
}
}
}
If this does not help, please attach error log.
Since the app is crashing, your ArrayList might not have been initialized as suggested in the comments.
For checking if the item exists you can use
if (arraylist_of_items != null && arraylist_of_items.contains(item)) {
// do your stuff here
}
Three days a go i was getting the "ConcurrentModificationException" but now i'm trying another approach inspired by other answers or better the following one:
boolean nuovo = true;
for (ItemModel itemModels : itemModel) {
if (itemModels.getCodiceArticolo()
.equals(code.getText()
.toString())) {
itemModels.setQta(String.valueOf(
Integer.parseInt(itemModels.getQta()) + 1));
nuovo = false;
break;
}
}
if (nuovo) {
itemModel.add(new ItemModel(
code.getText()
.toString(),
"1"));
}
And is not crashing anymore and seems to work fine.
Thank's all for suggestions.
UPDATE
THANKS TO kartik malik ANSWER i was able to even "update" my items by adding the last one added on top, as i'm using reverse recyclerView i've done it by this wasy instead of putting the item to position 0
ItemModel matchedItem = null;
int matchedItemIndex = -1;
boolean nuovo = true;
for (ItemModel itemModels : itemModel) {
if (itemModels.getCodiceArticolo()
.equals(code.getText()
.toString())) {
itemModels.setQta(String.valueOf(
Integer.parseInt(itemModels.getQta()) +
Integer.parseInt(qta.getText()
.toString())));
MediaPlayer mpFound = MediaPlayer.create(
OrdiniActivity.this,
R.raw.errorsound);
mpFound.start();
matchedItem = itemModels;
matchedItemIndex = itemModel.indexOf(itemModels);
nuovo = false;
break;
}
}
if (matchedItemIndex > -1) {
itemModel.remove(matchedItem);
itemModel.add(matchedItem);
}
if (nuovo) {
itemModel.add(new ItemModel(
code.getText()
.toString(),
qta.getText()
.toString()));
}
With the boolean i'm checking if the item exist or not and if it doesn't exist i add the item as a new one.
I'm using the MPAndroidChart and am really enjoying it.
A 'little' need I have is that I can put null values to the 'entrys'. I'm monitoring the apache conections on servers of my system, and I would to see if they is down (where I put the null value) or if they just no conections (0).
I tried, but the Entry class don't accept 'null' as value showing the message: 'The constructor Entry(null, int) is undefined'
Thanks!
A possible solution for you could be to check weather the object you received is null, or not. If the object is null, you don't even create an Entry object instead of just setting it's value to null.
Example:
// array that contains the information you want to display
ConnectionHolder[] connectionHolders = ...;
ArrayList<Entry> entries = new ArrayList<Entry>();
int cnt = 0;
for(ConnectionHolder ch : connectionHolders) {
if(ch != null) entries.add(new Entry(ch.getNrOfConnections(), cnt));
else {
// do nothing
}
cnt++; // always increment
}
This would create e.g. a LineChart where no circles are drawn on indices where the ConnectionHolder object was null.
For a future release of the library, I will try to add the feature so that null values are supported.
My solution is to draw another DataSet with TRANSPARENT (or arbitrary) color:
- chart with fixed number of X values
- Y values are updated periodically
- boolean flag indicate transparent part (or another color)
private static final int SERIES_SIZE = 360;
int xIndex = -1;
float xIndexVal;
private LineChart chart;
private boolean currentFlag;
public void createChart(LineDataSet dataSet) {
LineData chartData = new LineData();
prepareDataSet(dataSet);
chartData.addDataSet(dataSet);
for (int i = 0; i < SERIES_SIZE; i++) {
chartData.addXValue("" /*+ i*/);
}
chart.setData(chartData);
}
private void prepareDataSet(LineDataSet dataSet, YAxis axis, int color) {
// configure set
}
public void update(Float val, boolean flag) {
List<ILineDataSet> dsl = chart.getData().getDataSets();
Log.d("chart", String.format("%s --- %d sets, index %d", descr, dsl.size(), xIndex));
if (xIndex == SERIES_SIZE - 1) {
// remove all entries at X index 0
for (int i = 0; i < chart.getData().getDataSetCount(); i++) {
Entry entry0 = chart.getData().getDataSetByIndex(i).getEntryForIndex(0);
if (entry0 != null && entry0.getXIndex() == 0) {
chart.getData().removeEntry(entry0, i);
Log.d("chart", String.format("entry 0 removed from dataset %d, %d entries in the set", i, chart.getData().getDataSetByIndex(i).getEntryCount()));
}
else {
Log.d("chart", String.format("all %d entries in the set kept", chart.getData().getDataSetByIndex(i).getEntryCount()));
}
}
// remove empty set, if any
for (Iterator<ILineDataSet> mit = dsl.iterator(); mit.hasNext(); ) {
if (mit.next().getEntryCount() == 0) {
mit.remove();
Log.d("chart", String.format("set removed, %d sets", dsl.size()));
}
}
// move all entries by -1
for (ILineDataSet ds : dsl) {
for (Entry entry : ((LineDataSet)ds).getYVals()) {
entry.setXIndex(entry.getXIndex() - 1);
}
}
}
else {
xIndex++;
}
if (currentFlag != flag) {
currentFlag = !currentFlag;
LineDataSet set = new LineDataSet(null, "");
prepareDataSet(set, chart.getAxisLeft(), currentFlag ? Color.TRANSPARENT : Color.BLUE);
chart.getData().addDataSet(set);
if (xIndex != 0) {
chart.getData().addEntry(new Entry(xIndexVal, xIndex - 1), dsl.size() - 1);
}
}
xIndexVal = val;
chart.getData().addEntry(new Entry(val, xIndex), dsl.size() - 1);
chart.notifyDataSetChanged();
chart.invalidate();
}
I have two lists of Default and Chrome browsers history.
I want to merge these two lists into one list.
I need to update item if I find it duplicate (is common between two lists).
So, my "BrowserRecord" class is like this:
public class BrowserRecord {
private long id;
private int bookmark;
private long created;
private long date;
private String title;
private String url;
private long visits;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BrowserRecord record = (BrowserRecord) o;
return url.equals(record.url);
}
#Override
public int hashCode() {
return url.hashCode();
}
// other getter setter methods
...
}
and finally, I have a method that gets browsers histories and does merging:
public List<BrowserRecord> getHistory() {
List<BrowserRecord> browserList = new ArrayList<BrowserRecord>();
// get history of default and chrome browsers
List<BrowserRecord> defaultList = getDefaultBrowserHistory();
List<BrowserRecord> chromeList = getChromeBrowserHistory();
Log.e(TAG, "=> size of Default browser:" + defaultList.size());
Log.e(TAG, "=> size of Chrome browser:" + chromeList.size());
// compare list A with B, update A item if equal item found in B and push it to tempList
for(int i=0; i<chromeList.size(); i++) {
BrowserRecord chromeBrowser = chromeList.get(i);
for(int j=0; j<defaultList.size(); j++) {
BrowserRecord defaultBrowser = defaultList.get(j);
if(chromeBrowser.equals(defaultBrowser)) {
if(chromeBrowser.getBookmark() != defaultBrowser.getBookmark())
chromeBrowser.setBookmark(1);
chromeBrowser.setVisits(chromeBrowser.getVisits() + defaultBrowser.getVisits());
}
}
browserList.add(chromeBrowser);
}
// compare list B with A, jump if equal item found in A, push to tempList if item not found
for(int i=0; i<defaultList.size(); i++) {
BrowserRecord defaultBrowser = defaultList.get(i);
boolean found = false;
for(int j=0; j<chromeList.size(); j++) {
BrowserRecord chromeBrowser = chromeList.get(j);
if(defaultBrowser.equals(chromeBrowser)) {
found = true;
break;
}
}
if(!found)
browserList.add(defaultBrowser);
}
Log.e(TAG, "=> size of final browser:" + browserList.size());
return browserList;
}
I have tested this method and is working fine. Since my history records on mobile device after 3 years didn't exceed more than 200 records on one list and 150 for others, I assume something similar is happening for other users. But I'm sure is not optimum way.
What do you recommend?
any suggestion would be appreciated. Thanks.
Not sure I understand correctly, but it seems like what you're trying to do is, given both lists, create a final list which will contain all of the elements from both lists, removing any duplicates.
If this is the case, then take a look at Java's TreeSet class. If you iterate over all of the elements from both your lists and insert them into a TreeSet, you will basically get the result you're looking for. You can then use an Iterator to create an ArrayList containing all of the non-duplicate items from both your lists. As a side-effect of using a TreeSet, they will ordered (you can also use either a HashSet if you don't care about the order or a LinkedHashSet if you want to preserve the order of insertion).
I'm a complete programming noob so go easy...
So I'm wondering how I would go about checking the edittext string to see if it "isBlank" using this isblank Boolean.
I know its probably a very easy answer but I just can't seem to get my head around it.
Any help appreciated.
public static boolean isBlank(String string) {
if (string == null || string.length() == 0)
return true;
int l = string.length();
for (int i = 0; i < l; i++) {
if (!Character.isWhitespace(string.codePointAt(i)))
return false;
}
return true;
}
**Thanks Heaps guys all helped alot!!.. If I could +1 I would.
You can write your method much shorter, like so:
static boolean isBlank(String string) {
return string == null || string.trim().length() == 0;
}
The trim() method removes all whitespace characters from beginning and end of a string. If what remains has length == 0, the whole string must have consisted of whitespace only.
The usage in your code depends on your need, but generally you'll use it in if() statements to make the code more readable:
String foo = "... some string ...";
if (isBlank(foo)) {
// foo is empty or only contains whitespace
} else {
// foo contains some text.
}
You can do this in single line.
if(edittext.getText().toString().trim().length()>0){
Syste.out.println("Not Blank");
}else{
Syste.out.println("Blank");
}
Like this:
if (isBlank(edittext.getText().toString())) {
// Blank
} else {
// Not blank
}
You can do it in this way:
Declare Class level variable:
boolean blank = false;
public static boolean isBlank(String string) {
if (string == null || string.trim().length() == 0){
blank = true;
}
else{
blank = false;
}
return blank;
}
Trying to use a loop to set the text of 12 checkboxes from a db query. Would like to substitute "add1" with an array value and loop through all 12 instead of spelling out each one. Any ideas of how to do this?
Here is the code I am trying to modify:
add1Text= (CheckBox) findViewById(R.id.add1);
if (cursor.getString(cursor.getColumnIndex("add1")) == null) {
add1Text.setVisibility(View.GONE);
}
else {
add1Text.setText(cursor.getString(cursor.getColumnIndex("add1")));
}
Please note: everything below is off the top of my head, I can't test it right now. I'll test it later when I get a chance.
I think you'll need to keep track of which column to associate with each CheckBox... I'm presuming it's something like this:
Column: add1 => Checkbox: add1Text
Column: add2 => Checkbox: add2Text
and so on and so forth.
In this circumstance, you'll need to manually keep track of them, possibly in an array. I'd suggest making a Pair class that you can use. I've altered the class from this post [ A Java collection of value pairs? (tuples?) ]
public class Pair<L,R> {
private final L left;
private final R right;
public Pair(L left, R right) {
this.left = left;
this.right = right;
}
public L getLeft() { return left; }
public R getRight() { return right; }
#Override
public int hashCode() { return left.hashCode() ^ right.hashCode(); }
#Override
public boolean equals(Object o) {
if (o == null) return false;
if (!(o instanceof Pair)) return false;
Pair pairo = (Pair) o;
return this.left.equals(pairo.getLeft()) &&
this.right.equals(pairo.getRight());
}
}
Now, you'll need to make a List (or similar) containing the pairs that you want.
List<Pair<CheckBox, String>> list = new ArrayList<Pair<CheckBox, String>>;
list.add(new Pair<CheckBox, String>((CheckBox) findViewById(R.id.add1), "add1");
list.add(new Pair<CheckBox, String>((CheckBox) findViewById(R.id.add2), "add2");
list.add(new Pair<CheckBox, String>((CheckBox) findViewById(R.id.add3), "add3");
and so on and so forth
Then you can iterate through the List using something like
foreach (Pair<CheckBox, String> item in list)
{
if (cursor.getString(cursor.getColumnIndex(item.getLeft()) == null)
{
item.getRight().setVisibility(View.GONE);
}
else
{
item.getRight().setText(cursor.getString(cursor.getColumnIndex(item.getLeft()));
}
}
Got it! Forgot that I was dealing with objects and also realized I needed a third array. Here is what I came up with.
cList contains column names
fList are the objects (in this case CheckBoxes)
pList are the names of the objects I am selecting from the layout.
Object fList[]={add1Text,add2Text,add3Text};
int pList[]={R.id.add1,R.id.add2,R.id.add3};
cList = cursor.getColumnNames();
for (int i =0; i < fList.length; i++){
fList[i] = (CheckBox) findViewById(pList[i]);
if (cursor.getString(cursor.getColumnIndex(cList[i])) == null) {
((TextView) fList[i]).setVisibility(View.GONE);
}
else {
((TextView) fList[i]).setText(cList[i] + " - " + cursor.getString(cursor.getColumnIndex( cList[i])));
}
}
Sets the CheckBox text to ( Column name - Value )