简体   繁体   English

Android:如何使用选取框从 Textview 中删除结尾空白

[英]Android: How to remove end blank space from Textview with marquee

I have created TextView with marquee, but I want to remove the blank space between end and start showing the text again.我已经创建了带有选取框的 TextView,但我想删除 end 之间的空格并再次开始显示文本。

Here is the screen这是屏幕

http://i.stack.imgur.com/dfdT8.png http://i.stack.imgur.com/dfdT8.png

Here is my layout这是我的布局

<TextView
        android:id="@+id/text1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true"
        android:ellipsize="marquee"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:marqueeRepeatLimit="marquee_forever"
        android:scrollHorizontally="true"
        android:singleLine="true"
        android:text="dkfjdkf jdfjfsfjkfa asdfjakjfdkasf sfdjaskfjdksf sfdjsfjk gfhgfhfai ggfghgf" />

Use :利用 :

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

If by blank space you're referring to those black stripes before and after the TextView, you can adjust your layout witdh to android:layout_width="match_parent"如果通过空格你指的是TextView之前和之后的那些黑色条纹,你可以将你的布局调整为android:layout_width="match_parent"

<TextView
        android:id="@+id/text1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true"
        android:ellipsize="marquee"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:marqueeRepeatLimit="marquee_forever"
        android:scrollHorizontally="true"
        android:singleLine="true"
        android:text="dkfjdkf jdfjfsfjkfa asdfjakjfdkasf sfdjaskfjdksf sfdjsfjk gfhgfhfai ggfghgf" />

Also, make sure the parent layout of that TextView is not applying any margins此外,请确保该 TextView 的父布局未应用任何边距

This solution works up until API 28 when they introduced restrictions on non-SDK interfaces .API 28引入对非 SDK 接口的限制时,此解决方案一直有效。


I ran into the same problem and I couldn't find a solution online.我遇到了同样的问题,我在网上找不到解决方案。 I managed to solve the problem myself by looking at the source code for TextView and referencing other answers that provide an option to control the scroll speed .通过查看TextView 的源代码并参考 其他提供控制滚动速度选项的答案,我自己设法解决了这个问题。

Looking at the source code, it will set the gap or blank space to be 1/3 the size of your TextView.查看源代码,它将间隙或空白空间设置为 TextView 大小的 1/3。 So if your TextView is 600 pixels wide, it will create a 200 pixel gap between the start and end of the message.因此,如果您的 TextView 是 600 像素宽,它将在消息的开头和结尾之间创建 200 像素的间隙。 This solution will aim to make that value configurable.该解决方案旨在使该值可配置。

The tricky part is that the logic you need to override is all within private classes, fields, and methods on the super class.棘手的部分是您需要覆盖的逻辑都在超类的私有类、字段和方法中。 This makes the solution far less trivial.这使得解决方案变得不那么简单。

Add this class to your project... CustomTextView.java将此类添加到您的项目中... CustomTextView.java

public class CustomTextView extends AppCompatTextView {

    private static final float NEW_GAP = 0F;

    Object marqueeObject;
    Field mStatusField;
    Field mGhostStartField;
    Field mMaxScrollField;
    Field mGhostOffsetField;
    Field mFadeStopField;
    Field mMaxFadeScrollField;

    public CustomTextView(Context context) {
        super(context);
        setSelected(true);
    }

    public CustomTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setSelected(true);
    }

    public CustomTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setSelected(true);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        try {
            initMarqueeObject();

            if (didMarqueeRestart()) {
                // We need to update the values each time it restarts
                updateMarqueeFieldValues();
            }
        }
        catch(Exception exception) {
            exception.printStackTrace();
        }

        super.onDraw(canvas);
    }

    private void initMarqueeObject() throws Exception {
        if (marqueeObject == null) {
            Field marqueeField = getClass().getSuperclass().getSuperclass().getDeclaredField("mMarquee"); // use if extending from AppCompatTextView
            // Field marqueeField = getClass().getSuperclass().getDeclaredField("mMarquee"); // use if extending from TextView
            marqueeField.setAccessible(true);
            marqueeObject = marqueeField.get(this);
            initMarqueeFields();
        }
    }

    private void initMarqueeFields() throws Exception {
        mStatusField = marqueeObject.getClass().getDeclaredField("mStatus");
        mStatusField.setAccessible(true);
        mGhostStartField = marqueeObject.getClass().getDeclaredField("mGhostStart");
        mGhostStartField.setAccessible(true);
        mMaxScrollField = marqueeObject.getClass().getDeclaredField("mMaxScroll");
        mMaxScrollField.setAccessible(true);
        mGhostOffsetField = marqueeObject.getClass().getDeclaredField("mGhostOffset");
        mGhostOffsetField.setAccessible(true);
        mFadeStopField = marqueeObject.getClass().getDeclaredField("mFadeStop");
        mFadeStopField.setAccessible(true);
        mMaxFadeScrollField = marqueeObject.getClass().getDeclaredField("mMaxFadeScroll");
        mMaxFadeScrollField.setAccessible(true);
    }

    private boolean didMarqueeRestart() throws Exception {
        byte currentState = mStatusField.getByte(marqueeObject);
        return currentState == 0x1; // 0x1 is the byte object for the MARQUEE_STARTING state
    }

    private void updateMarqueeFieldValues() throws Exception {
        float textWidth = getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight();
        float originalGap = textWidth / 3.0F;
        // We have to calculate the lineWidth based on the original value
        float originalValueStartValue = mGhostStartField.getFloat(marqueeObject);
        float lineWidth = originalValueStartValue + textWidth - originalGap;
        float mGhostStart = lineWidth - textWidth + NEW_GAP;
        mGhostStartField.setFloat(marqueeObject, mGhostStart);
        mMaxScrollField.setFloat(marqueeObject, mGhostStart + textWidth);
        mGhostOffsetField.setFloat(marqueeObject, lineWidth + NEW_GAP);
        mFadeStopField.setFloat(marqueeObject, lineWidth);
        mMaxFadeScrollField.setFloat(marqueeObject, mGhostStart + lineWidth + lineWidth);
    }
}

You can now easily adjust the extra blank space using the NEW_GAP variable.您现在可以使用NEW_GAP变量轻松调整额外的空白空间。 In this example, it is set to 0 pixels.在本例中,它设置为 0 像素。 Set that constant to any pixel value that you wish to use.将该常数设置为您希望使用的任何像素值。

NOTE: this solution is extending from AppCompatTextView .注意:此解决方案是从AppCompatTextView扩展而来的。 If you are extending from TextView you should uncomment the relevant code in the initMarqueeObject() method.如果您从TextView扩展,您应该取消注释initMarqueeObject()方法中的相关代码。

Then just just use this new class in place of the old TextView in XML...然后只需使用这个新类代替 XML 中的旧 TextView ......

<com.yourpackagename.CustomTextView
    android:focusable="true"
    android:focusableInTouchMode="true"
    android:singleLine="true"
    android:scrollHorizontally="true"
    android:ellipsize="marquee"
    android:marqueeRepeatLimit="marquee_forever"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

There is no need to call setSelected(true) in your code as this is done internally within the new class.无需在代码中调用setSelected(true) ,因为这是在新类内部完成的。

This may not work forever as it will break if they rename certain variables or change the logic of TextView in future releases.这可能不会永远有效,因为如果他们重命名某些变量或在未来版本中更改TextView的逻辑,它将中断。 But it has worked great on all versions that I have tested it on.但它在我测试过的所有版本上都运行良好。

Hopefully this helps!希望这会有所帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM