Android EditText align actual text to onDraw positions? - java

When overriding onDraw in an edittext, I can make the text in any location I see fit. What I'm having trouble with is aligning their respective "boxes" ie the area where the actual char is clicked to edit said char. The text I can move, or rather draw wherever, but how do I align the actual physical position of the char to it's painted char? I'm at a bit of a loss I couldn't figure out what to override to change these positions. Any help would be awesome! I don't have any code to post, as it's literally just an extended edittext and override onDraw with a for loop.
I attempted to move the position of text digits individually inside an edit text using onDraw, but found it doesn't seem to move the actual location just the paint representation of it.
EDIT ----------------------------------
Ive found what I believe to be what needs overriden, the onSelection start and end. But, it seems to cause an infinite recursion loop? Anyone got any ideas as to how this is done? Indentation is hard to do on a phone but here's a sample of the rough idea:
class HexEditorEditText(context: Context, attrs: AttributeSet?) : EditText(context, attrs) {
private val characterWidth: Float
private val characterHeight: Float
init {
val textPaint = paint
characterWidth = textPaint.measureText("00")
characterHeight = textPaint.fontSpacing
}
override fun onDraw(canvas: Canvas?) {
val text = text.toString()
var x = 0f
var y = 0f
for (i in 0 until text.length step 2) {
val pair = text.substring(i, i + 2)
canvas?.drawText(pair, x, y, paint)
x += characterWidth + characterWidth / 2
}
}
override fun getSelectionStart(): Int {
val layout = layout
val line = layout.getLineForOffset(selectionStart)
val x = layout.getPrimaryHorizontal(selectionStart)
return (x / (characterWidth + characterWidth / 2)).toInt() * 2
}
override fun getSelectionEnd(): Int {
val layout = layout
val line = layout.getLineForOffset(selectionEnd)
val x = layout.getPrimaryHorizontal(selectionEnd)
return (x / (characterWidth + characterWidth / 2)).toInt() * 2
}
override fun setSelection(start: Int, stop: Int) {
super.setSelection((start / 2) * 2, (stop / 2) * 2)
}
}

Related

How to remove a part of Spannable

