Czy istnieje sposób, aby elipsize = „marquee” zawsze przewijało się?


93

Chcę użyć efektu marquee na TextView, ale tekst jest przewijany tylko wtedy, gdy TextView staje się fokusem. To jest problem, ponieważ w moim przypadku tak nie jest.

Ja używam:

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

Czy istnieje sposób, aby TextView zawsze przewijał swój tekst? Widziałem, jak to się robi w aplikacji Android Market, gdzie nazwa aplikacji przewija się na pasku tytułu, nawet jeśli nie jest aktywna, ale nie mogłem znaleźć tego w dokumentacji API.


Aby markiza działała, należy wybrać TextView, a nie fokus. Fokus daje wybór, ale nie odwrotnie.
— Denis Gladkiy

1
Spróbuj alwaysMarqueeTextView stackoverflow.com/a/28806003/3496570
— AndroidGeek

zmieniłbym właściwą odpowiedź dla tego jednego stackoverflow.com/a/3700651/4548520
— user25

Odpowiedzi:


62

Miałem do czynienia z problemem i najkrótszym rozwiązaniem, jakie wymyśliłem, jest utworzenie nowej klasy pochodnej od TextView. Klasa powinna przesłonić trzy metody onFocusChanged , onWindowFocusChanged i isFocused, aby obiekt TextView był skupiony.

@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;
}

Idealnie - to jest właśnie takie rozwiązanie, którego szukałem.
— Jason

6
Powinieneś także nadpisać metodę onWindowFocusChanged, jeśli chcesz, aby marquee nie zatrzymywał się, gdy wyskakują menu.
— virsir

7
Nawiasem mówiąc, faktycznie mieliśmy problemy z tym rozwiązaniem, ponieważ psuje ono stanowe elementy do rysowania: jeśli zmienisz elementy rysunkowe z fokusem / wyostrzeniem, to się zepsuje. Debugowanie zajęło nam prawie godzinę, zanim zdaliśmy sobie sprawę, że przyczyną tego był hack.
— Matthias,

Czy możesz podać bardziej szczegółowy opis problemu? Używam tej funkcji w wielu aplikacjach. Chciałbym również przyjrzeć się temu problemowi.
— hnviet

1
Ten hack działa świetnie i jest potrzebny tylko wtedy, gdy wiele TextViews jest na ekranie w tym samym czasie (jak w przypadku użycia w ListView) - ponieważ zwykle tylko jeden z nich może być skupiony, ale to rozwiązanie `` oszukuje '' system, mówiąc mu je wszystkie są :) W przeciwnym razie dla pojedynczego TextView należy użyć prostszej odpowiedzi, takiej jak stackoverflow.com/a/3510891/258848 .
— dimsuz

120

W końcu natknąłem się dzisiaj na ten problem i tak uruchomiłem hierarchyvieweraplikację Android Market.

Patrząc na tytuł na ekranie szczegółów aplikacji, używają zwykłego starego TextView. Badanie jego właściwości wykazało, że nie był skupiony, nie mógł być skupiony i generalnie był bardzo zwyczajny - poza tym, że został oznaczony jako wybrany .

Jedna linijka kodu później i już działało :)

textView.setSelected(true);

Ma to sens, biorąc pod uwagę to, co mówi Javadoc :

Widok można wybrać lub nie. Zwróć uwagę, że zaznaczenie to nie to samo, co fokus. Widoki są zwykle wybierane w kontekście AdapterView, takich jak ListView lub GridView.

tzn. kiedy przewijasz element w widoku listy (jak w aplikacji Market), tylko wtedy wybrany tekst zaczyna się przewijać. A ponieważ ten konkretny TextViewnie jest aktywny ani klikalny, nigdy nie straci swojego stanu wyboru.

Niestety o ile wiem nie ma możliwości wcześniejszego ustawienia wybranego stanu z układu XML.
Ale powyższa jedna linijka działa dobrze dla mnie.


