Есть ли способ заставить 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;
}
Hnviet
источник
Идеально - это как раз то решение, которое я искал.
Джейсон
6
Вы также должны переопределить метод onWindowFocusChanged, если хотите, чтобы область выделения не останавливалась при появлении меню.
virsir 08
7
Между прочим, у нас действительно были проблемы с этим решением, поскольку оно портит чертежи с сохранением состояния: если вы измените чертежи с помощью focus-in / focus-out, это сломается. Нам потребовался почти час отладки, пока мы не поняли, что причиной этого является взлом.
Матиас,
Не могли бы вы более подробно описать проблему? Я использую эту функцию во многих приложениях. Я бы тоже хотел взглянуть на эту проблему.
hnviet
1
Этот хак отлично работает и необходим только тогда, когда на экране одновременно отображается несколько TextView (например, при использовании в ListView) - потому что обычно только один из них может быть сфокусирован, но это решение `` обманывает '' систему, сообщая им все есть :) В противном случае для одного TextView следует использовать более простой ответ, например stackoverflow.com/a/3510891/258848 .
dimsuz 01
120

Сегодня я наконец столкнулся с этой проблемой, и поэтому загорелся hierarchyviewerприложением Android Market.

Глядя на заголовок на подробном экране приложения, они используют старый добрый TextView. Изучение его свойств показало, что он не сфокусирован, не может быть сфокусирован и в целом был очень обычным - за исключением того факта, что он был отмечен как выбранный .

Одна строчка кода позже, и у меня все заработало :)

textView.setSelected(true);

Это имеет смысл, учитывая то, что говорит Javadoc :

Вид можно выбрать или нет. Обратите внимание, что выделение - это не то же самое, что фокус. Представления обычно выбираются в контексте AdapterView, например ListView или GridView.

т.е. когда вы прокручиваете элемент в виде списка (например, в приложении "Маркет"), только после этого начинает прокручиваться выбранный текст. И поскольку этот конкретный TextViewобъект не может быть выбран для фокусировки или кликабельна, он никогда не потеряет свое состояние выбора.

К сожалению, насколько мне известно, невозможно предварительно установить выбранное состояние из XML макета.
Но однострочный вышеупомянутый мне подходит.

Кристофер Орр
источник
просто интересно: будет ли это также работать для TextView, которые могут быть сфокусированы, но должны прокручиваться, когда они не сфокусированы? Да, а состояние выбора "прилипает" даже при изменении состояния фокуса?
Матиас,
Полагаю, что так. Я не понимаю, почему изменение фокуса на базовом TextView(то есть не встроенном во что-то «выбираемое» вроде a ListView) изменило бы выбранное состояние.
Кристофер Орр,
У меня тоже работает. Спасибо за ваш ответ. Есть ли возможность изменить способ повтора ?!
Tima
@Mur: Да, прочтите TextViewдокументацию и посмотрите android:marqueeRepeatLimit.
Кристофер Орр,
1
Этот метод лучше, чем метод с фокусом, поскольку он не влияет на фокус представления. Если у вас есть представления, которые требуют внимания родителей, этот метод испортит фокус. Этот метод прост, эффективен и не полагается на хитрости для постоянной смены фокуса. Спасибо!
Ionut Negru
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 "заставляет его прокручиваться бесконечно
brack
19
Использование этого нарушает выделение фокуса для строки ListView, если TextView находится внутри нее.
Тиви
Прекрасно работает. Простое и элегантное решение. Благодарность!
Rabi
Как запускать и останавливать выделение TextView
Двиведи Джи
Это не сработало для меня, пока я не попробовал другой ответ - textView.setSelected (true);
Justin
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, а видимая область - 320 пикселей (ширина экрана), мы устанавливаем:

tXDelta = 700 - 320 = 380

А как нам определить ширину экрана и ширину текста?


Код

Взяв фрагмент Зара в качестве отправной точки:

    /**
     * @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, не нарушая возможности enabled / onFocus для textView. Он также непрерывно прокручивается, даже если вид не сфокусирован.

Тиви
источник
Мой ответ выше (т.е. добавление одной строки кода :) textView.setSelected(true);не работает в вашей ситуации?
Кристофер Орр,
к сожалению нет. У меня был ListView, заполненный несколькими TextView в каждой строке (отсюда космический кризис = P). каждая строка текстового представления является интерактивной и вызывает контекстное меню. Установка setSelected на true, похоже, нарушает контекстное меню.
Tivie
это? В большинстве случаев это может быть недооценка. Для меня по описанной выше причине было единственным выходом. (это многоразово, кстати!) = P
Tivie
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 Как есть, вы также можете повторно использовать объект анимации для других представлений, которые вы хотите анимировать. ;)
Zarah
Работает хоть и хорошо, но что делать, чтобы отображать длинные тексты ?! Теперь мой текст будет просто обрезан
Тима
@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 и 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);
Сони Кумар
источник