Có cách nào để làm cho ellipsize = “marquee” luôn cuộn không?


93

Tôi muốn sử dụng hiệu ứng marquee trên TextView, nhưng văn bản chỉ được cuộn khi TextView được lấy nét. Đó là một vấn đề, bởi vì trong trường hợp của tôi, nó không thể.

Tôi đang dùng:

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

Có cách nào để TextView luôn cuộn văn bản của nó không? Tôi đã thấy điều này được thực hiện trong ứng dụng Android Market, nơi tên ứng dụng sẽ cuộn trong thanh tiêu đề, ngay cả khi nó không nhận được tiêu điểm, nhưng tôi không thấy điều này được đề cập trong tài liệu API.


Để làm cho Marquee hoạt động, TextView nên được chọn, không tập trung. Tiêu điểm cung cấp sự lựa chọn nhưng không phải là ngược lại.
Denis Gladkiy

1
Hãy thử luônMarqueeTextView stackoverflow.com/a/28806003/3496570
AndroidGeek

tôi sẽ thay đổi câu trả lời đúng cho một stackoverflow.com/a/3700651/4548520 này
25

Câu trả lời:


62

Tôi đã phải đối mặt với vấn đề này và giải pháp ngắn nhất mà tôi đưa ra là tạo một lớp mới có nguồn gốc từ TextView. Lớp nên ghi đè ba phương thức onFocusChanged , onWindowFocusChangedisFocused để làm cho TextView đều tập trung.

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

Hoàn hảo - đây chỉ là giải pháp tôi đang tìm kiếm.
Jason

6
Bạn cũng nên ghi đè phương thức onWindowFocusChanged nếu bạn muốn bảng điều khiển không dừng lại khi các menu bật lên.
virsir

7
Nhân tiện, chúng tôi thực sự gặp vấn đề với giải pháp này, vì nó làm rối tung các đồ có thể kéo về trạng thái: nếu bạn thay đổi các đồ có thể kéo bằng focus-in / focus-out, thì điều này sẽ bị hỏng. Chúng tôi đã mất gần một giờ gỡ lỗi cho đến khi chúng tôi nhận ra rằng vụ hack này đã gây ra nó.
Matthias

Bạn có thể cung cấp mô tả chi tiết hơn về vấn đề này không? Tôi sử dụng tính năng này trong nhiều ứng dụng. Tôi cũng muốn xem xét vấn đề này.
hnviet

1
Hack này hoạt động tuyệt vời và chỉ cần thiết khi nhiều TextView trên màn hình cùng một lúc (như khi được sử dụng trong ListView) - bởi vì thường chỉ có một trong số chúng có thể được tập trung, nhưng giải pháp này 'đánh lừa' hệ thống bằng cách nói với tất cả chúng are :) Nếu không thì đối với một TextView đơn giản hơn, nên sử dụng anwer như stackoverflow.com/a/3510891/258848 .
dimsuz

120

Cuối cùng thì hôm nay tôi đã giải quyết được vấn đề này và bắt hierarchyviewerđầu sử dụng ứng dụng Android Market.

Nhìn vào tiêu đề trên màn hình chi tiết của ứng dụng, chúng sử dụng một cái cũ đơn giản TextView. Kiểm tra các thuộc tính của nó cho thấy rằng nó không tập trung, không thể lấy nét và nói chung là rất bình thường - ngoại trừ việc nó được đánh dấu là đã chọn .

Một dòng mã sau đó và tôi đã có nó hoạt động :)

textView.setSelected(true);

Điều này có ý nghĩa, dựa trên những gì Javadoc nói :

Một chế độ xem có thể được chọn hoặc không. Lưu ý rằng lựa chọn không giống như tiêu điểm. Chế độ xem thường được chọn trong ngữ cảnh của một Chế độ xem điều hợp như ListView hoặc GridView.

tức là Khi bạn cuộn qua một mục trong chế độ xem danh sách (như trong ứng dụng Market), chỉ khi đó văn bản hiện đã chọn mới bắt đầu cuộn. Và vì đặc biệt TextViewnày không thể lấy tiêu điểm hoặc có thể nhấp được, nên nó sẽ không bao giờ mất trạng thái lựa chọn.

