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.
I have a button which is let users when open the application then can use the function.
This is the function:
if(eyeDected)
{
if(detectedFrame > 25)
{
eyeDected = false;
detectedFrame = 0;
finish();
} else {
detectedFrame++;
Log.d("UNLOCK:", String.valueOf(detectedFrame));
}
} else {
eyeDected = true;
detectedFrame++;
Log.d("UNLOCK:", String.valueOf(detectedFrame));
}
I want do a while loop function: (the number of click is a,b,c) (For example b=1 is mean that user click once time only)
a=0
b=1
c=2
when 'a' = 0,3,6,9,12,15..... (mean 'a'+3) then run the function
when 'b' = 1,4,7,10 ,13....(mean b+3) then run the function
when 'c' = 2,5,8,11,16... (mean b+3) then run the function
How can i perform a while loop for these? Thanks
You can add following condition :
a=0
b=1
c=2
when 'a' = 0,3,6,9,12 (mean 'a'+3) then run a function
if(a%3 == 0){
// run a function
}
when 'b' = 1,4,7,10 (mean b+3) then run a function
if((b+1)%3 == 0){
// run B function
}
when 'c' = 2,5,8,11 (mean b+3) then run a function
if((c+2)%3 == 0){
// run B function
}
Ex. in loop :
for (int i=0;i<100;i++){
if( i % 3 == 0){
// run a function
}
else if( (i+1) % 3 == 0){
// run b function
}
else if( (i+2) % 3 == 0){
// run c function
}
}
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;
}
I like to make a game, but I get trouble with collecting points.
The purpose is to increase/decrease character point (charhop +1 or -1) whenever object 'face' is collided with injekBox, but the point just increase or decrease once then it return to the previous value.
The log also still print the value even if the object stop
I want to make the point change once if the 'face' collided with certain box, and will change again after collided with another box
char1.setHops(0);
public void onUpdate(final float pSecondsElapsed) {
if (char1.isJump()){
int rockPoint = char1.getPoints();
int maxBox = listBox.size();
int charHop = char1.getHops();
for (int j = 0; j < maxBox ; j++){
if (j == rockPoint){
j++;
}
Box injekBox = listBox.get(j);
if(injekBox.getRectangle().collidesWith(face)){
if(char1.isTurn()){
charHop++;
if (charHop == (maxBox - 1)){
char1.setTurn(false);
}
} else {
charHop--;
}
Log.i(this.toString(),"charHop: "+charHop);
injekBox.getRectangle().setColor(1, 0, 0);
} else {
injekBox.getRectangle().setColor(1, 1, 1);
}
}
}
}
Sorry for bad writing...
Thank you for attention :)
The scope of charHop is only within onUpdate. Once you leave that method, the contents of that variable is gone. You need the counterpart to char1.getHops()--something like char1.setHops(charHop);.
Html List tag not working in android TextView. This is my string content:
String str="A dressy take on classic gingham in a soft, textured weave of stripes that resembles twill. Take a closer look at this one.<ul><li>Trim, tailored fit for a bespoke feel</li><li>Medium spread collar, one-button mitered barrel cuffs</li><li>Applied placket with genuine mother-of-pearl buttons</li><li>;Split back yoke, rear side pleats</li><li>Made in the U.S.A. of 100% imported cotton.</li></ul>";
I loaded it in a text view like this:
textview.setText(Html.fromHtml(str));
The output looks like a paragraph. What can I do? Is there any solution for it?
Edit:
webview.loadData(str,"text/html","utf-8");
As you can see in the Html class source code, Html.fromHtml(String) does not support all HTML tags. In this very case, <ul> and <li> are not supported.
From the source code I have built a list of allowed HTML tags:
br
p
div
em
b
strong
cite
dfn
i
big
small
font
blockquote
tt
monospace
a
u
sup
sub
So you better use WebView and its loadDataWithBaseURL method. Try something like this:
String str="<html><body>A dressy take on classic gingham in a soft, textured weave of stripes that resembles twill. Take a closer look at this one.<ul><li>Trim, tailored fit for a bespoke feel</li><li>Medium spread collar, one-button mitered barrel cuffs</li><li>Applied placket with genuine mother-of-pearl buttons</li><li>;Split back yoke, rear side pleats</li><li>Made in the U.S.A. of 100% imported cotton.</li></ul></body></html>";
webView.loadDataWithBaseURL(null, str, "text/html", "utf-8", null);
I was having the same problem, and what I did is overriding the default TagHandler. This one worked for me.
public class MyTagHandler implements TagHandler {
boolean first = true;
String parent = null;
int index = 1;
#Override
public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
if (tag.equals("ul")) {
parent = "ul";
} else if (tag.equals("ol")) {
parent = "ol";
}
if (tag.equals("li")) {
if (parent.equals("ul")) {
if (first) {
output.append("\n\t•");
first = false;
} else {
first = true;
}
} else{
if (first) {
output.append("\n\t"+index+". ");
first = false;
index++;
} else {
first = true;
}
}
}
}
}
and for displaying the text...
myTextView.setText(Html.fromHtml("<ul><li>I am an Android developer</li><li>Another Item</li></ul>", null, new MyTagHandler()));
[Edit]
Kuitsi has also posted an really good library that does the same, got it from this SO link.
Full sample project is located at https://bitbucket.org/Kuitsi/android-textview-html-list.
Sample picture is available at https://kuitsi.bitbucket.io/stackoverflow3150400_screen.png
This solution is closest to masha's answer. Some code is also taken from inner class android.text.Html.HtmlToSpannedConverter. It supports nested ordered and unordered lists but too long texts in ordered lists are still aligned with item number rather than text. Mixed lists (ol and ul) needs some work too. Sample project contains implementation of Html.TagHandler which is passed to Html.fromHtml(String, ImageGetter, TagHandler).
Edit: For wider HTML tag support, https://github.com/NightWhistler/HtmlSpanner might also be worth trying.
A small fix to Aman Guatam code. The function above has problem of rendering newline character. For example: if before <li> tag is a <p> tag, 2 newline characters are rendered. Here is upgraded code:
import org.xml.sax.XMLReader;
import android.text.Editable;
import android.text.Html.TagHandler;
public class ListTagHandler implements TagHandler {
boolean first = true;
#Override
public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
// TODO Auto-generated method stub
if (tag.equals("li")) {
char lastChar = 0;
if (output.length() > 0)
lastChar = output.charAt(output.length() - 1);
if (first) {
if (lastChar == '\n')
output.append("\t• ");
else
output.append("\n\t• ");
first = false;
} else {
first = true;
}
}
}
}
WARNING
As of Android 7 android.text.Html actually supports li and ul tags and uses a basic BulletSpan(), which means in the latest versions of Android the Html.TagHandlersolutions posted here will be ignored
Make sure your code handles this change. In case you want a BulletSpan with a larger gap than the default, you can can replace it with another span:
val html = SpannableStringBuilder(HtmlCompat.fromHtml(source, HtmlCompat.FROM_HTML_MODE_COMPACT))
val bulletSpans = html.getSpans<BulletSpan>(0, html.length)
bulletSpans.forEach {
val spanStart = html.getSpanStart(it)
val spanEnd = html.getSpanEnd(it)
html.removeSpan(it)
val bulletSpan = BulletSpan(gapWidthInDp, context.getColor(R.color.textColorBlack))
html.setSpan(bulletSpan, spanStart, spanEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
Different solution using LeadingMarginSpan. Handles ordered and unordered lists as well as nesting.
public class ListTagHandler implements TagHandler
{
private int m_index = 0;
private List< String > m_parents = new ArrayList< String >( );
#Override
public void handleTag( final boolean opening, final String tag, Editable output, final XMLReader xmlReader )
{
if( tag.equals( "ul" ) || tag.equals( "ol" ) || tag.equals( "dd" ) )
{
if( opening )
{
m_parents.add( tag );
}
else m_parents.remove( tag );
m_index = 0;
}
else if( tag.equals( "li" ) && !opening ) handleListTag( output );
}
private void handleListTag( Editable output )
{
if( m_parents.get(m_parents.size()-1 ).equals( "ul" ) )
{
output.append( "\n" );
String[ ] split = output.toString( ).split( "\n" );
int lastIndex = split.length - 1;
int start = output.length( ) - split[ lastIndex ].length( ) - 1;
output.setSpan( new BulletSpan( 15 * m_parents.size( ) ), start, output.length( ), 0 );
}
else if( m_parents.get(m_parents.size()-1).equals( "ol" ) )
{
m_index++ ;
output.append( "\n" );
String[ ] split = output.toString( ).split( "\n" );
int lastIndex = split.length - 1;
int start = output.length( ) - split[ lastIndex ].length( ) - 1;
output.insert( start, m_index + ". " );
output.setSpan( new LeadingMarginSpan.Standard( 15 * m_parents.size( ) ), start, output.length( ), 0 );
}
}
}
If you only need to format a list, keep it simple and copy/paste a unicode character in your TextView to achieve the same result.
• Unicode Character 'BULLET' (U+2022)
I came here looking for TagHandler implementations. Both Truong Nguyen and Aman Guatam answers are very nice, but I needed a mixed version of both: I needed my solution not to overformat it and to be able to ressolve <ol> tags, since I'm parsing something like <h3>title</h3><ol><li>item</li><li>item</li><li>item</li></ol>.
Here's my solution.
import org.xml.sax.XMLReader;
import android.text.Editable;
import android.text.Html.TagHandler;
public class MyTagHandler implements TagHandler {
boolean first = true;
String parent = null;
int index = 1;
public void handleTag(final boolean opening, final String tag,
final Editable output, final XMLReader xmlReader) {
if (tag.equals("ul")) {
parent = "ul";
index = 1;
} else if (tag.equals("ol")) {
parent = "ol";
index = 1;
}
if (tag.equals("li")) {
char lastChar = 0;
if (output.length() > 0) {
lastChar = output.charAt(output.length() - 1);
}
if (parent.equals("ul")) {
if (first) {
if (lastChar == '\n') {
output.append("\t• ");
} else {
output.append("\n\t• ");
}
first = false;
} else {
first = true;
}
} else {
if (first) {
if (lastChar == '\n') {
output.append("\t" + index + ". ");
} else {
output.append("\n\t" + index + ". ");
}
first = false;
index++;
} else {
first = true;
}
}
}
}
}
Note that, since we are resetting the index value whenever a new list starts, it WILL NOT work if you nest lists like in <ol><li>1<ol><li>1.1</li><li>1.2</li></ol><li>2</li></ol>11.11.22
With that code, you would get 1, 1, 2, 3 instead of 1, 1, 2, 2.
You can simply replace the "li" with unicodes
#Override
public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
if (tag.equalsIgnoreCase("li")) {
if (opening) {
output.append("\u2022 ");
} else {
output.append("\n");
}
}
}
Sure, there ise a way of showing bullets in Android TextView. You can replace <li> tags with (which is HTML code for bullet).
If you want to try other list icons, use the preferred one from the table is this link;
http://www.ascii-code.com/
You can use Html.TagHandler. Below can be used for kotlin
class UlTagHandler : Html.TagHandler {
override fun handleTag(
opening: Boolean, tag: String, output: Editable,
xmlReader: XMLReader
) {
if (tag == "ul" && !opening) output.append("\n")
if (tag == "li" && opening) output.append("\n\t•")
}
}
and
textView.setText(Html.fromHtml(myHtmlText, null, UlTagHandler()));
Lord Voldermort's answer is a good starting point. However I required ol tag to display ordered list 1. 2. 3. .... instead of bullets. Also, nested tags need special handling to work properly.
In my code, I have maintained stack(parentList) to keep track of opened and closed ul and ol tags and also to know the current open tag.
Also, a levelWiseCounter is used to maintain different counts in case of nested ol tags.
myTextView.setText(Html.fromHtml("your string", null, new CustomTagHandler()));
.
.
.
private static class CustomTagHandler implements TagHandler
{
int level = 0;
private LinkedList<Tag> parentList = new LinkedList<DetailFragment.CustomTagHandler.Tag>();
private HashMap<Integer, Integer> levelWiseCounter = new HashMap<Integer, Integer>();
#Override
public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader)
{
if (tag.equalsIgnoreCase("ul") || tag.equalsIgnoreCase("ol"))
{
if (opening)
{
if (tag.equalsIgnoreCase("ul"))
{
parentList.push(Tag.UL);
}
else
{
parentList.push(Tag.OL);
}
level++;
}
else
{
if (!parentList.isEmpty())
{
parentList.pop();
//remove counter at that level, in any present.
levelWiseCounter.remove(level);
}
level--;
if (level < 0)
{
level = 0;
}
}
}
else if (tag.equalsIgnoreCase("li"))
{
if (opening && level > 0)
{
//new line check
int length = output.toString().length();
if (length > 0 && (output.toString().charAt(length - 1) == '\n'))
{
}
else
{
output.append("\n");
}
//add tabs as per current level of li
for (int i = 0; i < level; i++)
{
output.append("\t");
}
// append dot or numbers based on parent tag
if (Tag.UL == parentList.peek())
{
output.append("•");
}
else
{
//parent is OL. Check current level and retreive counter from levelWiseCounter
int counter = 1;
if (levelWiseCounter.get(level) == null)
{
levelWiseCounter.put(level, 1);
}
else
{
counter = levelWiseCounter.get(level) + 1;
levelWiseCounter.put(level, counter);
}
output.append(padInt(counter) + ".");
}
//trailing tab
output.append("\t");
}
}
}
/**
* Add padding so that all numbers are aligned properly. Currently supports padding from 1-99.
*
* #param num
* #return
*/
private static String padInt(int num)
{
if (num < 10)
{
return " " + num;
}
return "" + num;
}
private enum Tag
{
UL, OL
}
}
How about the next code (based on this link) :
public class TextViewHtmlTagHandler implements TagHandler
{
/**
* Keeps track of lists (ol, ul). On bottom of Stack is the outermost list
* and on top of Stack is the most nested list
*/
Stack<String> lists =new Stack<String>();
/**
* Tracks indexes of ordered lists so that after a nested list ends
* we can continue with correct index of outer list
*/
Stack<Integer> olNextIndex =new Stack<Integer>();
/**
* List indentation in pixels. Nested lists use multiple of this.
*/
private static final int indent =10;
private static final int listItemIndent =indent*2;
private static final BulletSpan bullet =new BulletSpan(indent);
#Override
public void handleTag(final boolean opening,final String tag,final Editable output,final XMLReader xmlReader)
{
if(tag.equalsIgnoreCase("ul"))
{
if(opening)
lists.push(tag);
else lists.pop();
}
else if(tag.equalsIgnoreCase("ol"))
{
if(opening)
{
lists.push(tag);
olNextIndex.push(Integer.valueOf(1)).toString();// TODO: add support for lists starting other index than 1
}
else
{
lists.pop();
olNextIndex.pop().toString();
}
}
else if(tag.equalsIgnoreCase("li"))
{
if(opening)
{
if(output.length()>0&&output.charAt(output.length()-1)!='\n')
output.append("\n");
final String parentList=lists.peek();
if(parentList.equalsIgnoreCase("ol"))
{
start(output,new Ol());
output.append(olNextIndex.peek().toString()+". ");
olNextIndex.push(Integer.valueOf(olNextIndex.pop().intValue()+1));
}
else if(parentList.equalsIgnoreCase("ul"))
start(output,new Ul());
}
else if(lists.peek().equalsIgnoreCase("ul"))
{
if(output.charAt(output.length()-1)!='\n')
output.append("\n");
// Nested BulletSpans increases distance between bullet and text, so we must prevent it.
int bulletMargin=indent;
if(lists.size()>1)
{
bulletMargin=indent-bullet.getLeadingMargin(true);
if(lists.size()>2)
// This get's more complicated when we add a LeadingMarginSpan into the same line:
// we have also counter it's effect to BulletSpan
bulletMargin-=(lists.size()-2)*listItemIndent;
}
final BulletSpan newBullet=new BulletSpan(bulletMargin);
end(output,Ul.class,new LeadingMarginSpan.Standard(listItemIndent*(lists.size()-1)),newBullet);
}
else if(lists.peek().equalsIgnoreCase("ol"))
{
if(output.charAt(output.length()-1)!='\n')
output.append("\n");
int numberMargin=listItemIndent*(lists.size()-1);
if(lists.size()>2)
// Same as in ordered lists: counter the effect of nested Spans
numberMargin-=(lists.size()-2)*listItemIndent;
end(output,Ol.class,new LeadingMarginSpan.Standard(numberMargin));
}
}
else if(opening)
Log.d("TagHandler","Found an unsupported tag "+tag);
}
private static void start(final Editable text,final Object mark)
{
final int len=text.length();
text.setSpan(mark,len,len,Spanned.SPAN_MARK_MARK);
}
private static void end(final Editable text,final Class<?> kind,final Object... replaces)
{
final int len=text.length();
final Object obj=getLast(text,kind);
final int where=text.getSpanStart(obj);
text.removeSpan(obj);
if(where!=len)
for(final Object replace : replaces)
text.setSpan(replace,where,len,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return;
}
private static Object getLast(final Spanned text,final Class<?> kind)
{
/*
* This knows that the last returned object from getSpans()
* will be the most recently added.
*/
final Object[] objs=text.getSpans(0,text.length(),kind);
if(objs.length==0)
return null;
return objs[objs.length-1];
}
private static class Ul
{
}
private static class Ol
{
}
}
I had the problem, that I always got an empty line after a list with #Kuitsis solution. I added a few lines in handleTag() and now the empty lines are gone:
#Override
public void handleTag(final boolean opening, final String tag, final Editable output, final XMLReader xmlReader) {
if (UL_TAG.equalsIgnoreCase(tag)) {
if (opening) { // handle <ul>
lists.push(new Ul());
} else { // handle </ul>
lists.pop();
if (output.length() > 0 && output.charAt(output.length() - 1) == '\n') {
output.delete(output.length() - 1, output.length());
}
}
} else if (OL_TAG.equalsIgnoreCase(tag)) {
if (opening) { // handle <ol>
lists.push(new Ol()); // use default start index of 1
} else { // handle </ol>
lists.pop();
if (output.length() > 0 && output.charAt(output.length() - 1) == '\n') {
output.delete(output.length() - 1, output.length());
}
}
} else if (LI_TAG.equalsIgnoreCase(tag)) {
if (opening) { // handle <li>
lists.peek().openItem(output);
} else { // handle </li>
lists.peek().closeItem(output, lists.size());
}
} else {
Log.d("TagHandler", "Found an unsupported tag " + tag);
}
}
this is a confirmation to what kassim has stated. there is fragmentation. i found how to resolve this. i have to rename <li> and ul to a custom tag. so:
myHTML.replaceAll("</ul>","</customTag>").replaceAll("<ul>","<customTag>");
//likewise for li
then in my handler i can look for that customTag (which does nothing) and make it do something.
//now my handler can handle the customtags. it was ignoring them after nougat.
public class UlTagHandler implements Html.TagHandler {
//for ul in nougat and up this tagHandler is completely ignored
#Override
public void handleTag(boolean opening, String tag, Editable output,
XMLReader xmlReader) {
if (tag.equals("customtag2") && opening)
output.append("\n\t\u25CF\t");
if (tag.equals("customtag2") && !opening)
output.append("\n");
}
}
this should make it work for all versions of android.