Existe-t-il un moyen de toujours faire défiler ellipsize = "marquee"?

93

Je souhaite utiliser l'effet de sélection sur un TextView, mais le texte ne défile que lorsque TextView obtient le focus. C'est un problème, car dans mon cas, ce n'est pas possible.

J'utilise:

  android:ellipsize="marquee"
  android:marqueeRepeatLimit="marquee_forever"

Existe-t-il un moyen pour que TextView fasse toujours défiler son texte? J'ai vu cela se faire dans l'application Android Market, où le nom de l'application défilera dans la barre de titre, même s'il ne reçoit pas le focus, mais je n'ai pas trouvé que cela soit mentionné dans la documentation de l'API.

Matthias
la source
Pour que le rectangle de sélection fonctionne, TextView doit être sélectionné et non ciblé. La mise au point donne la sélection mais pas l'inverse.
Denis Gladkiy
1
Essayez alwaysMarqueeTextView stackoverflow.com/a/28806003/3496570
AndroidGeek
Je changerais la bonne réponse pour celui-ci stackoverflow.com/a/3700651/4548520
user25

Réponses:

62

J'ai été confronté au problème et la solution la plus courte que j'ai trouvée est de créer une nouvelle classe dérivée de TextView. La classe doit remplacer trois méthodes onFocusChanged , onWindowFocusChanged et isFocused pour que le TextView soit entièrement concentré.

@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
    if(focused)
        super.onFocusChanged(focused, direction, previouslyFocusedRect);
}

@Override
public void onWindowFocusChanged(boolean focused) {
    if(focused)
        super.onWindowFocusChanged(focused);
}


@Override
public boolean isFocused() {
    return true;
}
hnviet
la source
Parfait - c'est juste la solution que je recherchais.
Jason
6
Vous devez également remplacer la méthode onWindowFocusChanged si vous souhaitez que le rectangle de sélection ne s'arrête pas lorsque les menus apparaissent.
virsir
7
Au fait, nous avons en fait eu des problèmes avec cette solution, car elle gâche les drawables avec état: si vous changez les drawables avec focus-in / focus-out, alors cela va casser. Il nous a fallu près d'une heure de débogage jusqu'à ce que nous nous rendions compte que ce hack en était la cause.
Matthias
Pouvez-vous fournir une description plus détaillée du problème? J'utilise cette fonctionnalité dans de nombreuses applications. Je voudrais également examiner ce problème.
hnviet
1
Ce hack fonctionne très bien et n'est nécessaire que lorsque plusieurs TextViews sont à l'écran en même temps (comme lorsqu'il est utilisé dans ListView) - car généralement un seul d'entre eux peut être ciblé, mais cette solution `` trompe '' le système en leur disant à tous are :) Sinon, pour un seul TextView, une réponse plus simple comme stackoverflow.com/a/3510891/258848 doit être utilisée.
dimsuz
120

Je me suis finalement heurté à ce problème aujourd'hui et je me suis donc lancé hierarchyviewersur l'application Android Market.

En regardant le titre sur l'écran de détail d'une application, ils utilisent un ancien TextView. L'examen de ses propriétés a montré qu'il n'était pas ciblé, ne pouvait pas l' être et était généralement très ordinaire - à l'exception du fait qu'il était marqué comme sélectionné .

Une ligne de code plus tard et je l'ai fait fonctionner :)

textView.setSelected(true);

Cela a du sens, étant donné ce que dit le Javadoc :

Une vue peut être sélectionnée ou non. Notez que la sélection n'est pas la même que le focus. Les vues sont généralement sélectionnées dans le contexte d'un AdapterView comme ListView ou GridView.

c'est-à-dire que lorsque vous faites défiler un élément dans une vue de liste (comme dans l'application Market), ce n'est qu'alors que le texte maintenant sélectionné commence à défiler. Et puisque ce particulier TextViewn'est pas focalisable ou cliquable, il ne perdra jamais son état de sélection.

