14
votes

Alignement des vues de texte multilignes sur leur ligne de base (ConstraintLayout Android Studio)

J'ai trois TextViews "hi" , "x" et "Hello World" que je voudrais aligner en bas du texte Hello World (ie hi_x_World). Hello World n'est qu'une ligne, mais layout_width et layout_height sont tous deux définis sur wrap_content .

Ils ont des tailles de police différentes, donc même si je peux facilement aligner le bas des boîtes des vues de texte, le texte lui-même ne s'aligne pas.

J'ai trouvé une app:layout_constraintBaseline_toBaselineOf="@+id/text paramètres XML différente app:layout_constraintBaseline_toBaselineOf="@+id/text qui fonctionne lorsque je n'ai qu'une seule ligne dans TextView. Cependant, lorsque j'ai 2 lignes ou plus (comme dans Hello World TextView), la ligne de base considérée est dans le "Bonjour" au lieu de "Monde".

Existe-t-il un moyen de modifier le paramètre pour considérer la ligne de base sous le mot «Monde» au lieu de «Bonjour»?

entrez la description de l'image ici


1 commentaires

Pouvez-vous montrer votre XML et votre code et également essayer de montrer ce que vous voulez réaliser avec une image comme ci-dessus.


4 Réponses :


0
votes

Vous pouvez utiliser:

app:layout_constraintTop_toTopOf="@+id/helloworldtextid"

N'oubliez pas non plus de définir la hauteur sur wrap_content sorte que le bas de TextViews s'aligne jusqu'au bas de "Hello World", même s'il est encapsulé sur deux lignes ou plus.

Si vous voulez qu'il soit centré sur "Hello World" lorsqu'il est multiligne, pensez à ajouter

app:layout_constraintBottom_toBottomOf="@+id/helloworldtextid"

avec celui du bas. Il centrera le texte verticalement sur le "Hello World" à plusieurs lignes.


2 commentaires

vous pouvez modifier le centrage (la valeur par défaut est parfaitement centrée = 0,5 en utilisant la propriété verticalBias


Ce n'est pas ce que le PO a demandé. Vous suggérez simplement d'aligner le bas de la première vue de texte sur le bas de celle souhaitée, pas sur sa ligne de base.



2
votes

AFAIK, il n'y a aucun moyen d'accomplir cette tâche en utilisant ConstraintLayout à partir d'aujourd'hui.

Si vous connaissez le contenu de "helloWorldTextView" à l'avance, vous pouvez diviser les lignes en plusieurs textViews, puis utiliser app:layout_constraintBaselineToBaselineOf .

Je sais que c'est une solution de contournement délicate, mais c'est la seule façon qui me vient à l'esprit.


0 commentaires

5
votes

Deuxième mise à jour: c'est une autre façon de voir la première solution tirée de cette réponse Stack Overflow qui fonctionnera également avec ConstraintLayout . Cette solution utilise un TextView personnalisé. Le TextView personnalisé renvoie la ligne de base de la dernière ligne de texte dans le TextView à partir de la fonction getBaseline() au lieu de la ligne de base de la première ligne qui est l'action par défaut. C'est une solution agréable et propre (IMO) qui prend en compte les TextViews multilignes mais aussi la gravité, etc.

Version Kotlin de BaselineLastLineTextView

<androidx.constraintlayout.widget.ConstraintLayout 
    android:layout_width="match_parent"
    android:id="@+id/layout"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/hiView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="hi"
        android:textSize="46sp"
        app:layout_constraintTop_toTopOf="@id/helloView"
        app:layout_constraintEnd_toStartOf="@+id/xView"
        app:layout_constraintStart_toStartOf="parent"/>

    <TextView
        android:id="@+id/xView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="x"
        android:textSize="36sp"
        app:layout_constraintTop_toTopOf="@id/helloView"
        app:layout_constraintEnd_toStartOf="@+id/helloView"
        app:layout_constraintStart_toEndOf="@+id/hiView" />

    <TextView
        android:id="@+id/helloView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello\nWorld!"
        android:textSize="50sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/xView"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:text="Adjust Base Lines"
        android:onClick="onClick"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/helloView" />


</androidx.constraintlayout.widget.ConstraintLayout>

Première mise à jour: Ceci est une mise à jour de ma réponse ci-dessous qui est toujours une solution valide (IMO). Il s'agit d'une approche alternative qui n'implique aucun code Java / Kotlin et peut être réalisée simplement en utilisant XML.

Créez un wrap_content wrap_content invisible qui a la même taille de police que le "Hello World!" TextView . (Vous devrez peut-être également prendre en compte le remplissage et les marges en fonction de la mise en page réelle.) Contraignez cette nouvelle vue au bas de "Hello World!", Rendez-la invisible et définissez le contenu sur quelque chose de court qui n'occupera qu'une seule ligne. Cela vous donnera une vue cible qui a la même ligne de base que la dernière ligne de "Hello World!" vue.

Contraignez les lignes de base de "hi" et "x" à la nouvelle vue invisible . Toutes les vues partageront désormais la même ligne de base et sans codage.

entrez la description de l'image ici

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    fun onClick(view: View) {
        button.isEnabled = false
        // Get the StaticLayout from the TextView
        val layout = helloView.layout

        // Get the base line location for last line of Hello World! TextView, "hi" and "x"
        val helloBaseLIne = layout.getLineBaseline(layout.lineCount - 1)
        val hiBaseLine = hiView.layout.getLineBaseline(0)
        val xBaseLine = xView.layout.getLineBaseline(0)
        
        // Shift "hi" and "x" down so base lines match that of hello world!
        hiView.updatePadding(top = helloBaseLIne - hiBaseLine)
        xView.updatePadding(top = helloBaseLIne - xBaseLine)
    }
}


