MarqueeText of Android custom View to achieve marquee effect

1. Background

    A text display with marquee effect is needed. This solution is more suitable for students who develop set-top box applications.

2. Demand

    2.1 Display the pages of the book, need to be folded when there is too much text

    2.2 When the focus is on this book, you need to scroll up the folded text

 

3. Analysis of requirements

    3.1 To use the marquee under normal circumstances, you only need to set the following properties of Textview

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

    After the setting is completed, when the TextView has focus and the text is too long, the marquee effect will be displayed. Note that the marquee effect will only be displayed when the TextView requests focus, otherwise it will be invalid.

    3.2 But on the set-top box, the app needs to use the focus effect to display the current item you have selected, which is commonly referred to as "focus".

    3.3 At this time, there will be two problems when using the marquee

        Question 1: My item is in a recycleView. The TextView acting as a marquee is just a child view in the subview of the recycleView. My outer layout needs to get the focus to move normally in the recycleView.

        Question 2: Even if I set the TextView to request focus, when I swipe up from the current item, the recycleView no longer thinks that I am moving between its child views, and the page turning effect of the recycleView will be problematic, that is when you Half of the previous line is displayed, but my focus is already on the previous line, but the previous line is not fully displayed. As shown below:

4. Solutions

    4.1 I need to have a control that does not require a focus but can still show the marquee to avoid conflicts with recycleView

    4.2 At the same time, my control also needs to have the original functions of TextView, so that I can use the marquee effect in simple situations

 

5. Code

    5.1 Layout file resource_item.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="@dimen/dimen_166dp"
    android:layout_height="@dimen/dimen_234dp">

    <ImageView
        android:id="@+id/item_image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitXY"
        android:src="@drawable/column_test_image" />

    <plat.skytv.main.view.MarqueeText
        android:id="@+id/item_name"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dimen_37dp"
        android:alpha="0.6"
        android:singleLine="true"
        android:background="@android:color/black"
        android:gravity="center"
        android:layout_gravity="bottom"
        android:textColor="@color/colorWhite"
        android:textSize="@dimen/dimen_17sp" />

    <ImageView
        android:id="@+id/item_cornerMask"
        style="@style/bigConerMask" />

    <ImageView
        android:id="@+id/item_focus"
        style="@style/FocusStyle"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitXY"
        android:src="@drawable/column_grid_item_selector" />
</FrameLayout>

    5.2 Custom view

package plat.skytv.main.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.TextPaint;
import android.util.AttributeSet;

import androidx.appcompat.widget.AppCompatTextView;

import plat.skytv.main.util.PLog;

public class MarqueeText extends AppCompatTextView implements Runnable {
    private int xLocation;// 当前滚动的位置
    private boolean isStop = false;
    private int textWidth;
    private boolean isMeasure = false;

    private float speed = 1; // 移动速度
    private String string; // 需要绘制得文字


    public MarqueeText(Context context) {
        super(context);
    }

    public MarqueeText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MarqueeText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    float textHeight;


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (!isMeasure) {// 文字宽度只需获取一次就可以了
            getTextWidth();
            textHeight = getTextHeight(getPaint());
            isMeasure = true;
            string = this.getText().toString();
        }
        canvas.drawText(string, xLocation, getHeight() / 2 + textHeight / 2, getPaint());
    }

    /**
     * http://blog.csdn.net/u014702653/article/details/51985821
     * 详细解说了
     *
     * @param
     * @return
     */
    private float getTextHeight(Paint paint) {
        Paint.FontMetrics fontMetrics = paint.getFontMetrics();
        return Math.abs((fontMetrics.bottom - fontMetrics.top)) / 2;
    }

    /**
     * 获取文字宽度
     */
    private void getTextWidth() {
        Paint paint = this.getPaint();
        string = this.getText().toString();
        PLog.e("getTextWidth str = " + string);
        textWidth = (int) paint.measureText(string);
    }

    @Override
    public void run() {
        xLocation -= speed;// 滚动速度
        if (isStop) { // 停止滚动后恢复初始状态
            xLocation = 0;
            return;
        }
        if (textWidth <= (-xLocation)) {
            //也就是说文字已经到头了
            xLocation = getWidth();
        }
        postInvalidate();
        postDelayed(this, 10);
    }

    // 开始滚动 针对不能请求焦点的情况
    public void startScroll() {
        if (textWidth <= this.getWidth()) { // 文字长度 <= view长度,不需要滚动
            return;
        }
        setText("");// 需要设置空 否则展示两个文字
        setSingleLine(false); // 需要设置false,否则无法滚动
        isStop = false;
        this.removeCallbacks(this);
        post(this);

    }

    // 停止滚动
    public void stopScroll() {
        isStop = true;
        setText(string);
        setSingleLine(true);
    }
}

    5.3 Use

    Set focus change monitoring when recycleView binds data

ImageView mItemFocus = itemView.findViewById(R.id.item_focus); // 展示焦点效果的view
MarqueeText mItemName = itemView.findViewById(R.id.item_name);
mItemFocus.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (hasFocus) {
                    mItemName.startScroll();
                } else {
                    mItemName.stopScroll();
                }
            }
        });

6. Problem

    6.1 In the layout file, singleline = true is set. As a result, scrolling is not possible when startScroll is called.

    6.2 Before scrolling, the text of this TextView must be set to an empty string, otherwise two texts will appear.

    6.3 Be suspicious, and post the result after finding the result

7. Reference documents:

    7.1 http://blog.csdn.net/u014702653/article/details/51985821

    7.2 https://www.cnblogs.com/hyhy904/p/11329139.html

Guess you like

Origin blog.csdn.net/HeartCircle/article/details/114001434