Malheureusement, pour autant que je sache, il n'y a aucun moyen de prédéfinir l'état sélectionné à partir du format XML.
Mais le one-liner ci-dessus fonctionne bien pour moi.

Christophe Orr
la source
Je me demande simplement: cela fonctionnerait-il également pour les TextViews qui sont focalisables, mais qui devraient toujours défiler lorsqu'ils ne sont pas mis au point? Iow, l'état de sélection "colle" même lorsque l'état de mise au point change?
Matthias le
Je suppose. Je ne vois pas pourquoi un changement de focus sur un élément de base TextView(c'est-à-dire un élément non intégré dans quelque chose de "sélectionnable" comme a ListView) changerait l'état sélectionné.
Christopher Orr
Cela fonctionne bien pour moi aussi. Merci pour votre réponse. Est-il possible de changer le mode de répétition?!
Tima
@Mur: Oui, lisez la TextViewdocumentation et jetez un œil à android:marqueeRepeatLimit.
Christopher Orr
1
Cette méthode est meilleure que celle avec le focus car elle ne gâche pas le focus des vues. Si vous avez des opinions qui exigent que leurs parents se concentrent, cette méthode gâchera la concentration. Cette méthode est simple, efficace et ne repose pas sur des hacks pour changer le focus à chaque fois. Je vous remercie!
Ionut Negru le
75

Mettez simplement ces paramètres dans votre TextView. Ça marche :)

    android:singleLine="true" 
    android:ellipsize="marquee"
    android:marqueeRepeatLimit ="marquee_forever"
    android:scrollHorizontally="true"
    android:focusable="true"
    android:focusableInTouchMode="true" 
Vinayak
la source
1
si ce fil de réponse n'est pas déjà mort, cela devrait être la réponse acceptée, à mon avis. J'ai ajouté cela à ma définition XML pour la vue texte, et cela a fonctionné sur le premier plan. J'ai essayé d'autres suggestions, et c'est la plus simple. - De plus, l'ajout de "android: marqueeRepeatLimit =" marquee_forever "le fait défiler indéfiniment
brack
19
L'utilisation de cela interrompt la sélection de focus d'une ligne ListView si le TextView se trouve à l'intérieur.
Tivie
Fonctionne parfaitement. Solution simple et élégante. Merci!
Rabi
Comment démarrer et arrêter le chapiteau de TextView
Dwivedi Ji
Cela n'a pas fonctionné pour moi jusqu'à ce que j'aie essayé l'autre réponse - textView.setSelected (true);
Justin le
13

TranslateAnimationfonctionne en "tirant" la vue dans une direction d'un montant spécifié. Vous pouvez définir où commencer ce "tirage" et où se terminer.

TranslateAnimation(fromXDelta, toXDelta, fromYDelta, toYDelta);

fromXDelta définit le décalage de la position de départ du mouvement dans l'axe X.

fromXDelta = 0 //no offset. 
fromXDelta = 300 //the movement starts at 300px to the right.
fromXDelta = -300 //the movement starts at 300px to the left

toXDelta définit la position de fin de décalage du mouvement dans l'axe X.

toXDelta = 0 //no offset. 
toXDelta = 300 //the movement ends at 300px to the right.
toXDelta = -300 //the movement ends at 300px to the left.

Si la largeur de votre texte est plus grande que le module de la différence entre fromXDelta et toXDelta, le texte ne pourra pas se déplacer totalement et de manière contraignante dans l'écran.


Exemple

Supposons que la taille de notre écran soit de 320x240 px. Nous avons un TextView avec un texte qui a une largeur de 700px et nous souhaitons créer une animation qui "tire" le texte afin que nous puissions voir la fin de la phrase.

                                       (screen)
                             +---------------------------+
                             |<----------320px---------->|
                             |                           |
                             |+---------------------------<<<< X px >>>>
               movement<-----|| some TextView with text that goes out...
                             |+---------------------------
                             |  unconstrained size 700px |
                             |                           |
                             |                           |
                             +---------------------------+


                             +---------------------------+
                             |                           |
                             |                           |
               <<<< X px >>>>---------------------------+|