Thật không may, theo như tôi biết thì không có cách nào để đặt trước trạng thái đã chọn từ XML bố cục.
Nhưng một lớp lót ở trên hoạt động tốt đối với tôi.


chỉ tự hỏi: điều này cũng sẽ hoạt động đối với các TextView có thể lấy tiêu điểm, nhưng vẫn nên cuộn khi không được lấy tiêu điểm? Ồ, trạng thái chọn có "dính" ngay cả khi trạng thái lấy nét thay đổi không?
Matthias

Tôi đoán vậy. Tôi không hiểu tại sao sự thay đổi tiêu điểm trên một tiêu điểm cơ bản TextView(tức là một tiêu điểm không được nhúng vào thứ gì đó "có thể chọn" như a ListView) sẽ thay đổi trạng thái đã chọn.
Christopher Orr

Nó cũng hoạt động tốt cho tôi. Cảm ơn bạn vì câu trả lời. Có khả năng thay đổi cách lặp lại không ?!
Tima

@Mur: Vâng, hãy đọc TextViewtài liệu và xem qua android:marqueeRepeatLimit.
Christopher Orr

1
Phương pháp này tốt hơn phương pháp có tiêu điểm vì nó không gây rối với tiêu điểm của khung nhìn. Nếu bạn có quan điểm yêu cầu cha mẹ họ phải tập trung, phương pháp đó sẽ làm rối tung sự tập trung. Phương pháp này đơn giản, hiệu quả và không dựa vào hack để thay đổi tiêu điểm mọi lúc. Cảm ơn bạn!
Ionut Negru

75

Chỉ cần đặt các tham số này trong TextView của bạn. Nó hoạt động :)

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

1
nếu chuỗi câu trả lời này chưa chết, theo ý kiến ​​của tôi thì đây phải là câu trả lời được chấp nhận. Tôi đã thêm điều này vào định nghĩa XML của mình cho chế độ xem văn bản và nó đã hoạt động trong lần chụp đầu tiên. Tôi đã thử các đề xuất khác, và đây là cách đơn giản nhất. - Ngoài ra, thêm "android: marqueeRepeatLimit =" marquee_forever" làm cho nó di chuyển vô thời hạn
Brack

19
Việc sử dụng này sẽ phá vỡ lựa chọn tiêu điểm của Hàng ListView nếu TextView nằm bên trong nó.
Tivie

Hoạt động hoàn hảo. Giải pháp đơn giản và thanh lịch. Cảm ơn!
Rabi

Làm thế nào để bắt đầu và dừng marquee của TextView
Dwivedi Ji

Điều này không hiệu quả với tôi cho đến khi tôi thử câu trả lời khác - textView.setSelected (true);
Justin

13

TranslateAnimationhoạt động bằng cách "kéo" Chế độ xem theo một hướng theo một lượng xác định. Bạn có thể đặt nơi bắt đầu "kéo" này và nơi kết thúc.

TranslateAnimation(fromXDelta, toXDelta, fromYDelta, toYDelta);

fromXDelta đặt độ lệch của vị trí bắt đầu của chuyển động trong trục 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ác định vị trí kết thúc bù của chuyển động trong trục 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.

Nếu chiều rộng của văn bản của bạn lớn hơn mô-đun của sự khác biệt giữa fromXDelta và toXDelta, thì văn bản sẽ không thể toàn bộ và bắt buộc phải di chuyển trong màn hình.


Thí dụ

Giả sử kích thước màn hình của chúng ta là 320x240 pxs. Chúng tôi có một TextView với văn bản có chiều rộng 700px và chúng tôi muốn tạo hoạt ảnh "kéo" văn bản để chúng tôi có thể nhìn thấy phần cuối của cụm từ.

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

Đầu tiên, chúng tôi thiết lập fromXDelta = 0để chuyển động không có độ lệch ban đầu. Bây giờ chúng ta cần tính toán giá trị toXDelta. Để đạt được hiệu ứng mong muốn, chúng ta cần "kéo" văn bản giống hệt px mà nó kéo ra khỏi màn hình. (trong lược đồ được biểu thị bằng <<<< X px >>>>) Vì văn bản của chúng ta có chiều rộng 700 và vùng hiển thị là 320px (chiều rộng màn hình), chúng tôi đặt:

