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_Yfloatwartoś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.