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>
最後,放幾張截圖: