Android 自定義 MarqueeView 實現跑馬燈效果 - 使用說明
本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家釋出
ofollow,noindex">Android 自定義 MarqueeView 實現跑馬燈效果 - 使用說明
Android 自定義 MarqueeView 實現跑馬燈 —— 原理篇
前言
我們知道,Android TextView 預設支援跑馬燈效果,但是不夠靈活,比如不能支援設定動畫執行時長,動畫效果等。
Github 上面也有一些優秀,實用的開源庫。
作者 | 開源庫 | star | 區別 | 實現原理 |
---|---|---|---|---|
sunfusheng | MarqueeView | 2.5k + | 支援對 View 進行復用,僅支援 TextView(內部最多有三個 TextView) | 基於 ViewFilp 實現 |
gongwen | MarqueeViewLibrary | 1.7k + | 不支援對 View 進行復用,支援各種 View | 基於 ViewFilp 實現 |
於是,我在想,能不能開發出一款支援對 View 進行復用,同時支援各種 View 的自定義控制元件出來了。終於,功夫不負有心人,最終實現了。支援的功能有
- 支援各種 View,通過 type 進行區分
- 內部對 View 進行復用,有多少種 type,內部就有多少個 View。
- 支援 view 的擺放位置(想做,向右,居中)
- 支援各種動畫,從上而下,從左而右 等,設定動畫時長
- 支援自定義動畫
- 支援監聽每一個 item 的點選事件
- 支援監聽 flip 事件,即當前 flip 到哪個 item
效果圖
我們先來看看效果圖吧。

image
使用說明
使用 MarqueeView 大概需要三個步驟:
第一步:在 Gradle 檔案中配置:
implementation 'com.xj:marqueeView:marqueeView:<latest-version>'
目前最新的版本是 0.1.20,最新版本可以到該網址檢視: marqueeView
implementation 'com.xj.marqueeView:marqueeView:0.1.20'
第二步:在 XML 檔案中使用
<com.xj.marqueeview.MarqueeView android:id="@+id/mv_multi_text5" android:layout_width="match_parent" android:layout_height="@dimen/mv_multi_text_height" android:layout_marginTop="10dp" android:background="@mipmap/bg" app:mvAnimDuration="500" app:mvDirection="top_to_bottom" app:mvInterval="3000"> </com.xj.marqueeview.MarqueeView>
自定義屬性說明
屬性 | 說明 |
---|---|
mvAnimDuration | 動畫執行時間 |
mvInterval | View 翻頁時間間隔 |
mvGravity | View 的擺放位置left、center、right |
mvDirection | 動畫滾動方向:bottom_to_top、top_to_bottom、right_to_left、left_to_right |
第三步:給 MarqueeView 設定 Adapater
首先,若 MarqueeView 的 ViewType 只有一種型別,那麼只需要繼承 CommonAdapter 即可
public class SimpleTextAdapter extends CommonAdapter<String> { public SimpleTextAdapter(Context context, List<String> datas) { super(context, R.layout.item_simple_text, datas); } @Override protected void convert(ViewHolder viewHolder, String item, int position) { TextView tv = viewHolder.getView(R.id.tv); tv.setText(item); } }
SimpleTextAdapter simpleTextAdapter = new SimpleTextAdapter(mContext, datas); simpleTextAdapter.setOnItemClickListener(new MultiItemTypeAdapter.OnItemClickListener() { @Override public void onItemClick(int position, View view) { Log.i(TAG, "onItemClick: position = " + position); if (marqueeView.isStart()) { marqueeView.stopFilp(); } else { marqueeView.startFlip(); } } }); marqueeView.setAdapter(simpleTextAdapter);
看一下效果圖:

image
更多用法
支援不同的 ViewType