movement<----- some TextView with text that goes out... ||
                             ---------------------------+|
                             |                           |
                             |                           |
                             |                           |
                             +---------------------------+

Tout d'abord, nous réglons fromXDelta = 0pour que le mouvement n'ait pas de décalage de départ. Nous devons maintenant déterminer la valeur de toXDelta. Pour obtenir l'effet souhaité, nous devons "extraire" le texte exactement du même px qu'il s'étend hors de l'écran. (dans le schéma est représenté par <<<< X px >>>>) Puisque notre texte a une largeur de 700 et que la zone visible est de 320px (largeur de l'écran), nous définissons:

tXDelta = 700 - 320 = 380

Et comment calculer la largeur de l'écran et la largeur du texte?


Code

Prenant l'extrait de Zarah comme point de départ:

    /**
     * @param view The Textview or any other view we wish to apply the movement
     * @param margin A margin to take into the calculation (since the view
     *               might have any siblings in the same "row")
     *
     **/
public static Animation scrollingText(View view, float margin){

    Context context = view.getContext(); //gets the context of the view

            // measures the unconstrained size of the view
            // before it is drawn in the layout
    view.measure(View.MeasureSpec.UNSPECIFIED, 
                         View.MeasureSpec.UNSPECIFIED); 

            // takes the unconstrained wisth of the view
    float width = view.getMeasuredWidth();

            // gets the screen width
    float screenWidth = ((Activity) context).getWindowManager().getDefaultDisplay().getWidth();


            // perfrms the calculation
    float toXDelta = width - (screenWidth - margin);

            // sets toXDelta to 0 if the text width is smaller that the screen size
    if (toXDelta < 0) {toXDelta = 0; } else { toXDelta = 0 - toXDelta;}

            // Animation parameters
    Animation mAnimation = new TranslateAnimation(0, toXDelta, 0, 0);
    mAnimation.setDuration(15000); 
    mAnimation.setRepeatMode(Animation.RESTART);
    mAnimation.setRepeatCount(Animation.INFINITE);

    return mAnimation;
}

Il existe peut-être des moyens plus simples de le faire, mais cela fonctionne pour toutes les vues auxquelles vous pouvez penser et est réutilisable. Il est particulièrement utile si vous souhaitez animer un TextView dans un ListView sans interrompre les capacités enabled / onFocus du textView. Il défile également en continu même si la vue n'est pas focalisée.

Tivie
la source
Ma réponse ci-dessus (c.-à-d. Ajouter une ligne de code:) textView.setSelected(true);ne fonctionne pas dans votre situation?
Christopher Orr
Malheureusement non. J'avais un ListView rempli de plusieurs TextViews dans chaque ligne (d'où la crise spatiale = P). chaque ligne de la vue de texte est cliquable et fait apparaître un menu contextuel. La définition de setSelected sur true semble interrompre le menu contextuel.
Tivie
c'est ça? Cela pourrait être une sorte de dépassement dans la plupart des cas. Pour moi, pour la raison décrite ci-dessus, était la seule solution. (c'est réutilisable, btw!) = P
Tivie
12

Je ne sais pas si vous avez encore besoin de la réponse, mais j'ai trouvé un moyen simple de le faire.

Configurez votre animation comme ceci:

Animation mAnimation = new TranslateAnimation(START_POS_X, END_POS_X, 
                START_POS_Y, END_POS_Y);
mAnimation.setDuration(TICKER_DURATION); 
mAnimation.setRepeatMode(Animation.RESTART);
mAnimation.setRepeatCount(Animation.INFINITE);

START_POS_X, END_POS_X, START_POS_YEt END_POS_Ysont des floatvaleurs, tout en TICKER_DURATIONest un que intje déclarai avec mes autres constantes.

Ensuite, vous pouvez maintenant appliquer cette animation à votre TextView:

