Чи є спосіб зробити так, щоб ellipsize = “marquee” завжди прокручувався?


93

Я хочу використовувати ефект виділення на TextView, але текст прокручується лише тоді, коли TextView фокусується. Це проблема, бо в моєму випадку це не може.

Я використовую:

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

Чи є спосіб, щоб TextView завжди прокручував текст? Я бачив, як це робиться в додатку Android Market, де назва програми буде прокручуватися в рядку заголовка, навіть якщо воно не отримує фокусу, але мені не вдалося знайти це, про що згадується в документах API.


Для того, щоб працювати з рамками, слід вибирати TextView, а не фокусувати його. Фокус дає вибір, але не навпаки.
Денис Гладкий,

1
Спробуйте alwaysMarqueeTextView stackoverflow.com/a/28806003/3496570
AndroidGeek

я б змінити правильну відповідь на цей один stackoverflow.com/a/3700651/4548520
user25

Відповіді:


62

Я стикався з проблемою, і найкоротше рішення, яке я придумав, - це створити новий клас, похідний від TextView. Клас повинен замінити три методи onFocusChanged , onWindowFocusChanged і isFocused, щоб зробити TextView цілком сфокусованим.

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

Ідеально - це якраз рішення, яке я шукав.
Джейсон,

6
Вам також слід замінити метод onWindowFocusChanged, якщо ви хочете, щоб рамка не зупинялася, коли з'являються меню.
virsir

7
До речі, у нас насправді були проблеми з цим рішенням, оскільки воно заплутує файли, що містять державні дані: якщо ви зміните малюнки з фокусуванням / фокусуванням, то це зламається. Потрібна була майже година налагодження, поки ми не зрозуміли, що цей хак це спричиняє.
Маттіас

Чи можете ви надати більш детальний опис проблеми? Я використовую цю функцію в багатьох додатках. Я хотів би також поглянути на цю проблему.
hnviet

1
Цей хак чудово працює і потрібен лише тоді, коли на екрані одночасно є кілька TextView (наприклад, коли він використовується в ListView) - оскільки зазвичай лише один із них може бути сфокусований, але це рішення "обманює" систему, повідомляючи їй усі є :) В іншому випадку для одного TextView слід використовувати простіший відповідь, наприклад stackoverflow.com/a/3510891/258848 .
dimsuz

120

Я нарешті зіткнувся з цією проблемою сьогодні і так розгорівся hierarchyviewerв додатку Android Market.

Переглядаючи заголовок на екрані деталей програми, вони використовують звичайний старий TextView. Вивчення його властивостей показало, що воно не було сфокусованим, не могло бути сфокусованим і, як правило, було дуже звичайним - за винятком того, що воно було позначене як вибране .

Пізніше один рядок коду, і у мене все запрацювало :)

textView.setSelected(true);

Це має сенс, враховуючи те, що говорить Javadoc :

Вигляд можна вибрати чи ні. Зверніть увагу, що виділення - це не те саме, що фокус. Перегляди зазвичай вибираються в контексті AdapterView, наприклад ListView або GridView.

тобто коли ви прокручуєте елемент у поданні списку (як у додатку Market), лише тоді вибраний зараз текст починає прокручувати. І оскільки це конкретно TextViewне можна фокусувати або натискати, воно ніколи не втратить свій стан вибору.

На жаль, наскільки мені відомо, немає можливості попередньо встановити вибраний стан із макета XML.
Але одна підкладка вище для мене чудово працює.


просто цікаво: чи це також буде працювати для TextViews, які фокусуються, але все одно повинні прокручувати, коли не фокусуєтесь? Отже, чи виділяється стан "дотримується" навіть при зміні стану фокусування?
Маттіас

Я так гадаю. Я не розумію, чому зміна фокусу на базовому TextView(тобто такому, який не вбудований у щось, що "можна вибрати", як-от ListView) змінить вибраний стан.
Крістофер Орр,

Це добре працює і для мене. Спасибі за вашу відповідь. Чи є можливість змінити спосіб повторення ?!
Тима

@Mur: Так, прочитайте TextViewдокументацію та подивіться android:marqueeRepeatLimit.
Крістофер Орр,

1
Цей метод кращий, ніж той, що має фокус, оскільки він не змішується з фокусом поглядів. Якщо у вас є погляди, які вимагають від батьків зосередження уваги, цей метод зіпсує фокус. Цей метод простий, ефективний і не покладається на хаки для зміни фокусу щоразу. Дякую!
Іонут Негру,

75

Просто помістіть ці параметри у свій TextView. Це працює :)

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

1
якщо цей потік відповідей ще не помер, на мою думку, це має бути прийнятою відповіддю. Я додав це до свого визначення XML для текстового подання, і воно спрацювало на першому знімку. Я спробував інші пропозиції, і це найпростіший. - Крім того, додавання "android: marqueeRepeatLimit =" marquee_forever "змушує його прокручувати нескінченно
зафіксуйте

