Are there any alternatives to adjustsFontSizeToFit for Android? - android

I am using flex in Text field to display my values/range. For IOS I am using the properties adjustsFontSizeToFit and minimumFontScale props for Text to achieve the ideal font size and scaling. I want the same scaling to be enabled for Android. Does anyone use any tricks for Android?
<View style={{flex: 40}}>
{
(Platform.OS === 'ios') ?
<Text style={{flex: 1, paddingRight: 2}} adjustsFontSizeToFit
minimumFontScale={0.5}>
//if decimal value exists show range
(pointFivePart_1 === '5') ?
<Text style={{ flexDirection: 'row' }} >
<Text style={styles.mainValueStyle}>{first_1}</Text>
<Text style=styles.decimalValueStyle}> .pointFivePart_1}</Text>
<Text style={styles.mainValueStyle}>°</Text>
</Text>
: <Text style={styles.mainValueStyle} >{min}°</Text>
}
//if two values then show the second value as range
{// render largest value if different than min
(maxSet === minSet) ? null : (
(pointFivePart_2 === '5') ?
<Text style={{ flexDirection: 'row' }} >
<Text style={styles.mainValueStyle}> - {first_2}</Text>
<Text style={styles.decimalValueStyle}>.{pointFivePart_2}</Text>
<Text style={styles.mainValueStyle}>°</Text>
</Text>
:
<Text style={styles.mainValueStyle} > - {maxSet}°</Text>
)
}
</Text>
:
//same styling for android

Simple but somehow working solution (crossplatform):
fontSize = Math.sqrt(textWidth*textHeight/text.length)
where textWidth, textHeight - size of your Text component, text.length - length of your text.
minimumFontScale can be achieved with
fontSize = Math.max(fontSize,minimumFontSize)
I came to this formula by solving such system of equations:
lettersInLine = textWidth/fontSize
lines = textLength/lettersInLine
lines*fontSize = textHeight
assuming here that font has square sized (width equal to height) letters.
Though this formula makes sense even without equations. You just
calculate square of whole text element
calculate square for each letter (dividing whole square to letters number)
take square root from letter square to get font size

Simply set adjustFontSizeToFit and numberOfLines
DONT SET fontSize
<Text adjustsFontSizeToFit numberOfLines={1}>This text will fit the width of the container</Text>

I am using slightly modified version of #nazar solution. I was not able to make it scale as i needed, text would get cut off, because the dynamic height based on screen device would affect the fontSize too much.
Anyways, here is my solution.
var fontSize = width / text.length;
var maxSize = width / 5;
fontSize = Math.min(fontSize, maxSize);
Maximum font size is defined by 5 letters. Less letters would make too big fontSize in this case. This value can be of course anything. You can make simple function out of this and call it anywhere you need with width parameter, which is idealy something like screen.width * 0.1, second parameter being length of the text and third parameter for the maxSize. So something like this:
export function GetFontSize(width, length, max) {
var fontSize = width / length;
var maxSize = width / max;
fontSize = Math.min(fontSize, maxSize);
return fontSize;
}

#aaltaf In android I generally define dimension of TextView into dimension file inside values and for font generally use default font from fontfamily
here is an example...
<TextView
android:id="#+id/shipment_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/send_two_dp"
android:gravity="center_vertical"
android:text="#{locationViewModel.sameDayShipmentText}"
android:textColor="#color/black"
android:textSize="#dimen/send_fourteen_sp"
android:fontFamily="sans-serif"
tools:text="6-8 hour delivery" />
Hope it will helped :)

Related

Jetpack compose IntrinsicSize.Min cuts off Text - want to force Text to take up exact height and minimum width required for that height