Première réponse: comme indiqué dans une autre réponse, il n'y a aucun moyen de le faire simplement en utilisant les contraintes ConstraintLayout . Vous devrez recourir à une solution programmatique.

Dans chaque TextView se trouve un StaticLayout qui peut révéler un peu la typographie du texte. En faisant référence à la disposition statique, un remplissage peut être ajouté aux vues appropriées pour aligner les lignes de base.

Dans cette démo, les trois TextViews ont simplement leurs sommets alignés. Au départ, les vues ressemblent à ceci:

entrez la description de l'image ici

Lorsque vous cliquez sur le bouton, les emplacements de la ligne de base sont calculés et un remplissage est ajouté aux sommets des TextViews "hi" et "x".

entrez la description de l'image ici

Les détails varieront avec la mise en œuvre, mais c'est la technique générale.

MainActivity.kt

<androidx.constraintlayout.widget.ConstraintLayout 
    android:id="@+id/layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/hiddenView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="A"
        android:textSize="50sp"
        android:visibility="invisible"
        app:layout_constraintBottom_toBottomOf="@id/helloView"
        app:layout_constraintEnd_toEndOf="parent" />

    <TextView
        android:id="@+id/hiView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="hi"
        android:textSize="46sp"
        app:layout_constraintBaseline_toBaselineOf="@id/hiddenView"
        app:layout_constraintEnd_toStartOf="@+id/xView"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@id/helloView" />

    <TextView
        android:id="@+id/xView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="x"
        android:textSize="36sp"
        app:layout_constraintBaseline_toBaselineOf="@id/hiddenView"
        app:layout_constraintEnd_toStartOf="@+id/helloView"
        app:layout_constraintStart_toEndOf="@+id/hiView"
        app:layout_constraintTop_toTopOf="@id/helloView" />

    <TextView
        android:id="@+id/helloView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello\nWorld!"
        android:textSize="50sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/xView"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:onClick="onClick"
        android:text="Adjust Base Lines"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/helloView" />


</androidx.constraintlayout.widget.ConstraintLayout>

activity_main.xml

class BaselineLastLineTextView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : AppCompatTextView(context, attrs) {

    override fun getBaseline(): Int {
        val layout = layout ?: return super.getBaseline()
        val baselineOffset = super.getBaseline() - layout.getLineBaseline(0)
        return baselineOffset + layout.getLineBaseline(layout.lineCount - 1)
    }
}


1 commentaires

L'astuce avec hiddenView fonctionne bien, merci! Il semble qu'il n'est pas nécessaire de le rendre invisible , vous pouvez simplement omettre l'attribut android:text .



0
votes

Utiliser la ligne directrice

Classe utilitaire représentant un objet d'assistance Guideline pour ConstraintLayout. Les objets d'assistance ne sont pas affichés sur l'appareil (ils sont marqués comme View.GONE) et ne sont utilisés qu'à des fins de mise en page. Ils ne fonctionnent que dans un ConstraintLayout.

Une ligne directrice peut être horizontale ou verticale :

a) Les lignes directrices verticales ont une largeur de zéro et la hauteur de leur parent ConstraintLayout

b) Les lignes directrices horizontales ont une hauteur de zéro et la largeur de leur parent ConstraintLayout

Les widgets peuvent ensuite être contraints à une ligne de guidage, ce qui permet de positionner facilement plusieurs widgets à partir d'une seule ligne de guidage, ou autorise un comportement de mise en page réactif en utilisant le positionnement en pourcentage.

Exemple entrez la description de l'image ici

Code:-

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent=".4"
        />

    <TextView
        android:id="@+id/textViewHi"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        android:layout_marginLeft="50dp"
        android:text="hi"
        android:textSize="30sp"
        app:layout_constraintBottom_toTopOf="@+id/guideline" />
    <TextView
        android:id="@+id/textViewX"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="@+id/textViewHi"
        android:layout_marginLeft="50dp"
        android:text="x"
        android:textSize="30sp"
        app:layout_constraintBottom_toTopOf="@+id/guideline" />

    <TextView
        android:id="@+id/textViewHelloWorld"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="@+id/textViewX"
        android:layout_marginLeft="50dp"
        android:text="Hello World"
        android:textSize="45sp"
        app:layout_constraintBottom_toTopOf="@+id/guideline" />

</androidx.constraintlayout.widget.ConstraintLayout>


0 commentaires