zastanawiam się tylko: czy to zadziała również w przypadku TextViews, które są aktywowane, ale powinny być przewijane, gdy nie jest skupione? No, czy stan zaznaczenia „utrzymuje się” nawet po zmianie stanu fokusu?
— Matthias,

Chyba tak. Nie rozumiem, dlaczego zmiana fokusu na podstawowy TextView(tj. Taki, który nie jest osadzony w czymś „wybieralnym”, takim jak a ListView) miałby zmienić wybrany stan.
— Christopher Orr,

U mnie też działa dobrze. Dziękuję za Twoją odpowiedź. Czy jest możliwość zmiany sposobu powtarzania ?!
— Tima

@Mur: Tak, przeczytaj TextViewdokumentację i przyjrzyj się android:marqueeRepeatLimit.
— Christopher Orr

1
Ta metoda jest lepsza niż ta z fokusem, ponieważ nie psuje ostrości widoków. Jeśli masz poglądy, które wymagają od rodziców skupienia się, ta metoda zepsuje koncentrację. Ta metoda jest prosta, wydajna i nie polega na hackach do zmiany punktu skupienia za każdym razem. Dziękuję Ci!
— Ionut Negru,

75

Po prostu umieść te parametry w swoim TextView. To działa :)

    android:singleLine="true" 
    android:ellipsize="marquee"
    android:marqueeRepeatLimit ="marquee_forever"
    android:scrollHorizontally="true"
    android:focusable="true"
    android:focusableInTouchMode="true" 

1
jeśli ten wątek odpowiedzi nie jest już martwy, to moim zdaniem powinna być zaakceptowana odpowiedź. Dodałem to do mojej definicji XML dla widoku tekstu i zadziałało w pierwszym ujęciu. Wypróbowałem inne sugestie i to jest najprostsze. - Dodanie „android: marqueeRepeatLimit =" marquee_forever "powoduje, że przewijanie jest nieskończone
— nawiasów

19
Użycie tego powoduje przerwanie zaznaczenia fokusu w wierszu ListView, jeśli TextView znajduje się w nim.
— Tivie,

Działa świetnie. Proste i eleganckie rozwiązanie. Dzięki!
— Rabi

Jak uruchomić i zatrzymać markietkę TextView
— Dwivedi Ji

To nie zadziałało, dopóki nie wypróbowałem drugiej odpowiedzi - textView.setSelected (true);
— Justin

13

TranslateAnimationdziała na zasadzie „ciągnięcia” widoku w jednym kierunku o określoną wartość. Możesz ustawić, gdzie rozpocząć i zakończyć to „ciągnięcie”.

TranslateAnimation(fromXDelta, toXDelta, fromYDelta, toYDelta);

fromXDelta ustawia przesunięcie pozycji początkowej ruchu w osi 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 określa przesunięcie pozycji końcowej ruchu w osi 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.

Jeśli szerokość twojego tekstu jest większa niż moduł różnicy między fromXDelta i toXDelta, tekst nie będzie mógł całkowicie i przymusowo poruszać się po ekranie.


Przykład

