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)
}
Related
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)
}
}
How to create BottomNavigation with one of the item is larger than the parent, but without using floatingActionButton. For example like this:
I tried to do that by wrapping the icon with Box but it get cut like this:
Then i try to separate that one button and use constraintLayout to position it, but the constraintLayout cover the screen like this. Even when i color it using Color.Transparent, it always feels like Color.White (i dont know why Color.Transparent never work for me). In this picture i give it Red color for clarity reason.
So how to do this kind of bottomNavBar without having to create heavy-custom-composable?
Update: so i try to make the code based on MARSK and Dharman comment (thanks btw). This is what i
BoxWithConstraints(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.background(Color.Transparent)
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
.background(Color.White)
.align(Alignment.BottomCenter)
)
Row(
modifier = Modifier
.zIndex(56.dp.value)
.fillMaxWidth()
.selectableGroup(),
horizontalArrangement = Arrangement.SpaceBetween,
) {
items.forEach { item ->
val selected = item == currentSection
BottomNavigationItem(
modifier = Modifier
.align(Alignment.Bottom)
.then(
Modifier.height(
if (item == HomeSection.SCAN) 84.dp else 56.dp
)
),
selected = selected,
icon = {
if (item == HomeSection.SCAN) {
ScanButton(navController = navController, visible = true)
} else {
ImageBottomBar(
icon = if (selected) item.iconOnSelected else item.icon,
description = stringResource(id = item.title)
)
}
},
label = {
Text(
text = stringResource(item.title),
color = if (selected) Color(0xFF361DC0) else LocalContentColor.current.copy(
alpha = LocalContentAlpha.current
),
style = TextStyle(
fontFamily = RavierFont,
fontWeight = if (selected) FontWeight.Bold else FontWeight.Normal,
fontSize = 12.sp,
lineHeight = 18.sp,
),
maxLines = 1,
)
},
onClick = {
if (item.route != currentRoute && item != HomeSection.SCAN) {
navController.navigate(item.route) {
launchSingleTop = true
restoreState = true
popUpTo(findStartDestination(navController.graph).id) {
saveState = true
}
}
}
}
)
}
}
}
It works in preview, but doesn't work when i try in app.
This one in the preview, the transparent working as expected:
And this is when i try to launch it, the transparent doesnt work:
Note: I assign that to bottomBar of Scaffold so i could access the navigation component. Is it the cause that Transparent Color doesnt work?
Update 2: so the inner paddingValues that makes the transparent doesnt work. I fixed it by set the padding bottom manually:
PaddingValues(
start = paddingValues.calculateStartPadding(
layoutDirection = LayoutDirection.Ltr
),
end = paddingValues.calculateEndPadding(
layoutDirection = LayoutDirection.Ltr
),
top = paddingValues.calculateTopPadding(),
bottom = SPACE_X7,
)
Custom Composable are not heavy, really.
Anyway, try this:-
Create a Container of MaxWidth (maybe a BoxWithConstraints or something), keep its background transparent, set the height to wrap content. Create the tabs as usual, but keeping the bigger tab's icon size bigger explicitly using Modifier.size(Bigger Size).
After you have this setup, add another container inside this container with white background, covering a specific height of the original container. Let's say 60%
Now set the z-index of all the icons and tabs to higher than the z-index of this lastly added container. Use Modifier.zIndex for this. And viola, you have your Composable ready.
In order to set a specific percentage height of the inner container, you will need access to the height of the original container. Use BoxWithConstraints for that, or just implement a simple custom Layout Composable
I'm trying to display another image with 1 button and go back to previous image with same button.
private void button2ActionPerformed(java.awt.event.ActionEvent evt)
{
if (labelIcon == labelIcon)
centerLabel.setIcon(labelicon2);
if (labelIcon == labelicon2)
centerLabel.setIcon(labelIcon);
}
I'm stuck with if changing only to another image and not going back. Do I need to get label properties somehow (don't know how) to execute second if statement or i need some kind of loop?
I can do it with 2 buttons but I think it can be done with 1. Am I wrong?
Add an int that keeps track of which image you are on, then if it's on 1, you change to 2, if it's on 2, you change to 1.
i.e.
int imageNumber = 1;
...
if (imageNumber == 1)
{
//change image to image 2
//also change imageNumber to 2
}
else if (imageNumber == 2)
{
//change image to image 1
//also change imageNumber to 1
}
if (labelIcon == labelIcon)
this code will always return TRUE becouse you are comparing the same object.
I think you ment something like
Icon labelIcon = centerLabel.getIcon();
if (labelIcon == this.labelIcon)
centerLabel.setIcon (labelicon2);
if (labelIcon == this.labelicon2)
centerLabel.setIcon(labelIcon);
}
Since the previous three answers all have their faults, here's mine. Use a boolean to check if the button has been clicked.
private boolean clicked = false;
private void button2ActionPerformed(ActionEvent evt) { //importing stuff is always nice...
if (!clicked) {
centerLabel.setIcon (labelicon2);
clicked = true;
} else {
centerLabel.setIcon(labelIcon);
clicked = false;
}
}
I am creating a text editor using a JTextPane that allows the user to change the color of selected text. But when the user selects the text, then chooses the option to change the color (say, to red) the text does not appear as red until the text is unselected. I tried using setSelectedTextColor to change the color of the selected text, but that doesn't work since that changes the text to red anytime text is selected afterwards. Is there a way to have selected text show up as it's actual color? Or like the way it works in Word where it's not the actual color of the text, but when text of different colors are selected they show up as different colors even when selected.
I use the following code to set up the JTextPane and button that changes the selected text to red:
JButton redButton = new JButton(new StyledEditorKit.ForegroundAction("red", Color.RED));
redButton.setFocusable(false);
buttonPanel.add(redButton);
The JTextPane is set up as with content type HTML and uses the HTMLEditorKit:
p=new JTextPane();
p.setSize(300, 300);
kit = new HTMLEditorKit();
p.setEditorKit(kit);
p.setDocument(kit.createDefaultDocument());
p.setContentType("text/html");
p.setEditable(true);
Let me know if you need more source code to understand the question. Thank You!
Take a look at the DefaultHighlightPainter inner class of DefaultHighlighter.
The method
public void paint(Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c) {
Rectangle alloc = bounds.getBounds();
try {
// --- determine locations ---
TextUI mapper = c.getUI();
Rectangle p0 = mapper.modelToView(c, offs0);
Rectangle p1 = mapper.modelToView(c, offs1);
// --- render ---
Color color = getColor();
if (color == null) {
g.setColor(c.getSelectionColor());
}
else {
g.setColor(color);
}
As you can see it uses either getColor() or getSelectionColor(). You can extend the class and adapt the highlight painting.
Or use a simpler approach to override your JTextPane's getSelectionColor(). In the method just check whether text is selected and use attributes of selected elements to get desired ccolor. If nothing is selected just return super.getSelectedColor()
UPDATE:
Actually applying colors for selection is used on low level GlyphView's
public void paint(Graphics g, Shape a) {
...
JTextComponent tc = (JTextComponent) c;
Color selFG = tc.getSelectedTextColor();
if (// there's a highlighter (bug 4532590), and
(tc.getHighlighter() != null) &&
// selected text color is different from regular foreground
(selFG != null) && !selFG.equals(fg)) {
Highlighter.Highlight[] h = tc.getHighlighter().getHighlights();
if(h.length != 0) {
boolean initialized = false;
int viewSelectionCount = 0;
for (int i = 0; i < h.length; i++) {
Highlighter.Highlight highlight = h[i];
int hStart = highlight.getStartOffset();
int hEnd = highlight.getEndOffset();
if (hStart > p1 || hEnd < p0) {
// the selection is out of this view
continue;
}
if (!SwingUtilities2.useSelectedTextColor(highlight, tc)) {
continue;
}
...
As you can see applying selection color vs default color of the view is defined in the SwingUtilities2.useSelectedTextColor(highlight, tc)
In the sources http://kickjava.com/src/com/sun/java/swing/SwingUtilities2.java.htm
public static boolean useSelectedTextColor(Highlighter.Highlight JavaDoc h, JTextComponent JavaDoc c) {
Highlighter.HighlightPainter JavaDoc painter = h.getPainter();
String JavaDoc painterClass = painter.getClass().getName();
if (painterClass.indexOf("javax.swing.text.DefaultHighlighter") != 0 &&
painterClass.indexOf("com.sun.java.swing.plaf.windows.WindowsTextUI") != 0) {
return false;
}
try {
DefaultHighlighter.DefaultHighlightPainter JavaDoc defPainter =
(DefaultHighlighter.DefaultHighlightPainter JavaDoc) painter;
if (defPainter.getColor() != null &&
!defPainter.getColor().equals(c.getSelectionColor())) {
return false;
}
} catch (ClassCastException JavaDoc e) {
return false;
}
return true;
}
So using the color depends on L&F and painter. If you define your onw painter the color won't be used.
It sounds like you may be using something other than a font family name. I re-factored this example to use JTextPane and saw the expected result. As noted there, the actions require a font family name, e.g. the default or face=SansSerif, as specified by the FontFamilyAction class nested in StyledEditorKit.
JTextPane textPane = new JTextPane();
Simplest way to change the color of selected Text :
int start = textPane.getSelectionStart();
int end = textPane.getSelectionEnd();
int selectedLength = end - start;
StyleDocument style = pane.getStyledDocument();
//this give your attribute set of selected Text.
AttributeSet oldSet = style.getCharacterElement(end-1).getAttributes();
//StyleContext for creating attribute set
StyleContext sc = StyleContext.getDefaultStyleContext();
// Attribute set which contains new color with old attributes
AttributeSet s = sc.addAttribute(oldSet, StyleConstants.Foreground, Color.RED);
//This set the color of the Text
style.setCharacterAttributes(start, selectedLength, s, true);
Adding my view. This could be further simple then above approaches.
JEditorPane ep = new JEditorPane() {
#Override
public Color getSelectionColor() {
return COLOR_YOU_WANT;
}
#Override
public Color getSelectedTextColor() {
return COLOR_YOU_WANT;
}
};
i would extend the BasicTabbedPaneUi so i can design my own tabPane. I have one problem with the html text, is to set the color of the text once the tab is selected. I override the paintText method with the following, almost the same code as the orignal method:
#Override
protected void paintText(Graphics g, int tabPlacement, Font font, FontMetrics metrics, int tabIndex, String title, Rectangle textRect, boolean isSelected) {
g.setFont(font);
View v = getTextViewForTab(tabIndex);
if (v != null) {
// html
Color fg = tabPane.getForegroundAt(tabIndex);
if (isSelected && (fg instanceof UIResource)) {
Color selectedFG = UIManager.getColor(
"TabbedPane.selectedForeground");
if (selectedFG != null) {
fg = selectedFG;
}
}
v.paint(g, textRect);
} else {
// plain text
int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
if (tabPane.isEnabled() && tabPane.isEnabledAt(tabIndex)) {
Color fg = tabPane.getForegroundAt(tabIndex);
if (isSelected && (fg instanceof UIResource)) {
Color selectedFG = UIManager.getColor(
"TabbedPane.selectedForeground");
if (selectedFG != null) {
fg = selectedFG;
}
}
g.setColor(fg);
SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
title, mnemIndex,
textRect.x, textRect.y + metrics.getAscent());
} else { // tab disabled
g.setColor(tabPane.getBackgroundAt(tabIndex).brighter());
SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
title, mnemIndex,
textRect.x, textRect.y + metrics.getAscent());
g.setColor(tabPane.getBackgroundAt(tabIndex).darker());
SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
title, mnemIndex,
textRect.x - 1, textRect.y + metrics.getAscent() - 1);
}
}
}
In the case we have html text for the tab, it v is not null. If i set a color to the graphics object used in the paint method it does not change the text color.
I use html because i want to have my tab's text on two lines.
Thanks for helping changing the color.
I would be reluctant to develop a custom TabbedPaneUI unless it were part of a complete Look & Feel implmentation.
Instead, consider a custom tab component, as shown in TabComponentsDemo and discussed in How to Use Tabbed Panes. This will give you absolute control over the component's appearance, without sacrificing compatibility with the user's chosen Look & Feel.