Hello I'm developing an android application for my University Mobile Device Tech. exam, the application talks to a REST API which gives some data in return on a specific route. So I have the necessity of creating UI elements dynamically on the spot with the data I get from the API for each user.
So I'm creating generic UI elements in Kotlin to be added programmatically to my activity once the user reach such point in the lifecycle of the application.
The generic UI element is composed as follows:
A CardView containing a ConstraintLayout which contains a TextView
I've struggled a little with setting up the parameters of the first two elements, now I'm stuck at the last element which I fail to set up correctly because I cannot find a way to set the property: android:fontFamily="sans-serif-medium"
I've created a reference in the designer tool of Android studio for the code I need to recreate in Kotlin as follows:
<TextView
android:id="@ id/placeholder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:fontFamily="sans-serif-medium"
android:text="@string/No_projects"
android:textAlignment="center"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="@color/black"
android:textSize="20sp"
android:textStyle="bold"
android:typeface="normal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.5" />
This XML equal such declared parameters in the android studio design tools.
I had difficulties setting the textStyle and typeface properties in Kotlin, but found a (probably) solution here
Here instead there is the code fragment where i create and set up one-by-one all the parameters shown in the image/xml fragment:
val genericTextView = TextView(context)
genericTextView.layoutParams.height = ConstraintLayout.LayoutParams.WRAP_CONTENT
genericTextView.layoutParams.width = ConstraintLayout.LayoutParams.WRAP_CONTENT
(genericTextView.layoutParams as ViewGroup.MarginLayoutParams).setMargins(0, 8, 0, 8)
genericTextView.id = generateViewId()
genericTextView.tag = "NoProjectsText"
genericTextView.visibility = TextView.VISIBLE
genericTextView.text = R.string.No_projects.toString()
genericTextView.textSize = 20f
genericTextView.setTextColor(context.getColor(R.color.black))
genericTextView.textAlignment = TextView.TEXT_ALIGNMENT_CENTER
genericTextView.setTextAppearance(com.google.android.material.R.style.TextAppearance_AppCompat_Medium)
genericTextView.setTypeface(genericTextView.typeface, android.graphics.Typeface.BOLD)
// Constraints for inner text for elements cards
val connections2 = genericTextView.layoutParams as ConstraintLayout.LayoutParams
connections2.apply {
connections2.endToEnd = ConstraintSet.PARENT_ID
connections2.topToTop = ConstraintSet.PARENT_ID
connections2.startToStart = ConstraintSet.PARENT_ID
connections2.bottomToBottom = ConstraintSet.PARENT_ID
connections2.horizontalBias = 0.5f
connections2.verticalBias = 0.5f
}
I cannot find a property in TextView
class that modifies or set the font family.
I hope that I've set the rest of the parameters correctly.
Thanks for any help or suggestion.
CodePudding user response:
A simple solution (a bit devious) is to create a layout that contains the TextView with all styles in xml and call it in the code. Take a look bellow
layout_text_view.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@ id/placeholder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:fontFamily="sans-serif-medium"
android:text="@string/No_projects"
android:textAlignment="center"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="@color/black"
android:textSize="20sp"
android:textStyle="bold"
android:typeface="normal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.5" />
MainActivity.kt
val inflater = LayoutInflater.from(this)
val tv = inflater.inflate(R.layout.layout_text_view, null, false) as TextView
tv.text = "Some text"
tv.visibility = View.VISIBLE
CodePudding user response:
I'm not sure it's actually possible to do this in code, in general. There are classes like Typeface.CustomFallbackBuilder that allow you to construct a FontFamily
and apply it to a Typeface
, but that was added very recently, in API 29. So was the FontFamily
class itself!
Font families were added late in Android's life, as far as I'm aware here's the documentation for when Fonts in XML were added - that includes defining font families in XML. Like it says there, this was added to Android itself in 8.0 - the feature was backported to older APIs through the support library.
So I assume when you define a font-family
attribute in XML, what's actually happening is a lot of work behind the scenes, resolving resources to select the appropriate typeface (possibly at compilation time) rather than a neat class like FontFamily
that only just got created!
From my own experimentation, even this backported functionality has limits that Android itself doesn't in the later APIs - like ignoring all weights except 400 and (I think) 700, and just picking whichever font in the family is closest to those (sometimes even switching between normal and italic if it gives a closer weight)
This whole styling/theming side of things has always been a bit complicated to me, so it's possible you could define a style with each font family, and then apply one of those as a TextAppearance/Style/Theme/ThemeOverlay at runtime. I'm not sure, but it could be something to look into.
But for your example, sans-serif-medium
, really that's not a font family, it's a specific typeface within the sans-serif font family. So maybe you don't need font families at all? If you can translate that to sans_serif_500.ttf
or Typeface.SANS_SERIF
or whatever, you can easily use that in code and create a Typeface
from it to apply to your TextView
. I don't know how easy that would be for what you're doing, but I'm assuming you're working with a known set of font families here.
Sorry this is a bit vague, if it's not obvious I'm not sure about how a lot of this works myself, but hopefully it can point you in the right direction! I've had problems with font-family
myself in the past, with the way it ignores weights and creates inconsistencies, even though the Material Type System uses weights other than Regular and Bold. I found it easier to create font families for each weight (pretending they're 400) and refer to them explicitly in styles - and I only needed a family so I could link them with their italic variant. If you need that too, I'd look into the "apply a theme" idea and see if you can overlay some styling (using an XML resource that defines your font family) at runtime