I have a Text component that I want the width to expand as big as it wants to accommodate the supplied text and to be exactly 2 lines tall.
I have attempted to implement this by calculating the height using my font size and the number of lines that I want:
val textHeight = textStyle.lineHeight.value * 2
Then, based on various articles (such as this one https://www.answertopia.com/jetpack-compose/jetpack-compose-intrinsicsize-tutorial/) and what I understand of IntrinsicSize, shouldn't I be able to constrain the height and use IntrinsicSize.Min to set the text's width to fit everything at the minimum width required? This actually just cuts off the text.
Text(
modifier = Modifier.height(textHeight).width(IntrinsicSize.Min),
text = "Fabrication et assemblage soignes",
style = textStyle,
)
For some reason ends up rendering as
I want it to look like
Fabrication et
assemblage soigne
On two lines, wrapped to the minimum width.
How can I achieve the result I want?
Also note using .wrapContentWidth() doesn't work because then the text just takes up 1 line and leaves the extra height space empty.
If it's just for that specific text you want, which I highly doubt, you could insert a line break inside the string as follows: "Fabrication et\nassemblage soignes".
If it's for more general sentences, then it's not clear how you would want the Text component to distribute the words between the two lines.
Assuming you would want the width of the lines to be as close as possible (like this strategy), the most 'elegant' way I can think of (which is not very elegant) is implementing an algorithm which would measure the entire width of the text, find the whitespace closest to the middle and replace it with a line break.
I've implemented something simpler which does manage to divide at the position you want:
fun String.attemptToDivideEqually(): String {
if (this.length <= 2) return this
val idealLineBreakIndex = this.closestWhiteSpaceToIndex((this.length + 1) / 2) ?: return this
return this.substring(0, idealLineBreakIndex) + "\n" + this.substring(
idealLineBreakIndex + 1,
this.length
)
}
fun String.closestWhiteSpaceToIndex(index: Int): Int? {
if (index !in 0 .. this.lastIndex) return null
val maxDistance = maxOf(index, this.lastIndex - index)
for (i in 0 .. maxDistance) {
if (index + i <= this.lastIndex && this[index + i] == ' ') return index + i
if (index - i >= 0 && this[index - i] == ' ') return index - i
}
return null
}
However, I don't recommend using it in production code for the following reasons:
Characters can differ greatly in width, so if for whatever reason, the first 'half' of the string has significantly more 'narrow' letters, than the latter, it may look odd.
If there are only a few number of words, it may look odd.
There are probably more reasons I cannot currently think of + I have not tested it thoroughly.
It was an interesting question nonetheless!

How to detect Android device's default font size with CSS media queries?

In Android, Settings > Accessibility > Font Size the user can set font size between "Small", "Default", "Large", "Largest". Among other things, this setting affects default font size of HTML content in WebViews.
I've developed my layouts to look well with the default font size. Setting font size to "Largest" causes text to get cut off in some places, a horizontal scrollbar appear on others etc. In these cases I can use an alternative layout (for example, stack things vertically instead of horizontally), but I'm not sure how to detect which layout to use.
Ideally, I would use CSS media queries. Something like:
#foo {
display: flex;
}
#media (min-width: 360px) {
#foo {
/* If at least 360px available, use a horizontal layout */
flex-direction: row;
}
}
Problem is, the 360px breakpoint isn't affected by the Font Size setting on the device (which makes sense). I've also tried other measurement units: rem, ch, cm – but none of them seemed to take into account device's font size.
I've thought about doing something like this on page load:
display a line of constant text (say, "0000000000") on the screen
measure it in JS
if the measured width / device width ratio is above some set constant, switch to the alternative layout
But this approach would add complexity, delay and repaints and flicker on page load.
Is there a way to take device font size in account in CSS (media queries or otherwise)?
Short Answer
No, you cannot do this just using CSS. However you can minimise impact using a method similar to the one you mentioned in your question (measuring font size and adjusting layout accordingly).
Long Answer
You cannot do this with just CSS, however it is possible to have a performant website without repaints and fall-back to your default styles for no JS.
There is one downside to this method, you do end up injecting a style sheet into the page which will affect first contentful paint times. However bear in mind that this is essentially the same as having a matching media query (so in reality, there is no difference between this and a media query other than it relies on JavaScript).
You can mitigate this by inlining the relevant styles but obviously that carries a page weight cost. You will have to decide which is the greater sin!
The solution is quite simple.
(1) Work out the user's font size using the method similar to the one you described.
(2) Load in conditional CSS that overrides the key layout options as you desire.
(2a) Alternatively add a class to the body and change the layout based on that from styles within existing style sheets or inlined in the document if above the fold.
1. Work out the user's font size
You can do this in vanilla JS right within the header of the page as an inline script so it does not delay anything (other than parsing the script) and it will still be performant.
Try the below example with you font size set to "medium" first, then set your font-size to "extra large" and run the script again. Your font size should show as 16px and 24px respectively.
var el = document.getElementById('foo');
var style = window.getComputedStyle(el, null).getPropertyValue('font-size');
var fontSize = style;
console.log(style)
<div id="foo">a</div>
We now have a font size that is relative to the user's scaling.
We can further improve this by simply dividing the resultant font size by 16 (default size) to get a %age scale.
var el = document.getElementById('foo');
var style = window.getComputedStyle(el, null).getPropertyValue('font-size');
var fontSize = style;
var fontSizePercentage = parseFloat(style) / 16 * 100;
console.log(style, fontSizePercentage + "%");
<div id="foo">a</div>
2. Load in conditional CSS
Now that we know if the font size has been scaled by the user we simply conditionally load CSS.
To do this we need a simple JavaScript check
//set to whatever criteria you need, if your site still works on "large" font size and only needs adjustment at "extra large" then use 124 etc.
if(fontSizePercentage > 100){
//add the CSS
}
In the below example I have 3 columns that turn into 3 rows to demonstrate how font size dictates which styles to apply.
Please note to simulate a dynamically added CSS file I added some inline CSS code that gets written to a style sheet, you would obviously just add a style sheet (I have included a function to do this and just commented out where that function is called).
var el = document.getElementById('foo');
var style = window.getComputedStyle(el, null).getPropertyValue('font-size');
var fontSize = style;
var fontSizePercentage = parseFloat(style) / 16 * 100;
el.remove();
console.log(fontSizePercentage);
//this is just to simulate adding CSS, you would obviously import a style sheet properly here. I have put the proper function further down.
var normal = `
.col3{
float: left;
width: 32%;
margin-right: 1%;
}
`
var bigger = `
.col3{
float: left;
width: 100%;
}
`
var styleSheet = document.createElement("style");
styleSheet.type = "text/css";
if(fontSizePercentage > 100){
styleSheet.innerText = bigger;
}else{
styleSheet.innerText = normal;
}
document.head.appendChild(styleSheet);
////actual style sheet code/////
function addCss(fileName) {
var head = document.head;
var link = document.createElement("link");
link.type = "text/css";
link.rel = "stylesheet";
link.href = fileName;
head.appendChild(link);
}
if(fontSizePercentage > 100){
//addCss('url-to-large-font-size-layout');
}else{
//addCss('url-to-normal-font-size-layout');
}
<div id="foo">a</div>
<div class="col3">column</div>
<div class="col3">column</div>
<div class="col3">column</div>
You will see from the example we add the stylesheet dynamically, choosing between two style sheets in the example. In reality you would probably only need to do the check for the requirement to use the large fonts stylesheet as your standard font size will be covered by your main CSS.
pros
This method effectively is the same as a 'font size media query' and carries a tiny JavaScript overhead.
cons
If you are bothered by repaints then performance is obviously important to you, this method adds an additional request and can delay First Contentful Paint / Initial page rendering for "above the fold content".
Because of this I offer a second suggestion:
2a. Add a class to the body.
Use exactly the same method as above but instead of inserting a style sheet simply use the check for font size to add a class to the body on page load.
The simply include the styles within your current style sheets but with the additional body class as a qualifier.
var el = document.getElementById('foo');
var style = window.getComputedStyle(el, null).getPropertyValue('font-size');
var fontSize = style;
var fontSizePercentage = parseFloat(style) / 16 * 100;
el.remove();
var bod = document.getElementById('simulated-body');
if(fontSizePercentage > 100){
bod.classList.add('large-font-layout');
}
console.log(fontSizePercentage);
.col3{
float: left;
width: 32%;
margin-right: 1%;
}
.large-font-layout .col3{
float: left;
width: 100%;
}
<div id="simulated-body">
<div id="foo">a</div>
<div class="col3">column</div>
<div class="col3">column</div>
<div class="col3">column</div>
</div>
pros this doesn't add any extra requests, should not affect your page paint. Generally this option will be preferable to the first option as you should only need to override a handful of CSS classes so the added weight is negligible.
cons - adds extra weight to your CSS.
Conclusion
Somebody should add a "user font size" media query :-P
Seriously though I would go with the (2a) option I gave you and inline your critical CSS. If you are changing more than 100 classes in your CSS (and so CSS weight becomes a problem) then something is wrong with your design so the speed difference will be negligible. Couple that with the fact that the JS is less than 1kb and it will not affect your paints and it is a simple but effective solution to your problem.
Bonus info for option 2
As an additional thought you can combine option 2 with checking the screen width to really minimise the amount of data sent down the wire. However this then starts adding considerable complexity which is something you said you wanted to avoid. I have included it here for completeness.
function getFontSizePercentage(){
var el = document.getElementById('foo');
var style = window.getComputedStyle(el, null).getPropertyValue('font-size');
var fontSize = style;
var fontSizePercentage = parseFloat(style) / 16 * 100;
el.remove();
return fontSizePercentage;
}
function getPageWidth(){
return Math.max(
document.body.scrollWidth,
document.documentElement.scrollWidth,
document.body.offsetWidth,
document.documentElement.offsetWidth,
document.documentElement.clientWidth
);
}
function addCSS(fileName) {
var head = document.head;
var link = document.createElement("link");
link.type = "text/css";
link.rel = "stylesheet";
link.href = fileName;
head.appendChild(link);
}
var fontSizePercentage = getFontSizePercentage();
var pageWidth = getPageWidth();
var cssSize = "1920";
switch(true) {
case (pageWidth < 1366):
cssSize = "1366";
break;
case (pageWidth < 728):
var cssSize = "728";
break;
default:
cssSize = "1920";
}
var cssPath = "styles/screen-width-" + cssSize + "-font-size-adjust";
if(fontSizePercentage > 100){
console.log("adding CSS for width: " + cssSize, cssPath);
addCSS(cssPath);
}else{
console.log("not adding CSS", cssPath);
}
<div id="foo">a</div>
you can use the VH (viewport height) or VW(viewport width) font-size measures and the font will resize based on % of device viewport for example:
1vh = 1% of viewport heigth
2vw = 2% of viewport width etc
If you are doing media selector you could as well use rem or px as font-size.
You will still have to add #media selector for different devices, keep in mind viewport is the actual size of a device screen, like 10inch screens will have a #media selector up to 1280px or bellow.
/* please use caniuse.com for browser compatibility table*/
h1 {
font-size: 2.5vw;
}
h2 {
font-size: 2vw;
}
p {
font-size: 12px;
}
#media only screen and (max-width:768px) {
h1 {
font-size: 5vw;
}
h2 {
font-size: 4vw;
}
p {
font-size: 18px;
}
}
<h1>text</h1>
<h2>text</h2>
<p>some normal font controled by media query</p>
Is there a way to take device font size in account in CSS (media queries or otherwise)?
Media queries are used to set font size based on device width, not the contrary.
The unit you might use will always be based on the device width, not on the font size (as this can't be already defined at this point).
What you can do is, instead of defining the width of your columns based on percentage or using px units, you can set them on the base font size.
For instance, instead of
.flex-col {
flex: 0 1 30%;
}
you may use
.flex-col {
flex: 0 1 20rem;
}
The real problem are not your media queries but the fact that your containers are not defined in a responsive way (with both fix width and height for instance) causing cut off and scrollbars.

React-Native Element changing size while scrolling

I'm trying to make a react-native scrollview with 3 (or more) elements where the element in the middle of the screen is always 1.75 times the normal element size, and while scrolling the size changes dynamically. Can I find when the element is in the center of screen if the size of the scrollview will be changing? Is it possible to do without some complicated mathematical approach?
I was trying putting conditions to all elements' styles but can't find a way to determine when the condition is met.
handleScroll(event) {
var x = event.nativeEvent.contentOffset.x;
var page = x / this.state.totalWidth;
this.setState({ position: page })
}
isElementFocused(start, stop) {
return (this.state.position >= start && this.state.position < stop);
}
Element:
<View style={styles.swipeBox,
{
backgroundColor: this.isElementFocused(1, 2) ? this.getColor(1) : colors.primary,
width: this.isElementFocused(1, 2) ? this.getWidth(1) : this.state.baseWidth,
height: this.isElementFocused(1, 2) ? this.getHeight(1) : this.state.baseHeight,
}}>
<Text>test</Text>
</View>
I think you want to use smoothScrollToPosition()
please visit https://www.programcreek.com/java-api-examples/?class=android.support.v7.widget.RecyclerView&method=smoothScrollToPosition

Titanium/Appcelerator images will not show on Android

I'm back with tales of frustrating adventures. This time concerning images on the android side of titanium.
Long story short, I can't get any images to show up for android whatsoever, whether it be a background image or a plain image in an imageView object.
I will provide code I'm trying and keep it extremely small and simple so that it can be easily replicated for all of our testing purposes.
The Code:
Attempt #1 programatically creating view and image:
index.js
var header = Ti.UI.createImageView({
width: 300,
image: '/images/header.png',
width: 300
});
var win = Ti.UI.createWindow({
backgroundColor: 'white',
height: Ti.UI.FILL,
title: 'test',
width: Ti.UI.FILL
});
win.add(header);
win.open();
Attempt #2 plain jane .xml and .tss styling:
index.js:
$.index.open();
index.xml:
<Alloy>
<Window class="container">
<Label id="label">Hello World!!</Label>
<ImageView id='img1'></ImageView>
</Window>
</Alloy>
index.tss:
".container": {
backgroundColor: 'white'
}
"#img1": {
width: 300,
image: '/header.png',
width: 300,
top: 0,
left: 0
}
file locations (i copied the same image to 3 different locations to try and get something):
app/assets/header.png
app/assets/android/header.png
app/assets/android/images/header.png
IMPORTANT, What I have tried:
using "/images/header.png
using "images/header.png"
using "header.png"
using "/header.png"
First thing, in the ImageView property you have mentioned the width twice, so you can remove one declaration and put the height of the image, like 300 (you can put Ti.UI.SIZE to maintain the aspect ratio)
Second, put the images inside app/asset/android/images/res-<density> respectively. Replace <density> with for example mdpi / hdpi / xhdpi. (you can put this in res-hdpi for testing)
Do a clean build and then check if it is getting reflected or not.
Very Simple!
Alloy example:
Put your image at app/assets/images for example app/assets/images/header.png .
Now your code is
<ImageView id='img1' image='/images/header.png' ></ImageView>
Or in .tss file
"#img1": {
width: 300,
image: '/images/header.png',
width: 300,
top: 0,
left: 0
}
End !
put your image :
app/assets/images/header.png
then access it with
<ImageView id='img1' image="/images/header.png"></ImageView>
important : try to clean your project first for every changes you made at assets folder before you run the app!

jQuery wScratchPad: Responsive Design?

I have added the wScratchPad to a website project. And it's working.
But the project is based on responsive design. That means the size of all elements adapt to the screen size, so it's optimized for mobile devices too.
The situation is that the size of the scratch-area remains the same, no matter how small you make the browser-window.
<div id="wScratchPad"></div>
<script type="text/javascript">
var scratch = function(e, percent){
if ( percent > 50 ) {
sp.wScratchPad('clear');
}
}
var sp = $("#wScratchPad").wScratchPad({
width : 363,
height : 117,
realtimePercent : true,
scratchDown: scratch,
scratchMove: scratch,
cursor:'./cursors/coin.png',
scratchUp: scratch,
image: './images/1.png',
image2: './images/2.png'
});
</script>
The "width" and "height" are needed to calculate the overlaying canvas and the pixels for uncovering the background-image.
Is there any way to let the scratch area size adapt to the window-size?
One solution you could try would be to set the scratchpad's dimensions to be a percentage of the dimensions of the browser viewport, using document.documentElement.clientWidth/clientHeight. For example:
var sp = $("#wScratchPad").wScratchPad({
width : document.documentElement.clientWidth / 4,
height : document.documentElement.clientHeight / 4,
...
});

Categories

Resources