1. 程式人生 > >Android 上下滾動(跑馬燈)效果實現

Android 上下滾動(跑馬燈)效果實現

產品的有個需求是 文字上下滾動,第一想到的是用屬性動畫實現,2個TextView 切換滾動,網上看了一堆資料大部分都是 TextSwitch,寫部落格不貼效果圖真的是很惆悵,不知道具體效果如何,第一次進入切換是否有問題,還有就是最後一個切換到第二個是否有問題,動畫是否流暢等,啥都看不到。所以還是按照第一個想法,找到了類似的做法,但是好像有點問題,於是修改一番,效果圖如下:(gif幀率有點低,不是很順暢,真機上挺好的)

思路就是2個TextView 利用屬性動畫上下切換,延遲傳送執行緒觸發下次滾動。

程式碼如下:

1.ScrrollTextView.java

import android.animation.ObjectAnimator;
import android.content.Context;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.lanjinger.choiassociatedpress.R;

import java.util.List;

/**
 * 上下滾動的 textView
 */
public class ScrollTextView extends LinearLayout {
    private TextView mBannerTV1;
    private TextView mBannerTV2;
    private Handler handler;
    private boolean isShow = false;
    private int startY1, endY1, startY2, endY2;
    private Runnable runnable;
    private List<String> list;
    private int position = 0;
    private int offsetY = 100;
    private boolean hasPostRunnable = false;

    public ScrollTextView(Context context) {
        this(context, null);
    }

    public ScrollTextView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ScrollTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        View view = LayoutInflater.from(context).inflate(R.layout.widget_scroll_text_layout, this);
        mBannerTV1 = view.findViewById(R.id.tv_banner1);
        mBannerTV2 = view.findViewById(R.id.tv_banner2);
        handler = new Handler();
        runnable = new Runnable() {
            @Override
            public void run() {
                isShow = !isShow;
                if (position == list.size() - 1) {
                    position = 0;
                }

                if (isShow) {
                    mBannerTV1.setText(list.get(position++));
                    mBannerTV2.setText(list.get(position));
                } else {
                    mBannerTV2.setText(list.get(position++));
                    mBannerTV1.setText(list.get(position));
                }

                startY1 = isShow ? 0 : offsetY;
                endY1 = isShow ? -offsetY : 0;
                ObjectAnimator.ofFloat(mBannerTV1, "translationY", startY1, endY1).setDuration(300).start();

                startY2 = isShow ? offsetY : 0;
                endY2 = isShow ? 0 : -offsetY;
                ObjectAnimator.ofFloat(mBannerTV2, "translationY", startY2, endY2).setDuration(300).start();

                handler.postDelayed(runnable, 3000);
            }
        };
    }

    public List<String> getList() {
        return list;
    }

    public void setList(List<String> list) {
        this.list = list;

        //處理最後一條資料切換到第一條資料 太快的問題
        if (list.size() > 1) {
            list.add(list.get(0));
        }
    }

    public void startScroll() {
        mBannerTV1.setText(list.get(0));
        if (list.size() > 1) {
            if(!hasPostRunnable) {
                hasPostRunnable = true;
                //處理第一次進入 第一條資料切換第二條 太快的問題
                handler.postDelayed(runnable,3000);
            }
        } else {
            //只有一條資料不進行滾動
            hasPostRunnable = false;
//            mBannerTV1.setText(list.get(0));
        }
    }

    public void stopScroll() {
        handler.removeCallbacks(runnable);
        hasPostRunnable = false;
    }


}

注意點:1.就是開啟和關閉滾動,都需要在控制元件外面控制。如果需要在控制元件內部自行控制也可以,

onAttachedToWindow 這裡開啟執行緒,注意判斷列表是否為空。

2.當列表資料大於1條是,我會手動在後面再加上第一條資料,為了避免,最後一條切換到一條是滾動太快(即動效不明顯的問題),我這裡沒有判空,因為傳遞過來是一定有資料的,為了嚴瑾可以加上判空.

2.widget_scroll_text_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <TextView
        android:id="@+id/tv_banner1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:ellipsize="end"
        android:singleLine="true"
        android:textColor="@color/skin_common_title"
        android:textSize="12sp" />

    <TextView
        android:id="@+id/tv_banner2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:ellipsize="end"
        android:singleLine="true"
        android:textColor="@color/skin_common_title"
        android:textSize="12sp" />

</RelativeLayout>

3.具體呼叫

        ScrollTextView marqueeText = headView.findViewById(R.id.xxxx);

        List<String> demographicsList = new ArrayList<>();

        demographicsList.add("今日測試股票 上市");
        demographicsList.add("今日科倫藥業 中國人保 可申購");
        demographicsList.add("今日中國平安 上市");

        marqueeText.setList(demographicsList);
        marqueeText.startScroll();

注意:正確處理生命週期的問題,什麼時候呼叫starScroll(),什麼時候呼叫stopScroll(),根據具體的生命週期而定;

尾:

感覺這樣寫想要啥動效都可以自行定義,更具需求定製屬性動畫就好了,也不需要多餘的new TextView();簡單的看了下,好像TextSwitch 要麻煩點。

今日任務做完,研究 TextSwitch 去,對比下2者的優缺點;