Załóżmy, że rozmiar naszego ekranu to 320x240 pikseli. Mamy TextView z tekstem o szerokości 700 pikseli i chcemy stworzyć animację, która „ciągnie” tekst, abyśmy mogli zobaczyć koniec frazy.

                                       (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... ||
                             ---------------------------+|
                             |                           |
                             |                           |
                             |                           |
                             +---------------------------+

Najpierw ustawiliśmy fromXDelta = 0tak, aby ruch nie miał offsetu początkowego. Teraz musimy obliczyć wartość toXDelta. Aby osiągnąć pożądany efekt, musimy „wyciągnąć” tekst dokładnie z tego samego piksela, który rozciąga się poza ekran. (w schemacie jest reprezentowane przez <<<< X px >>>>) Ponieważ nasz tekst ma 700 szerokości, a widoczny obszar to 320px (szerokość ekranu) ustalamy:

tXDelta = 700 - 320 = 380

Jak obliczymy szerokość ekranu i szerokość tekstu?


Kod

Biorąc fragment Zarah jako punkt wyjścia:

    /**
     * @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;
}

Mogą istnieć prostsze sposoby, aby to zrobić, ale działa to w przypadku każdego widoku, o którym myślisz, i można go użyć ponownie. Jest to szczególnie przydatne, jeśli chcesz animować TextView w ListView bez przerywania możliwości enabled / onFocus w textView. Przewija się również w sposób ciągły, nawet jeśli widok nie jest ustawiony.


Czy moja odpowiedź powyżej (tj. Dodaj jedną linię kodu :) textView.setSelected(true);nie działa w Twojej sytuacji?
— Christopher Orr,

Niestety nie. Miałem ListView wypełniony kilkoma TextViews w każdym wierszu (stąd kryzys przestrzeni = P). każdy wiersz widoku tekstu jest klikalny i wyświetla menu kontekstowe. Ustawienie setSelected na true wydaje się przerywać menu kontekstowe.
— Tivie,

czy to jest W większości przypadków może to być swego rodzaju przeregulowanie. Dla mnie z powodu opisanego powyżej było to jedyne rozwiązanie. (przy okazji jest wielokrotnego użytku!) = P
— Tivie

12

Nie wiem, czy nadal potrzebujesz odpowiedzi, ale znalazłem łatwy sposób, aby to zrobić.

Skonfiguruj swoją animację w następujący sposób:

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_YI END_POS_Ysą floatwartościami, a TICKER_DURATIONto intoświadczyłem z moich innych stałych.

Następnie możesz teraz zastosować tę animację do swojego TextView:

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

I to wszystko. :)

Moja animacja zaczyna się po prawej stronie poza ekranem (300f) i kończy po lewej stronie poza ekranem (-300f), z czasem trwania 15s (15000).


1
To nie wydaje się bardzo proste. Ale nie sądzę, żeby nadal potrzebował odpowiedzi; zobacz zaakceptowaną odpowiedź powyżej .. :)
— Christopher Orr

2
Ale myślę, że to rozwiązanie jest prostsze niż tworzenie nowej klasy. : D W obecnej sytuacji możesz również ponownie użyć obiektu animacji do innych widoków, które chcesz animować. ;)
— Zarah

Choć działa dobrze, ale co mam zrobić, żeby wyświetlać długie teksty ?! Teraz mój tekst zostanie właśnie wycięty
— Tima

@Mur Votema: Czy próbowałeś ustawić szerokość swojego TextView na wrap_content?
— Zarah

3
Nie jest to świetne rozwiązanie, ponieważ parametry rozmiaru i czasu trwania są bardzo zależne od rozmiaru tekstu. Ponadto będzie animowany, nawet jeśli długość tekstu jest mniejsza niż szerokość widoku. setSelected (true) to naprawdę najłatwiejsze rozwiązanie.
— Anm

5

Napisałem następujący kod dla ListView z elementami tekstowymi marquee. Opiera się na opisanym powyżej rozwiązaniu setSelected. Zasadniczo rozszerzam klasę ArrayAdapter i nadpisuję metodę getView, aby wybrać TextView przed jej zwróceniem:

    // 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;

        }
    }

1

To jest odpowiedź, która pojawia się na górze mojej wyszukiwarki Google, więc pomyślałem, że mógłbym zamieścić tutaj przydatną odpowiedź, ponieważ mam problemy z zapamiętaniem tego dość często. W każdym razie to działa dla mnie i wymaga atrybutów XML oraz A 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);
            }
        }
    });

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" />

// W Javie

        mtvMarque.setEllipsize(TextUtils.TruncateAt.MARQUEE);
        mtvMarque.setSelected(true);
        mtvMarque.setSingleLine(true);
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.