In my android app user can change text foreground and background colors by selecting text, everything works fine, but if user created red foreground for text Hello World, and wants to remove red foreground in the World word, code removes the whole sentence background, if there's a way to do it, please, help me, here is the function:
private fun changeTextForegroundColor(
#ColorInt foregroundColor: Int?,
startPosition: Int,
endPosition: Int
) {
val resultText: Spannable = bindingContent.contentEditText.text.toSpannable()
//Get foreground spans and remove them.
val spans: Array<ForegroundColorSpan> = resultText.getSpans(
startPosition, endPosition,
ForegroundColorSpan::class.java)
repeat(spans.count()) {
resultText.removeSpan(spans[it])
}
// If foregroundColor == null, then just remove the span, what we do above
if (foregroundColor != null) {
resultText.setSpan(
ForegroundColorSpan(foregroundColor),
startPosition,
endPosition,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
bindingContent.contentEditText.setText(resultText)
}
I tried not to remove, but to replace the foreground color with white(my text color) but it causes lags in my app, so I need to remove a word World, without removing the Hello word foreground in my span.
Make sure that you are using integer colors and not color ids in your code. You, of course, could use the color ids but you will have to make the conversion to a color integer. The following demonstrates this.
val newColor =
ResourcesCompat.getColor(resources, android.R.color.holo_blue_light, null)
changeTextForegroundColor(newColor, 0, 5)
Then in changeTextForgroundColor()
// If foregroundColor == null, then just remove the span, what we do above
if (foregroundColor != null) {
resultText.setSpan(
ForegroundColorSpan(foregroundColor),
startPosition,
endPosition,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
Finally, I wrote code that as I guess can resolve my problem, here I remove spans if span within selection (It won't remove span which range outside of selection), then check if color is null, then set color to white, else to another one. This code helps to to avoid issue when one span was painted over another one:
private fun changeTextForegroundColor( #ColorInt foregroundColor: Int?, startPosition: Int, endPosition: Int) {
val resultText: Spannable =
noteBinding.contentActivityInclude.contentEditText.text?.toSpannable() ?: return
val spans: Array<ForegroundColorSpan> = resultText.getSpans(
startPosition, endPosition,
ForegroundColorSpan::class.java)
spans.forEach {
val selectedSpanStart = resultText.getSpanStart(it)
val selectedSpanEnd = resultText.getSpanEnd(it)
if (selectedSpanStart >= startPosition && selectedSpanEnd <= endPosition) {
resultText.removeSpan(it)
}
}
if (foregroundColor == null) {
resultText.setSpan(
ForegroundColorSpan(Color.WHITE),
startPosition,
endPosition,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
} else {
resultText.setSpan(
ForegroundColorSpan(foregroundColor),
startPosition,
endPosition,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
noteBinding.contentActivityInclude.contentEditText.setText(resultText)
}

How to draw a grid out of grid squares seamlessly in JavaFX

I'm been writing maze algorithms, and want to draw the mazes generated using JavaFX.
To begin with, I'm attempting to draw a simple grid - but made up of smaller shapes so that I'll later be able to change the shape of the grid into that of a maze.
I'm using little upper left corner shapes (like ┏) and a GridPane, but this is leading to small discontinuities between the cells. (screenshot and code below). How can I wedge these shapes together seamlessly? Am I barking up the wrong tree with the Gridpane idea?
attempted grid drawing so far
My code, below, is actually written in Scala; I'm using ScalaFX, but finding help for ScalaFX online is a nightmare so I've been going solely off JavaFX docs - they are pretty much the same thing as far as I've gathered.
val lineLength: Int = 30
def makeClosedCell(length: Int = lineLength): Shape = {
val wallN = Line(0,0,length,0)
val wallW = Line(0,0,0,length)
val closedCell: Shape = Shape.union(wallN, wallW)
return closedCell
}
def makeOpenW(length: Int = lineLength): Shape = Line(0,0,length,0)
def makeOpenN(length: Int = lineLength): Shape = Line(0,0,0,length)
def initialiseGrid(r: GridPane, sizex: Int, sizey: Int): Unit = {
for (i <- 0 until sizex) {
val colConst = new ColumnConstraints()
colConst.setPercentWidth(100.0 / sizex)
r.getColumnConstraints().add(colConst)
}
for (i <- 0 until sizey) {
val rowConst = new RowConstraints()
rowConst.setPercentHeight(100.0 / sizey)
r.getRowConstraints().add(rowConst)
}
for(j <- sizey-1 to 0 by -1){
for(i <- 0 until sizex){
r.add(makeClosedCell(),i,j)
}
r.add(makeOpenN(),sizex,j)
}
for(i <- 0 until sizex){
r.add(makeOpenW(),i,sizey)
}
}
Just found a solution. I've found this can be solved by fixing the exact column and row widths and heights to the same value as the line lengths, like so:
val rowConst = new RowConstraints()
// remove: rowConst.setPercentHeight(100.0 / sizey)
rowConst.setMinHeight(length)
rowConst.setMaxHeight(length)
r.getRowConstraints().add(rowConst)
With length passed into the initialisation function, of course.

Merge multiple image into one image with OpenCV

I tried searching for this quite a lot and cant seem to figure it out. All of the threads I read shows that everyone is doing the same thing. But it doesn't seem to work for me.
I want to create a new image and add two images to this created image.
For this I'm generating a Mat.zeros onto which I will be adding my two images(lets say A and B. I'll call the zeros Mat G).
I scale down the images A and B.
I create an ROI on G with the size of A and then copy A to G.submat.
I create an ROT on G with size B and then copy B to G.submat.
When I finally save G, all I get is a black image( because I generated G as Mat.zeros )
. I'm guessing copyTo is not really copying the content from A to G on the ROI of G.
I'm not sure what I'm doing wrong.
I'm using Kotlin which compiles down to Java.
#PostConstruct
fun init() {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
#Test
fun `merge image`() {
val frameLeft = Imgcodecs.imread("/home/nischit/Downloads/potato1.jpg")
val frameRight = Imgcodecs.imread("/home/nischit/Downloads/potato2.jpg")
val frameOutput = Mat.zeros(1920, 1080, frameLeft.depth())
println(frameLeft.depth())
println(frameRight.depth())
println(frameOutput.depth())
val tempLeft = Mat()
val tempRight = Mat()
scaleFrame(frameLeft, tempLeft)
scaleFrame(frameRight, tempRight)
println("tempLeft: ${tempLeft.cols()},${tempLeft.rows()}")
println("tempRight: ${tempRight.cols()},${tempRight.rows()}")
tempLeft.copyTo(frameOutput.submat(Rect(10, 10, tempLeft.cols(), tempLeft.rows())))
tempRight.copyTo(frameOutput.submat(Rect(10, 500, tempRight.cols(), tempRight.rows())))
saveImage(frameOutput, 2)
}
fun scaleFrame(frame: Mat, out: Mat, maxWidth: Int = MAX_WIDTH, maxHeight: Int = MAX_HEIGHT) {
val isFrameInPortraitMode = frame.height() > frame.width()
val scale = if (isFrameInPortraitMode) {
(maxHeight * 1.0 / frame.height())
} else {
(maxWidth * 1.0 / frame.width())
}
Imgproc.resize(frame, out, Size(frame.width() * scale, frame.height() * scale))
}
fun saveImage(frame: Mat, i: Int = 0) {
Imgcodecs.imwrite("/home/nischit/Downloads/potatoGenerated$i.jpg", frame)
}
Okay, the problem was with channels in the blank image I had created i.e frameOutput. All of the inputs have 3 channels while this matrix had 1 channel as I was not initializing it correctly.
Replacing
val frameOutput = Mat.zeros(1920, 1080, frameLeft.depth()) with val frameOutput = Mat.zeros(Size(1920.0, 1080.0), CvType.CV_8UC3) worked.

Modify margin params in code [duplicate]

Can this attribute be changed dynamically in Java code?
android:layout_marginRight
I have a TextView, that has to change its position some pixels to the left dynamically.
How to do it programmatically?
EDIT: A more generic way of doing this that doesn't rely on the layout type (other than that it is a layout type which supports margins):
public static void setMargins (View v, int l, int t, int r, int b) {
if (v.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
p.setMargins(l, t, r, b);
v.requestLayout();
}
}
You should check the docs for TextView. Basically, you'll want to get the TextView's LayoutParams object, and modify the margins, then set it back to the TextView. Assuming it's in a LinearLayout, try something like this:
TextView tv = (TextView)findViewById(R.id.my_text_view);
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)tv.getLayoutParams();
params.setMargins(0, 0, 10, 0); //substitute parameters for left, top, right, bottom
tv.setLayoutParams(params);
I can't test it right now, so my casting may be off by a bit, but the LayoutParams are what need to be modified to change the margin.
NOTE
Don't forget that if your TextView is inside, for example, a
RelativeLayout, one should use RelativeLayout.LayoutParams instead of
LinearLayout.LayoutParams
Update: Android KTX
The Core KTX module provides extensions for common libraries that are part of the Android framework, androidx.core.view among them.
dependencies {
implementation "androidx.core:core-ktx:{latest-version}"
}
The following extension functions are handy to deal with margins:
Note: they are all extension functions of MarginLayoutParams, so
first you need to get and cast the layoutParams of your view:
val params = (myView.layoutParams as ViewGroup.MarginLayoutParams)
setMargins() extension function:
Sets the margins of all axes in the ViewGroup's MarginLayoutParams. (The dimension has to be provided in pixels, see the last section if you want to work with dp)
inline fun MarginLayoutParams.setMargins(#Px size: Int): Unit
// E.g. 16px margins
params.setMargins(16)
updateMargins() extension function:
Updates the margins in the ViewGroup's ViewGroup.MarginLayoutParams.
inline fun MarginLayoutParams.updateMargins(
#Px left: Int = leftMargin,
#Px top: Int = topMargin,
#Px right: Int = rightMargin,
#Px bottom: Int = bottomMargin
): Unit
// Example: 8px left margin
params.updateMargins(left = 8)
updateMarginsRelative() extension function:
Updates the relative margins in the ViewGroup's MarginLayoutParams (start/end instead of left/right).
inline fun MarginLayoutParams.updateMarginsRelative(
#Px start: Int = marginStart,
#Px top: Int = topMargin,
#Px end: Int = marginEnd,
#Px bottom: Int = bottomMargin
): Unit
// E.g: 8px start margin
params.updateMargins(start = 8)
The following extension properties are handy to get the current margins:
inline val View.marginBottom: Int
inline val View.marginEnd: Int
inline val View.marginLeft: Int
inline val View.marginRight: Int
inline val View.marginStart: Int
inline val View.marginTop: Int
// E.g: get margin bottom
val bottomPx = myView1.marginBottom
Using dp instead of px:
If you want to work with dp (density-independent pixels) instead of px, you will need to convert them first. You can easily do that with the following extension property:
val Int.px: Int
get() = (this * Resources.getSystem().displayMetrics.density).toInt()
Then you can call the previous extension functions like:
params.updateMargins(start = 16.px, end = 16.px, top = 8.px, bottom = 8.px)
val bottomDp = myView1.marginBottom.dp
Old answer:
In Kotlin you can declare an extension function like:
fun View.setMargins(
leftMarginDp: Int? = null,
topMarginDp: Int? = null,
rightMarginDp: Int? = null,
bottomMarginDp: Int? = null
) {
if (layoutParams is ViewGroup.MarginLayoutParams) {
val params = layoutParams as ViewGroup.MarginLayoutParams
leftMarginDp?.run { params.leftMargin = this.dpToPx(context) }
topMarginDp?.run { params.topMargin = this.dpToPx(context) }
rightMarginDp?.run { params.rightMargin = this.dpToPx(context) }
bottomMarginDp?.run { params.bottomMargin = this.dpToPx(context) }
requestLayout()
}
}
fun Int.dpToPx(context: Context): Int {
val metrics = context.resources.displayMetrics
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), metrics).toInt()
}
Then you can call it like:
myView1.setMargins(8, 16, 34, 42)
Or:
myView2.setMargins(topMarginDp = 8)
Use LayoutParams (as explained already). However be careful which LayoutParams to choose. According to https://stackoverflow.com/a/11971553/3184778 "you need to use the one that relates to the PARENT of the view you're working on, not the actual view"
If for example the TextView is inside a TableRow, then you need to use TableRow.LayoutParams instead of RelativeLayout or LinearLayout
Use This function to set all Type of margins
public void setViewMargins(Context con, ViewGroup.LayoutParams params,
int left, int top , int right, int bottom, View view) {
final float scale = con.getResources().getDisplayMetrics().density;
// convert the DP into pixel
int pixel_left = (int) (left * scale + 0.5f);
int pixel_top = (int) (top * scale + 0.5f);
int pixel_right = (int) (right * scale + 0.5f);
int pixel_bottom = (int) (bottom * scale + 0.5f);
ViewGroup.MarginLayoutParams s = (ViewGroup.MarginLayoutParams) params;
s.setMargins(pixel_left, pixel_top, pixel_right, pixel_bottom);
view.setLayoutParams(params);
}

JavaFX ScrollPane programmatically moving the viewport - centering content

I can use the setVvalue(double) and setHvalue(double) methods to move a viewport in a JavaFX ScrollPane. What I'm struggling to do is center a particular node in the content of the scroll pane based on its position. I've tried all sorts of combos of localToScene() and boundsInParent(). I've read (a lot) and seen this example
How to scroll to make a Node within the content of a ScrollPane visible?
Which is close but doesn't center the objects just puts them visible. Having the built in mouse panning is brilliant but I'm making heavy weather of the programmatic panning.
Ultimately I need to be able to do a zoom too so I have put the actual shapes in a Group and added the group to the scroll pane content. I think I'm supposed to do the zooming on the group and again I need to be able to zoom around the center of the group so we are back to manipulating and identifying the current center position. Any pointers or examples that can be provided would be really really appreciated. The code sample in the link above is a good SSCCE.
Thanks in advance,
Andy
I'll try explaining this without code, because I don't think that's necessary here.
Let's say the content of your scrollpane has height h, and the height of the viewport is v. If h = v, then the content would fit perfectly into the viewport, and you would need no scrollbar. In this situation (with a non-movable scrollbar), for an element to be centered, it would need to be positioned at the center of the scrollpane's content. You can't move it to the viewport's center by means of scrolling.
Now consider h to be twice the size of v (i.e. h = 2v). In this situation the upper 1/4 and the lower 1/4 of the scrollpane's content could not be centered by scrolling.
(If you absolutely needed to center any component by scrolling you should consider padding your content pane, but we'll continue with the unpadded solution here)
When you think about it, you'll realize the possible scrollable distance of the scrollbar is h - v, and you will scroll that amount by setting vvalue to 1.0.
To center a point y (here the point y is a coordinate of the scrollpane's content pane) you can use the following vvalue:
vvalue = (y - 0.5 * v) / (h - v)
The nominator of this expression is the y-coordinate of what is shown at the top of the viewport when the point y is centered inside the viewport. The denominator is the total scrollable distance.
Edit: Adding some code anyway!
public void centerNodeInScrollPane(ScrollPane scrollPane, Node node) {
double h = scrollPane.getContent().getBoundsInLocal().getHeight();
double y = (node.getBoundsInParent().getMaxY() +
node.getBoundsInParent().getMinY()) / 2.0;
double v = scrollPane.getViewportBounds().getHeight();
scrollPane.setVvalue(scrollPane.getVmax() * ((y - 0.5 * v) / (h - v)));
}
(Please note this assumes that the node is a direct child of the scrollpane's contentpane)
Hope this helps! :)
I know, it is little bit late, but maybe someone will find it useful.:)
As regards centering, this is not hard task it takes only little bit of math. You need two things. Size of whole content of ScrollPane (width and height of scrollable area, not only of frame with scrollbars, but complete as if no scrollbars are present) and position of centered object inside of ScrollPane content.
You get size of content like this: scrollPane.getContent().getBoundsInLocal().getHeight() or .getWidth()
And position of your object: node.getBoundsInParent().getMinY() or .getMinX() - minimal position of object. You can get its height and width too.
Then calculate position of your center and move scrollbars according this formula.
double vScrollBarPosition = scrollPane.getVMax() * (yPositionOfCenter / heightOfScrollPaneContent)
Here is an example on how to center a pic that has been rotated.
The code is in Scala and therefore applicable to ScalaFX, but JavaFX and Java developers will be able to read/use it :
def turnPic(angle:Int) {
val virtualSurfaceMultiplier = 8.0;
currentAngle = ( floor((currentAngle % 360) / 90) * 90 + angle + 360 ) % 360;
imageView.rotate = currentAngle;
val orientationSwitched = (currentAngle / 90) % 2 > 0;
val width= scrollPane.getViewportBounds().getWidth();
val height= scrollPane.getViewportBounds().getHeight();
val viewPort = new Rectangle2D(0, 0, image.width.value, image.height.value);
imageView.setViewport(viewPort);
imageView.fitHeight.value = virtualSurfaceMultiplier * height;
imageView.fitWidth.value = 0 //virtualSurfaceMultiplier * width;
def centerV(picHeight:Double) {
val node = scrollPane.getContent();
val totalHeight = scrollPane.getContent().getBoundsInLocal().getHeight();
val offsetToCenter = (node.getBoundsInParent().getMaxY() + node.getBoundsInParent().getMinY()) / 2.0;
val viewportWidth = scrollPane.getViewportBounds().getHeight();
val res = (scrollPane.getVmax() * ((offsetToCenter - 0.5 * picHeight * zoomFactor) / (totalHeight - picHeight * zoomFactor)));
scrollPane.setVvalue(res);
}
def centerH(picWidth:Double) {
val node = scrollPane.getContent();
val totalWidth = scrollPane.getContent().getBoundsInLocal().getWidth();
val offsetToCenter = (node.getBoundsInParent().getMaxX() + node.getBoundsInParent().getMinX()) / 2.0;
val viewportWidth = scrollPane.getViewportBounds().getWidth();
val res = (scrollPane.getHmax() * ((offsetToCenter - 0.5 * picWidth * zoomFactor) / (totalWidth - picWidth * zoomFactor)));
scrollPane.setHvalue(res);
System.err.println(s"trace centerH> $FILE:$LINE:" + " totalWidth:" + totalWidth + " offsetToCenter=" + offsetToCenter + " vWidth=" + viewportWidth + "res=" + res);
}
if (orientationSwitched) {
zoomFactor = 1.0 / (virtualSurfaceMultiplier * image.width.value / image.height.value);
centerH(height);
centerV(width);
}
else
{
zoomFactor = 1.0 / virtualSurfaceMultiplier;
centerH(width);
centerV(height);
}
imageView.setScaleX(zoomFactor);
imageView.setScaleY(zoomFactor);
}

Categories