I have a TextView that needs to be highlighted in multiple positions.
I've done this before with EditText and it worked fine.
The only difference is that EditText implements Spannable whereas TextView doesn't.
TextView seems to remain span only from the last setSpan() method call.
Code
TextView tvMainActivityTitle = findViewById(R.id.tvMainActivityTitle);
SpannableString spanStr = new SpannableString(tvMainActivityTitle.getText());
ForegroundColorSpan color = new ForegroundColorSpan(Color.rgb(192,0,0));
spanStr.setSpan(color, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spanStr.setSpan(color, 2, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spanStr.setSpan(color, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); // <- this one wins
tvMainActivityTitle.setText(spanStr);
So it paints only the first character in the TextView.
I assume that TextView struggles with this type of task.
How do I fix this?
You just need multiple instances of ForegroundColorSpan for each set method & it will work.
For your example:
TextView tvMainActivityTitle = findViewById(R.id.tvMainActivityTitle);
SpannableString spanStr = new SpannableString(tvMainActivityTitle.getText());
ForegroundColorSpan color1 = new ForegroundColorSpan(Color.rgb(192, 0, 0));
ForegroundColorSpan color2 = new ForegroundColorSpan(Color.rgb(192, 0, 0));
ForegroundColorSpan color3 = new ForegroundColorSpan(Color.rgb(192, 0, 0));
spanStr.setSpan(color1, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spanStr.setSpan(color2, 2, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spanStr.setSpan(color3, 5, 6, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); // <- this one wins
tvMainActivityTitle.setText(spanStr);
Also for further read: https://www.geeksforgeeks.org/text-styling-with-spans-in-android/
Refer #Mayur Gajra's answer for the solution.
Adding the reason why this happens for better understanding.
So, we can see that each span can only be used to set one span.
Why is that so?
On digging up the SpannableString setSpan(), I can see this code,
for (int i = 0; i < count; i++) {
if (spans[i] == what) {
int ostart = data[i * COLUMNS + START];
int oend = data[i * COLUMNS + END];
data[i * COLUMNS + START] = start;
data[i * COLUMNS + END] = end;
data[i * COLUMNS + FLAGS] = flags;
sendSpanChanged(what, ostart, oend, nstart, nend);
return;
}
}
what is the span argument passed to the setSpan method. what is an object.
Hence if (spans[i] == what) checks if there is already a span set with the same instance. If so, the span is replaced with the new span.
Related
I have a method in Python that makes use of OpenCV to remove the background from an image. I want the same functionality to work with android's version of OpenCV but I just cant seem to wrap my head around how the arrays work and how I can process them.
This is what I have so far in Java :
private Bitmap GetForeground(Bitmap source){
source = scale(source,300,300);
Mat mask = Mat.zeros(source.getHeight(),source.getWidth(),CvType.CV_8U);
Mat bgModel = Mat.zeros(1,65,CvType.CV_64F);
Mat ftModel = Mat.zeros(1,65,CvType.CV_64F);
int x = (int)Math.round(source.getWidth()*0.1);
int y = (int)Math.round(source.getHeight()*0.1);
int width = (int)Math.round(source.getWidth()*0.8);
int height = (int)Math.round(source.getHeight()*0.8);
Rect rect = new Rect(x,y, width,height);
Mat sourceMat = new Mat();
Utils.bitmapToMat(source, sourceMat);
Imgproc.grabCut(sourceMat, mask, rect, bgModel, ftModel, 5, Imgproc.GC_INIT_WITH_RECT);
int frameSize=sourceMat.rows()*sourceMat.cols();
byte[] buffer= new byte[frameSize];
mask.get(0,0,buffer);
for (int i = 0; i < frameSize; i++) {
if (buffer[i] == 2 || buffer[i] == 0){
buffer[i] = 0;
}else{
buffer[i] = 1 ;
}
}
byte[][] sourceArray = getMultiChannelArray(sourceMat);
byte[][][] reshapedMask = ReshapeArray(buffer, sourceMat.rows(), sourceMat.cols());
return source;
}
private byte[][][] ReshapeArray(byte[] arr, int rows, int cols){
byte[][][] out = new byte[cols][rows][1];
int index=0;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
out[i][j][0] = arr[index];
index++;
}
}
return out;
}
public static byte[][] getMultiChannelArray(Mat m) {
//first index is pixel, second index is channel
int numChannels=m.channels();//is 3 for 8UC3 (e.g. RGB)
int frameSize=m.rows()*m.cols();
byte[] byteBuffer= new byte[frameSize*numChannels];
m.get(0,0,byteBuffer);
//write to separate R,G,B arrays
byte[][] out=new byte[frameSize][numChannels];
for (int p=0,i = 0; p < frameSize; p++) {
for (int n = 0; n < numChannels; n++,i++) {
out[p][n]=byteBuffer[i];
}
}
return out;
}
The python code I want to recreate :
image = cv2.imread('Images/handheld.jpg')
image = imutils.resize(image, height = 300)
mask = np.zeros(image.shape[:2],np.uint8)
bgModel = np.zeros((1,65),np.float64)
frModel = np.zeros((1,65),np.float64)
height, width, d = np.array(image).shape
rect = (int(width*0.1),int(height*0.1),int(width*0.8),int(height*0.8))
cv2.grabCut(image, mask, rect, bgModel,frModel, 5,cv2.GC_INIT_WITH_RECT)
mask = np.where((mask==2) | (mask == 0),0,1).astype('uint8')
image = image*mask[:,:,np.newaxis]
I have no idea how to convert the last two lines of the python code. If there is a way to just run python clean on an android device within my own project that would also be awesome.
At this point, you should consider talking a look to SL4A project which would allow you run your Python code on Android through java app.
Here are interesting links :
https://github.com/damonkohler/sl4a
https://norwied.wordpress.com/2012/04/11/run-sl4a-python-script-from-within-android-app/
http://jokar-johnk.blogspot.com/2011/02/how-to-make-android-app-with-sl4a.html
Let's see both the commands and try to convert them to Java API calls. It may not be simple 2 line in code.
mask = np.where((mask==2) | (mask == 0),0,1).astype('uint8')
In the above command, we are creating a new image mask which has uint data type of pixel values. The new mask matrix would have value 0 for every position where previous mask has a value of either 2 or 0, otherwise 1. Let's demonstrate this with an example:
mask = [
[0, 1, 1, 2],
[1, 0, 1, 3],
[0, 1, 1, 2],
[2, 3, 1, 0],
]
After this operation the output would be:
mask = [
[0, 1, 1, 0],
[1, 0, 1, 1],
[0, 1, 1, 0],
[0, 1, 1, 0],
]
So this above command is simply generating a binary mask with only 0 and 1 values. This can replicated in Java using Core.compare() method as:
// Get a mask for all `1` values in matrix.
Mat mask1vals;
Core.compare(mask, new Scalar(1), mask1vals, Core.CMP_EQ);
// Get a mask for all `3` values in matrix.
Mat mask3vals;
Core.compare(mask, new Scalar(3), mask3vals, Core.CMP_EQ);
// Create a combined mask
Mat foregroundMask;
Core.max(mask1vals, mask3vals, foregroundMask)
Now you need to multiply this foreground mask with the input image, to get final grabcut image as:
// First convert the single channel mat to 3 channel mat
Imgproc.cvtColor(foregroundMask, foregroundMask, Imgproc.COLOR_GRAY2BGR);
// Now simply take min operation
Mat out;
Core.min(foregroundMask, image, out);
I am trying to write a program where the text is growing so that the larger the number, the larger the font size.
I have an array from 0-9 showing on my screen but struggle to have the growth of the font size.
Does anyone have a hint for me?
My current code is:
int[] numbers = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
void setup() {
size(800, 500);
}
void draw() {
background(28, 130, 42);
for (int i = 0; i < numbers.length; i++) {
textSize(32);
fill(51, 102, 104);
text(""+(i), 100+(i)*50, height/2);
}
}
textSize(32) is hardcoded and not dynamic, do something like this:
for (int i = 0; i < numbers.length; i++) {
textSize(3 * i + 3);
// Your code that draws to screen
}
I've changed 32 to 3 * i + 3 so your textSize will range from 3-30.
Ofcourse this can be anything you want, just make sure it's some calculation based on i, as the loop continues i will increase and fontSize will too.
Try to use this function, where 'fontSize' is the value of the array (times n, depending on how big you want your font).
.setFont(new Font("TimesRoman", Font.PLAIN, fontSize));
I'm trying to visualize the process of solving the travelling salesman problem using the 2-opt-change algorithm but sometimes it seems like the execution of the program gets stuck and the window freezes without any error.
Sometimes even the lines drawn on a canvas disappear.
Some help would be nice.
Here's the drawing code:
int numCities = getNumOfCities();
Order currentOrder = data.getCurrentOrder();
if (newValue) {
GraphicsContext gc = canvasCities.getGraphicsContext2D();
gc.clearRect(0, 0, canvasCities.getWidth(), canvasCities.getHeight());
gc.setStroke(new Color(0, 0, 0, 1));
gc.setLineWidth(1);
gc.setFill((Color.GRAY));
for (int i = 0; i < currentOrder.getSize(); i++) {
City current = data.getCities()[currentOrder.getValue(i)];
City next;
if ((i + 1) > numCities - 1) {
next = data.getCities()[currentOrder.getValue(0)];
} else {
next = data.getCities()[currentOrder.getValue(i + 1)];
}
gc.fillOval(current.getX() - 5, current.getY() - 5, 10, 10);
gc.strokeLine(current.getX(), current.getY(), next.getX(), next.getY());
}
if(!(data.getBestEver()==null)) {
Order best = data.getBestEver();
gc.setStroke(new Color(1, 0, 0, 1));
gc.setLineWidth(3);
gc.setFill((Color.GRAY));
for (int i = 0; i < best.getSize(); i++) {
City current = data.getCities()[best.getValue(i)];
City next;
if ((i + 1) > numCities - 1) {
next = data.getCities()[best.getValue(0)];
} else {
next = data.getCities()[best.getValue(i + 1)];
}
gc.fillOval(current.getX() - 5, current.getY() - 5, 10, 10);
gc.strokeLine(current.getX(), current.getY(), next.getX(), next.getY());
}
}
repaint.set(false); //boolean that indicated if something changed and a repaint is necessairy
}
but sometimes it seems like the execution of the program gets stuck
and the window freezes without any error.
JFX consumes lots of resources...especially with Canvas and Shapes (e.g. recursive Group, etc.) If it got stuck it could be the "stack" was on the brink of overflow. Try with -Xss[nnM] where nn is any number, M stands for Megabytes. Example -Xss16M : Stack with 16MB for JFX threads. If it still behaves "weirdly" then combine -Xss with -Xms (for the heap).
The Problem was the multithreading part. After converting the paint part into a runnable that was invoked using Platform.runLater() it works fine.
Thank's to all who spent time on thinking about my problem.
I have the following code:
GridLayout grid = new GridLayout(3, 3);
grid.addComponent(btnRemove, 0, 0);
grid.addComponent(lblIstMenge, 1, 0);
grid.addComponent(btnAdd, 2, 0);
int i = 0;
if (vList != null && vList.size() > 0)
{
for (VTr component : vList)
{
String transactionTypeName = component.getTransactionTypeName();
transaktionTable.addItem(new Object[]{++transaktionTableCounter + "",
transactionTypeName,
"123123123123123", grid, "Bemerkung^^^"},
transaktionTableCounter);
// System.out.println("Grid: " + grids.get(i));
}
}
Which gives me something like this:
So the grid is added only in the last column. I have tried creating different grids for each column in a list but this did not work for me.
If you have any ideas or recommendations it would be nice.
When I move the instantiation of the buttons and grids inside the for loop it is working as expected.
int i = 0;
if (vList != null && vList.size() > 0)
{
for (VTr component : vList)
{
btnAdd = new Button();
btnAdd.setIcon(new ThemeResource("images/btnIncrease.png"));
btnRemove = new Button();
btnRemove.setIcon(new ThemeResource("images/btnDescrease.png"));
GridLayout grid = new GridLayout(3, 3);
grid.addComponent(btnRemove, 0, 0);
grid.addComponent(lblIstMenge, 1, 0);
grid.addComponent(btnAdd, 2, 0);
String transactionTypeName = component.getTransactionTypeName();
transaktionTable.addItem(new Object[]{++transaktionTableCounter + "", transactionTypeName,
"123123123123123", grid, "Bemerkung^^^"}, transaktionTableCounter);
}
}
I have created a Mesh object containing position, normal, and color information using the following code:
final Vector3[] vertexVectors = this.getVertexVectors();
final short[] indices = this.getIndices();
final Vector3[] vertexNormals = this.getNormals(vertexVectors, indices);
final float[] vertices = new float[vertexVectors.length * 7];
for (int index = 0; index < vertexVectors.length; index++)
{
vertices[(index * 7) + 0] = vertexVectors[index].x;
vertices[(index * 7) + 1] = vertexVectors[index].y;
vertices[(index * 7) + 2] = vertexVectors[index].z;
vertices[(index * 7) + 3] = vertexNormals[index].x;
vertices[(index * 7) + 4] = vertexNormals[index].y;
vertices[(index * 7) + 5] = vertexNormals[index].z;
vertices[(index * 7) + 6] = Color.toFloatBits(0, 255, 0, 255);
}
final Mesh mesh = new Mesh(true, vertices.length / 3, indices.length, new VertexAttribute(Usage.Position, 3, "a_position"), new VertexAttribute(Usage.Normal, 3, "a_normal"), new VertexAttribute(Usage.ColorPacked, 4, "a_color"));
mesh.setVertices(vertices);
mesh.setIndices(indices);
return mesh;
I then create a Model object and a ModelInstance object from the mesh with this code:
private Model model;
private ModelInstance instance;
final ModelBuilder modelBuilder = new ModelBuilder();
modelBuilder.begin();
modelBuilder.part("0", this.getCustomMesh(), GL10.GL_TRIANGLES, new Material());
this.model = modelBuilder.end();
this.instance = new ModelInstance(this.model);
I render the ModelInstance using this code:
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
this.modelBatch.begin(this.cam);
this.modelBatch.render(this.instance, this.lights);
this.modelBatch.end();
My problem is that the model has no color. The model should be green per the vertex colors but as long as I use lighting, the model appears white. If i remove the lighting, the model appears green as expected (but without the pretty shading). I have tried adding Gdx.gl.glEnable(GL10.GL_COLOR_MATERIAL); to my constructor per this question, but this only makes the model appear brighter but still white. What other settings are needed for my model to render the vertex colors with lighting?
It seems adding ColorAttribute.createDiffuse to the model's material fixes the issue. This means changing this line:
modelBuilder.part("0", this.getCustomMesh(), GL10.GL_TRIANGLES, new Material());
with this:
modelBuilder.part("0", this.getCustomMesh(), GL10.GL_TRIANGLES, new Material(ColorAttribute.createDiffuse(0, 0, 0, 0)));
I do not fully understand why and as an added curiosity, the values passed to the createDiffuse method seem to have no noticeable affect on the model.