tXDelta = 700 - 320 = 380

Và làm thế nào để chúng ta xác định Chiều rộng màn hình và Chiều rộng văn bản?


Lấy Zarah Snippet làm điểm khởi đầu:

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

Có thể có nhiều cách dễ dàng hơn để thực hiện việc này, nhưng cách này phù hợp với mọi chế độ xem bạn có thể nghĩ đến và có thể tái sử dụng. Đặc biệt hữu ích nếu bạn muốn tạo hiệu ứng cho TextView trong ListView mà không phá vỡ khả năng bật / onFocus của textView. Nó cũng cuộn liên tục ngay cả khi Chế độ xem không được lấy nét.


Câu trả lời của tôi ở trên (tức là thêm một dòng mã textView.setSelected(true);:) không hoạt động trong trường hợp của bạn?
Christopher Orr

tiếc là không có. Tôi đã có một ListView chứa một số TextView trong mỗi hàng (do đó, khủng hoảng không gian = P). mỗi hàng của chế độ xem văn bản đều có thể nhấp được và bật lên menu ngữ cảnh. Đặt setSelected thành true dường như phá vỡ menu ngữ cảnh.
Tivie

Là nó? Có thể là một loại ảnh chụp quá mức cho hầu hết các trường hợp. Đối với tôi, vì lý do được mô tả ở trên, là giải pháp duy nhất. (nó có thể tái sử dụng, btw!) = P
Tivie

12

Tôi không biết liệu bạn có còn cần câu trả lời hay không, nhưng tôi đã tìm thấy một cách dễ dàng để làm điều này.

Thiết lập hoạt ảnh của bạn như vậy:

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_YEND_POS_Yfloatgiá trị, trong khi TICKER_DURATIONlà một inttôi tuyên bố với các hằng số khác của tôi.

Sau đó, bây giờ bạn có thể áp dụng hoạt ảnh này cho TextView của mình:

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

Và đó là nó. :)

Hoạt ảnh của tôi bắt đầu ở bên phải ngoài màn hình (300f) và kết thúc ở bên trái ngoài màn hình (-300f), với thời lượng là 15 giây (15000).


1
Điều đó có vẻ không đơn giản cho lắm. Nhưng tôi không nghĩ anh ấy vẫn cần câu trả lời; xem câu trả lời được chấp nhận ở trên .. :)
Christopher Orr

2
Nhưng tôi nghĩ giải pháp này đơn giản hơn so với việc tạo một lớp mới. : D Như hiện tại, bạn cũng có thể sử dụng lại đối tượng hoạt ảnh cho các khung nhìn khác mà bạn muốn tạo hoạt ảnh. ;)
Zarah

Mặc dù nó hoạt động tốt, nhưng tôi phải làm gì, để hiển thị các văn bản dài?! Bây giờ văn bản của tôi sẽ bị cắt
Tima

@Mur Votema: Bạn đã thử đặt chiều rộng TextView của mình thành wrap_content chưa?
Zarah

3
Không phải là một giải pháp tuyệt vời, vì các tham số kích thước và thời lượng phụ thuộc rất nhiều vào kích thước văn bản. Hơn nữa, sẽ tạo hoạt ảnh, ngay cả khi chiều dài văn bản nhỏ hơn chiều rộng chế độ xem. setSelected (true) thực sự là giải pháp dễ dàng nhất.
Anm

5

Tôi đã viết mã sau cho một ListView với các mục văn bản marquee. Nó dựa trên giải pháp setSelected được mô tả ở trên. Về cơ bản, tôi đang mở rộng lớp ArrayAdapter và ghi đè phương thức getView để chọn TextView trước khi trả về:

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

Đây là câu trả lời xuất hiện trên đầu tìm kiếm google của tôi, vì vậy tôi nghĩ rằng tôi có thể đăng một câu trả lời hữu ích ở đây vì tôi phải vật lộn với việc ghi nhớ điều này khá thường xuyên. Dù sao, điều này phù hợp với tôi và yêu cầu các thuộc tính XML và 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" />

// Trong Java

        mtvMarque.setEllipsize(TextUtils.TruncateAt.MARQUEE);
        mtvMarque.setSelected(true);
        mtvMarque.setSingleLine(true);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.