19
Використання цього порушує виділення фокусу рядка ListView, якщо TextView знаходиться всередині нього.
Тіві

Працює ідеально. Просте та елегантне рішення. Дякую!
Рабі,

Як почати та зупинити рамку TextView
Dwivedi Ji

Це не спрацювало для мене, поки я не спробував іншу відповідь - textView.setSelected (true);
Джастін

13

TranslateAnimationпрацює, "тягнучи" Вид в одному напрямку на вказану суму. Ви можете встановити, з чого починати цю «тягу» і де закінчувати.

TranslateAnimation(fromXDelta, toXDelta, fromYDelta, toYDelta);

fromXDelta встановлює зміщення початкового положення руху по осі 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 визначає зміщення кінцевого положення руху по осі 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.

Якщо ширина вашого тексту перевищує модуль різниці між fromXDelta та toXDelta, текст не зможе скласти та вимушено переміщатися по екрану.


Приклад

Припустимо, наш розмір екрану становить 320x240 пікселів. У нас є TextView з текстом, що має ширину 700 пікселів, і ми хочемо створити анімацію, яка "тягне" текст, щоб ми могли бачити кінець фрази.

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

Спочатку ми встановлюємо fromXDelta = 0так, щоб рух не мав початкового зміщення. Тепер нам потрібно визначити значення toXDelta. Для досягнення бажаного ефекту нам потрібно "витягнути" текст точно такими самими пікселями, які він витягує з екрану. (на схемі представлено <<<< X px >>>>) Оскільки наш текст має 700 ширину, а видима область 320px (ширина екрану), ми встановлюємо:

tXDelta = 700 - 320 = 380

І як ми визначаємо ширину екрану та ширину тексту?


Код

Беручи фрагмент Zarah як вихідну точку:

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

Можуть бути простіші способи зробити це, але це працює для будь-якого представлення, яке ви можете придумати, і воно багаторазове. Це особливо корисно, якщо ви хочете анімувати TextView у ListView, не порушуючи при цьому ввімкнені / onFocus можливості textView. Він також прокручується безперервно, навіть якщо вигляд не сфокусований.


Чи не відповідає моя відповідь вище (тобто додати один рядок коду textView.setSelected(true);:) у вашій ситуації?
Крістофер Орр,

На жаль, немає. У мене був ListView, заповнений кількома TextView у кожному рядку (отже, космічна криза = P). кожен рядок текстового перегляду можна натиснути і з’явиться контекстне меню. Налаштування setSelected на true, здається, порушує контекстне меню.
Тіві

є це? У більшості випадків це може бути певною мірою. Для мене з описаної вище причини було єдиним рішенням. (це багаторазово, до речі!) = P
Тіві

12

Не знаю, чи потрібна вам все-таки відповідь, але я знайшов простий спосіб це зробити.

Налаштуйте анімацію так:

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_YІ END_POS_Yє floatцінності, в той час як TICKER_DURATIONце intя заявив з моїми іншими константами.

Тоді ви можете застосувати цю анімацію до свого TextView:

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

І це все. :)

Моя анімація починається з правого боку поза екраном (300f) і закінчується з лівого боку поза екраном (-300f), тривалістю 15 секунд (15000).


1
Це здається не дуже простим. Але я не думаю, що йому все ще потрібна відповідь; див. прийняту відповідь вище .. :)
Крістофер Орр

2
Але я думаю, що це рішення простіше, ніж створення нового класу. : D Так, як є, ви також можете повторно використовувати об’єкт анімації для інших видів, які ви хочете анімувати. ;)
Зара

Це працює добре, але що мені робити, щоб відображати довгі тексти ?! Тепер мій текст буде просто вилучений
Тима

@Mur Votema: Ви пробували встановити ширину TextView на wrap_content?
Зара

3
Не чудове рішення, оскільки параметри розміру та тривалості надзвичайно залежать від розміру тексту. Далі, буде анімовано, навіть якщо довжина тексту менше ширини перегляду. setSelected (true) - насправді найпростіше рішення.
Anm

5

Я написав наступний код для ListView з текстовими елементами для розмітки. Він базується на описаному вище рішенні setSelected. В основному, я розширюю клас ArrayAdapter і замінюю метод getView, щоб вибрати TextView перед тим, як повернути його:

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

Це відповідь, яка з’являється у верхній частині мого пошуку в Google, тому я подумав, що можу опублікувати тут корисну відповідь, оскільки я боюся з тим, щоб пам’ятати це досить часто. У будь-якому випадку, це працює для мене і вимагає атрибутів XML та 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" />

// На Java

        mtvMarque.setEllipsize(TextUtils.TruncateAt.MARQUEE);
        mtvMarque.setSelected(true);
        mtvMarque.setSingleLine(true);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.