image
從 gif 效果圖中,我們可以看到,一共有三種 type:
- 只含有 TextView
- 含有一個 ImageView 和 TextView
- 含有兩個 TextView 和 一個ImageView
要想實現上述效果,需要兩個步驟:
第一步:繼承於 ItemViewDelegate,重寫 getItemViewLayoutId,isForViewType,convert
方法,其中 getItemViewLayoutId 方法表示返回佈局 layoutId,convert 方法在重新整理當前 View 的時候會呼叫,可以用來重新整理資料
/** * Created by xujun on 1/9/2018$ 18:25$. */ public class TextItemViewDelegate implements ItemViewDelegate<MultiTypeBean> { @Override public int getItemViewLayoutId() { return R.layout.item_simple_text; } @Override public boolean isForViewType(MultiTypeBean item, int position) { return item.mItemViewType == MultiTypeBean.ItemViewType.text; } @Override public void convert(ViewHolder holder, MultiTypeBean multiTypeBean, int position) { TextView tv = holder.getView(R.id.tv); tv.setText(multiTypeBean.title); } }
public class ImageTextItemViewDelegate implements ItemViewDelegate<MultiTypeBean> { @Override public int getItemViewLayoutId() { return R.layout.item_image_text; } @Override public boolean isForViewType(MultiTypeBean item, int position) { return item.mItemViewType == MultiTypeBean.ItemViewType.imageText; } @Override public void convert(ViewHolder holder, MultiTypeBean multiTypeBean, int position) { TextView tv = holder.getView(R.id.tv); tv.setText(multiTypeBean.title); ImageView iv = holder.getView(R.id.iv); iv.setImageResource(multiTypeBean.resImageId); } }
public class MultiTextItemViewDelegate implements ItemViewDelegate<MultiTypeBean> { @Override public int getItemViewLayoutId() { return R.layout.item_multi_text; } @Override public boolean isForViewType(MultiTypeBean item, int position) { return item.mItemViewType == MultiTypeBean.ItemViewType.multiTextAndImage; } @Override public void convert(ViewHolder holder, MultiTypeBean multiTypeBean, int position) { TextView tv = holder.getView(R.id.tv); tv.setText(multiTypeBean.title); TextView tvContent = holder.getView(R.id.tv_content); tvContent.setText(multiTypeBean.content); ImageView iv = holder.getView(R.id.iv); iv.setImageResource(multiTypeBean.resImageId); } }
第二步:將 ItemViewDelegate 新增到 MultiItemTypeAdapter 中,並給 marqueeView 設定 Adapter。
MultiItemTypeAdapter<MultiTypeBean> multiItemTypeAdapter = new MultiItemTypeAdapter<MultiTypeBean>(mContext, datas); multiItemTypeAdapter.addItemViewDelegate(new TextItemViewDelegate()); multiItemTypeAdapter.addItemViewDelegate(new ImageTextItemViewDelegate()); multiItemTypeAdapter.addItemViewDelegate(new MultiTextItemViewDelegate()); multiItemTypeAdapter.setOnItemClickListener(new MultiItemTypeAdapter.OnItemClickListener() { @Override public void onItemClick(int position, View view) { Log.i(TAG, "onItemClick: position = " + position); if (marqueeView.isStart()) { marqueeView.stopFilp(); } else { marqueeView.startFlip(); } } }); marqueeView.setAdapter(multiItemTypeAdapter);
其他用法
- 設定佈局的對齊方向:
void setGravity(int gravity)
- 設定動畫的方向:
void setDirection(int direction)
- 設定動畫的執行時間:(內建動畫支援,自定義動畫不支援)
void setAnimDuration(int animDuration)
- 設定兩個 View 的輪播間隔
void setInterval(int interval)
- 設定進入進出動畫(即自定義動畫)
setInAndOutAnimation(Animation inAnimation, Animation outAnimation)
- 設定 Flip 監聽
void setIFlipListener(IFlipListener IFlipListener)
mMvMultiText5.setIFlipListener(new MarqueeView.IFlipListener() { @Override public void onFilpStart(int position, View view) { Log.i(TAG, "onFilpStart: position = " + position); } @Override public void onFilpSelect(int position, View view) { Log.i(TAG, "onFilpSelect: position = " + position); } });
- 設定點選事件監聽
simpleTextAdapter.setOnItemClickListener(new MultiItemTypeAdapter.OnItemClickListener() { @Override public void onItemClick(int position, View view) { Log.i(TAG, "onItemClick: position = " + position); if (marqueeView.isStart()) { marqueeView.stopFilp(); } else { marqueeView.startFlip(); } } });
當然,以上功能也支援自定義屬性:
自定義屬性說明
屬性 | 說明 |
---|---|
mvAnimDuration | 動畫執行時間 |
mvInterval | View 翻頁時間間隔 |
mvGravity | View 的擺放位置left、center、right |
mvDirection | 動畫滾動方向:bottom_to_top、top_to_bottom、right_to_left、left_to_right |
感謝
https://github.com/hongyangAndroid/baseAdapter
參考了鴻洋大佬 baseAdapter 的大部分用法
https://github.com/sunfusheng/MarqueeView
裡面 View 的複用也給了我相應的思路。不過 ViewFliper 無法實現多種 ViewType 的複用,最終捨棄了該方案,採用自定義 FrameLayout 的方式。
關於我
GitHub: gdutxiaoxu
CSDN 部落格:https://blog.csdn.net/gdutxiaoxu
簡書主頁: https://www.jianshu.com/u/ca9b3e19f454
個人微信公眾號:

image
如果覺得效果還不錯,請 star,謝謝。