TextView tickerText = (TextView) findViewById(R.id.ticker);
tickerText.setAnimation(mAnimation);

Et c'est tout. :)

Mon animation commence sur le côté droit hors écran (300f) et se termine sur le côté gauche hors écran (-300f), avec une durée de 15s (15000).

Zarah
la source
1
Cela ne semble pas très simple. Mais je ne pense pas qu'il ait encore besoin de la réponse; voir la réponse acceptée ci-dessus .. :)
Christopher Orr
2
Mais je pense que cette solution est plus simple que de créer une nouvelle classe. : D En l'état, vous pouvez également réutiliser l'objet d'animation pour d'autres vues que vous souhaitez animer. ;)
Zarah
Cela fonctionne bien, mais que dois-je faire pour afficher de longs textes?! Maintenant, mon texte sera juste coupé
Tima
@Mur Votema: Avez-vous essayé de définir la largeur de votre TextView sur wrap_content?
Zarah
3
Ce n'est pas une bonne solution, car les paramètres de taille et de durée sont extrêmement dépendants de la taille du texte. De plus, animera, même si la longueur du texte est inférieure à la largeur de la vue. setSelected (true) est vraiment la solution la plus simple.
Anm
5

J'ai écrit le code suivant pour un ListView avec des éléments de texte de sélection. Il est basé sur la solution setSelected décrite ci-dessus. Fondamentalement, j'étends la classe ArrayAdapter et remplace la méthode getView pour sélectionner le TextView avant de le retourner:

    // Create an ArrayAdapter which selects its TextViews before returning      
    // them. This would enable marqueeing while still making the list item
    // clickable.
    class SelectingAdapter extends ArrayAdapter<LibraryItem>
    {
        public
        SelectingAdapter(
            Context context, 
            int resource, 
            int textViewResourceId, 
            LibraryItem[] objects
        )
        {
            super(context, resource, textViewResourceId, objects);
        }

        @Override
        public
        View getView(int position, View convertView, ViewGroup parent)
        {
            View view = super.getView(position, convertView, parent);
            TextView textview = (TextView) view.findViewById(
                R.id.textview_playlist_item_title
            );
            textview.setSelected(true);
            textview.setEnabled(true);
            textview.setFocusable(false);
            textview.setTextColor(0xffffffff);
            return view;

        }
    }
smichak
la source
1

C'est la réponse qui apparaît en haut de ma recherche Google, alors j'ai pensé que je pourrais publier une réponse utile ici car j'ai du mal à m'en souvenir assez souvent. Quoi qu'il en soit, cela fonctionne pour moi et nécessite des attributs XML et un onFocusChangeListener.

//XML
        <TextView
            android:id="@+id/blank_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="5dp"
            android:layout_gravity="center_horizontal|center_vertical"
            android:background="#a4868585"
            android:textColor="#fff"
            android:textSize="15sp"
            android:singleLine="true"
            android:lines="1"
            android:ellipsize="marquee"
            android:marqueeRepeatLimit ="marquee_forever"
            android:scrollHorizontally="true"
            android:focusable="true"
            android:focusableInTouchMode="true"
            tools:ignore="Deprecated" />

//JAVA
    titleText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            if (!hasFocus) {
                titleText.setSelected(true);
            }
        }
    });
Thunderstick
la source
1

// xml

 <TextView
            android:id="@+id/tvMarque"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ellipsize="marquee"
            android:layout_gravity="center_horizontal"
            android:fadingEdge="horizontal"
            android:marqueeRepeatLimit="marquee_forever"
            android:scrollHorizontally="true"
            android:padding="5dp"
            android:textSize="16sp"
            android:text=""
            android:textColor="@color/colorSyncText"
            android:visibility="visible" />

// En Java

        mtvMarque.setEllipsize(TextUtils.TruncateAt.MARQUEE);
        mtvMarque.setSelected(true);
        mtvMarque.setSingleLine(true);
Soni Kumar
la source