มีวิธีทำให้ ellipsize =“ marquee” เลื่อนเสมอหรือไม่?


93

ฉันต้องการใช้เอฟเฟกต์กระโจมบน TextView แต่ข้อความจะถูกเลื่อนเมื่อ TextView ได้รับโฟกัสเท่านั้น นั่นเป็นปัญหาเพราะในกรณีของฉันมันทำไม่ได้

ฉันใช้:

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

มีวิธีให้ TextView เลื่อนข้อความอยู่เสมอหรือไม่? ฉันเคยเห็นสิ่งนี้ทำในแอป Android Market โดยที่ชื่อแอปจะเลื่อนในแถบหัวเรื่องแม้ว่าจะไม่ได้รับโฟกัส แต่ฉันไม่พบสิ่งนี้ที่กล่าวถึงในเอกสาร API


ในการทำงานแบบกระโจมควรเลือก TextView ไม่เน้น โฟกัสให้การเลือก แต่ไม่ใช่การย้อนกลับ
Denis Gladkiy

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

สมบูรณ์แบบ - นี่เป็นเพียงคำตอบที่ฉันกำลังมองหา
Jason

7
คุณควรแทนที่เมธอด onWindowFocusChanged หากคุณต้องการให้กระโจมไม่หยุดเมื่อเมนูปรากฏขึ้น
virsir

7
อย่างไรก็ตามเรามีปัญหากับวิธีแก้ปัญหานี้จริง ๆ เนื่องจากมันทำให้สิ่งที่วาดไม่สมบูรณ์: ถ้าคุณเปลี่ยน drawables ด้วย focus-in / focus-out สิ่งนี้จะพัง เราใช้เวลาเกือบชั่วโมงในการดีบักจนกระทั่งเรารู้ว่าแฮ็คนี้เป็นสาเหตุ
Matthias

คุณสามารถให้รายละเอียดเพิ่มเติมเกี่ยวกับปัญหาได้หรือไม่ ฉันใช้คุณสมบัตินี้ในหลายแอพ ฉันอยากจะดูปัญหานี้ด้วย
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 ที่โฟกัสได้หรือไม่ แต่ยังควรเลื่อนเมื่อไม่ได้โฟกัส Iow สถานะการเลือก "ติด" แม้ในขณะที่สถานะโฟกัสเปลี่ยนไปหรือไม่?
Matthias

ฉันเดาอย่างนั้น ฉันไม่เห็นว่าเหตุใดการเปลี่ยนโฟกัสบนพื้นฐานTextView(กล่าวคือสิ่งที่ไม่ได้ฝังอยู่ในสิ่งที่ "เลือกได้" เช่น a ListView) จะเปลี่ยนสถานะที่เลือก
Christopher Orr

มันใช้ได้ดีสำหรับฉันเช่นกัน ขอบคุณสำหรับคำตอบ. มีความเป็นไปได้ไหมที่จะเปลี่ยนวิธีการทำซ้ำ!
Tima

@Mur: ใช่อ่านเอกสารและใช้เวลาดูที่TextView android:marqueeRepeatLimit
Christopher Orr

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 "ทำให้เลื่อนไปเรื่อย ๆ
วงเล็บ

19
การใช้สิ่งนี้จะทำลายการเลือกโฟกัสของแถว ListView หาก TextView อยู่ภายใน
Tivie

ทำงานได้อย่างสมบูรณ์ โซลูชันที่เรียบง่ายและสง่างาม ขอบคุณ!
Rabi

วิธีการเริ่มต้นและหยุดกระโจมของ TextView
Dwivedi Ji

สิ่งนี้ไม่ได้ผลสำหรับฉันจนกว่าฉันจะลองคำตอบอื่น - 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 ที่มีข้อความที่มีความกว้าง 700px และเราต้องการสร้างภาพเคลื่อนไหวที่ "ดึง" ข้อความเพื่อที่เราจะได้เห็นจุดสิ้นสุดของวลี

                                       (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 Snippet เป็นจุดเริ่มต้น:

    /**
     * @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);ไม่ได้ในสถานการณ์ของคุณหรือไม่?
Christopher Orr

น่าเสียดายที่ไม่ ฉันมี ListView ที่เต็มไปด้วย TextView หลายรายการในแต่ละแถว (ดังนั้นวิกฤตพื้นที่ = P) แต่ละแถวของ textview สามารถคลิกได้และปรากฏเมนูบริบท การตั้งค่า setSelected เป็น true ดูเหมือนว่าจะทำลายเมนูบริบท
Tivie

ใช่ไหม? อาจเป็นการ overshot สำหรับกรณีส่วนใหญ่ สำหรับฉันด้วยเหตุผลที่อธิบายไว้ข้างต้นเป็นทางออกเดียว (มันใช้ซ้ำได้ btw!) = 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
ดูเหมือนจะไม่ง่ายนัก แต่ฉันไม่คิดว่าเขายังต้องการคำตอบ ดูคำตอบที่ยอมรับด้านบน .. :)
Christopher Orr

2
แต่ฉันคิดว่าวิธีนี้ง่ายกว่าการสร้างคลาสใหม่ : D ตามที่เป็นอยู่คุณยังสามารถใช้วัตถุแอนิเมชั่นซ้ำสำหรับมุมมองอื่น ๆ ที่คุณต้องการทำให้เคลื่อนไหวได้ ;)
Zarah

แม้ว่าจะใช้งานได้ดี แต่ฉันจะทำอย่างไรเพื่อแสดงข้อความยาว ๆ ! ตอนนี้ข้อความของฉันจะถูกตัด
Tima

@Mur Votema: คุณลองตั้งค่าความกว้างของ TextView เป็น wrap_content แล้วหรือยัง?
Zarah

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);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.