1. 程式人生 > >Android跑馬燈特效之動態實現多個文字同時一起滾動

Android跑馬燈特效之動態實現多個文字同時一起滾動

       最近做直播專案,有個跑馬燈效果,剛開始用原生的TextView實現,給產品一看,果斷被否定了,於是自定義了一個MarqueeTextView可以自動獲得焦點,加入足夠多的文字的自己就跑動起來了,於是又拿給產品,說是有五個廣告文字同時一起跑動,由於UI請假,只能自行腦補設計效果,然後自定義了一個MarqueeView往上面新增一個view,裡面包含5個文字,一直找產品確認是不是最多隻有5個文,產品也確認過是5個,於是請求資料成功後,把裡面的內容設定到文本里面,但是執行一段時間後發現一個問題,5個文字的文字過多時同時一起跑,跑到3個就消失了,這很顯然和需求不符,第2天后臺添加了一個數據,這樣的話之前固定寫死5個文字的方法肯定不行,於是改為使用橫向的HorisontalScrollView+recycleview,線上程裡面開啟位移動畫這樣也可以實現跑馬燈效果,然後請求到資料後,直接往recycleview裡面適配就好了,這樣使用也出現了一個很奇怪的大bug,HorisontalScrollView+LinearLayout+recycleview在Android7.0及以上系統上最多隻顯示3個數據,於是改為RelativeLayout,這時介面正常顯示,在Activity或Fragment結束時記得清除動畫避免記憶體洩漏。希望小夥伴們接到需求要認真思考,確定後再開始寫程式碼,要不然做很多無用功,來回折騰浪費時間,當然有些不確定的需求也沒辦法,只有我們自己儘量思考全面,做更多準備。

1.使用自定義MarqueeTextView實現跑馬燈效果,程式碼如下:

/**
 * 作者: njb
 * 時間: 2018/12/13 14:55
 * 描述:
 * 來源:
 */
public class MarqueeTextView extends android.support.v7.widget.AppCompatTextView {
    public MarqueeTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

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

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

    @Override

    public boolean isFocused() {
        return true;
    }
}

佈局檔案程式碼:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity"
    android:background="@color/colorAccent">

    <com.example.administrator.timertask.view.MarqueeTextView
        android:id="@+id/marquee_tv"
        android:layout_width="wrap_content"
        android:layout_height="30dp"
        android:layout_marginTop="30dp"
        android:background="@drawable/shape_profit_scroll"
        android:ellipsize="marquee"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:gravity="center"
        android:marqueeRepeatLimit="marquee_forever"
        android:singleLine="true"
        android:text="跑馬燈!GO!GO!GO!跑起來了跑馬燈!GO!GO!GO!跑起來了跑馬燈!GO!GO!GO!跑起來了跑馬燈!GO!跑起來了 "
        android:textColor="#00f"
        android:textSize="12sp"
        android:layout_gravity="center"/>
</LinearLayout>
2.使用自定義的MarqueeView實現跑馬燈效果,程式碼如下:
/**
 * 作者: njb
 * 時間: 2018/12/13 14:55
 * 描述:
 * 來源:
 */
public class MarqueeView extends HorizontalScrollView implements Runnable {
    private Context context;
    private LinearLayout mainLayout;//跑馬燈滾動部分
    private int scrollSpeed = 10;//滾動速度
    private int scrollDirection = LEFT_TO_RIGHT;//滾動方向
    private int currentX;//當前x座標
    private int viewMargin = 20;//View間距
    private int viewWidth;//View總寬度
    private int screenWidth;//螢幕寬度

    public static final int LEFT_TO_RIGHT = 1;
    public static final int RIGHT_TO_LEFT = 2;
    private int scrollDuration = 3000;

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

    public MarqueeView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MarqueeView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.context = context;
        initView();
    }

    void initView() {
        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        screenWidth = wm.getDefaultDisplay().getWidth();
        mainLayout = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.scroll_content, null);
        this.addView(mainLayout);
    }

    public void addViewInQueue(View view){
        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
                LinearLayout.LayoutParams.WRAP_CONTENT);
        lp.setMargins(viewMargin, 0, 0, 0);
        view.setLayoutParams(lp);
        mainLayout.addView(view);
        view.measure(0, 0);//測量view
        viewWidth = viewWidth + view.getMeasuredWidth() + viewMargin;
    }

    //開始滾動
    public void startScroll(){
        removeCallbacks(this);
        currentX = (scrollDirection == LEFT_TO_RIGHT ? viewWidth : -screenWidth);
        post(this);
    }

    //停止滾動
    public void stopScroll(){
        removeCallbacks(this);
    }

    //設定View間距
    public void setViewMargin(int viewMargin){
        this.viewMargin = viewMargin;
    }

    //設定滾動速度
    public void setScrollSpeed(int scrollSpeed){
        this.scrollSpeed = scrollSpeed;
    }

    //設定滾動方向 預設從左向右
    public void setScrollDirection(int scrollDirection){
        this.scrollDirection = scrollDirection;
    }

    //設定滾動間隔時間
    public void setDuration(int scrollDuration){
        this.scrollDuration  = scrollDuration;
        Log.d("scrollDuration",scrollDuration+"");
    }

    @Override
    public void run() {
        switch (scrollDirection){
            case LEFT_TO_RIGHT:
                mainLayout.scrollTo(currentX, 0);
                currentX --;

                if (-currentX >= screenWidth) {
                    mainLayout.scrollTo(viewWidth, 0);
                    currentX = viewWidth;
                }
                break;
            case RIGHT_TO_LEFT:
                mainLayout.scrollTo(currentX, 0);
                currentX ++;

                if (currentX >= viewWidth) {
                    mainLayout.scrollTo(-screenWidth, 0);
                    currentX = -screenWidth;
                }
                break;
            default:
                break;
        }
        postDelayed(this, 100 / scrollSpeed);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        return false;
    }
}

佈局檔案程式碼如下:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:background="#222222">

   <com.example.administrator.timertask.weight.MarqueeView
        android:id="@+id/marquee_view"
        android:layout_width="wrap_content"
        android:layout_height="30dp"
        android:gravity="center"
        android:ellipsize="marquee"
        android:marqueeRepeatLimit="marquee_forever"
        android:singleLine="true"
        android:focusable="true"
        android:scrollHorizontally="true"
        android:focusableInTouchMode="true"
        android:layout_marginTop="40dp"/>
</LinearLayout>

3.使用HorizontalScrollView+Recycleview實現跑馬燈效果,程式碼如下:

package com.example.administrator.timertask;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.TranslateAnimation;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;

import com.example.administrator.timertask.adapter.MarqueeAdapter;
import com.example.administrator.timertask.bean.ProfitBean;

import java.util.ArrayList;
import java.util.List;

public class HorizontalScrollViewActivity extends AppCompatActivity {
    private HorizontalScrollView mNoticeScrollView;
    private TranslateAnimation mRigthToLeftAnim;
    private final static float SCOLL_V = 0.2f;
    private RelativeLayout ll_marquee;
    private MarqueeAdapter marqueeAdapter;
    private List<ProfitBean> mList;
    private RecyclerView recyclerView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_horizontalscrollview);
        initView();
        initAdapter();
    }

    private void initAdapter() {
        mList = new ArrayList<>();
        for(int i=0;i<8;i++){
            ProfitBean profitBean = new ProfitBean();
            profitBean.setName("恭喜張昊天戰隊愛尚值收益222222.88元");
            mList.add(profitBean);
        }
        marqueeAdapter = new MarqueeAdapter(this,mList);
        recyclerView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false));
        recyclerView.setAdapter(marqueeAdapter);
    }

    private void initView() {
        ll_marquee = findViewById(R.id.ll_marquee);

        mNoticeScrollView = (HorizontalScrollView) findViewById(R.id.horiSv);
         recyclerView = findViewById(R.id.rv_marquee);


        recyclerView.post(new Runnable() {
            @Override
            public void run() {
                mRigthToLeftAnim = new TranslateAnimation(mNoticeScrollView.getWidth(), -recyclerView.getWidth(), 0, 0);
                mRigthToLeftAnim.setRepeatCount(Animation.INFINITE);
                mRigthToLeftAnim.setInterpolator(new LinearInterpolator());
                mRigthToLeftAnim.setDuration((long) ((mNoticeScrollView.getWidth() + recyclerView.getWidth()) / SCOLL_V));
                recyclerView.startAnimation(mRigthToLeftAnim);
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        recyclerView.clearAnimation();
    }
}

MarqueeAdapter程式碼:

package com.example.administrator.timertask.adapter;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.example.administrator.timertask.R;
import com.example.administrator.timertask.bean.ProfitBean;

import java.util.List;

public class MarqueeAdapter extends RecyclerView.Adapter<MarqueeAdapter.ViewHolder> {
    private Context mContext;
    private List<ProfitBean> mDatas;

    public MarqueeAdapter(Context mContext, List<ProfitBean> mDatas) {
        this.mContext = mContext;
        this.mDatas = mDatas;
    }


    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(mContext).inflate(R.layout.item_marquee,parent, false);
        ViewHolder myViewHolder = new ViewHolder(view);
        return myViewHolder;
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        holder.itemView.setTag(position);
        ProfitBean profitBean = mDatas.get(position);
        holder.textView.setText(profitBean.getName());
    }

    @Override
    public int getItemCount() {
        return mDatas.size();
    }

    static class ViewHolder extends RecyclerView.ViewHolder{
        TextView textView;

        ViewHolder(View view) {
            super(view);
            textView = view.findViewById(R.id.tv_profit);
        }
    }
}

實體類:

package com.example.administrator.timertask.bean;

public class ProfitBean {
    private String name;
    private String price;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPrice() {
        return price;
    }

    public void setPrice(String price) {
        this.price = price;
    }
}

佈局檔案程式碼:activity_horizontalscrollview

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

    <HorizontalScrollView
        android:id="@+id/horiSv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#222222"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:scrollbars="none">

        <RelativeLayout
            android:id="@+id/ll_marquee"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal">

            <android.support.v7.widget.RecyclerView
                android:id="@+id/rv_marquee"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
        </RelativeLayout>
    </HorizontalScrollView>


</LinearLayout>

最後,放幾張截圖: