本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布
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 的自定义控件出来了。终于,功夫不负有心人,最终实现了。支持的功能有
我们先来看看效果图吧。
使用 MarqueeView 大概需要三个步骤:
implementation 'com.xj:marqueeView:marqueeView:'
目前最新的版本是 0.1.20,最新版本可以到该网址查看:marqueeView
implementation 'com.xj.marqueeView:marqueeView:0.1.20'
自定义属性说明
属性 | 说明 |
---|---|
mvAnimDuration | 动画执行时间 |
mvInterval | View 翻页时间间隔 |
mvGravity | View 的摆放位置left、center、right |
mvDirection | 动画滚动方向:bottom_to_top、top_to_bottom、right_to_left、left_to_right |
首先,若 MarqueeView 的 ViewType 只有一种类型,那么只需要继承 CommonAdapter 即可
public class SimpleTextAdapter extends CommonAdapter {
public SimpleTextAdapter(Context context, List 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);
看一下效果图:
从 gif 效果图中,我们可以看到,一共有三种 type:
要想实现上述效果,需要两个步骤:
第一步:继承于 ItemViewDelegate,重写 getItemViewLayoutId,isForViewType,convert
方法,其中 getItemViewLayoutId 方法表示返回布局 layoutId,convert 方法在刷新当前 View 的时候会调用,可以用来刷新数据
/**
* Created by xujun on 1/9/2018$ 18:25$.
*/
public class TextItemViewDelegate implements ItemViewDelegate {
@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 {
@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 {
@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 multiItemTypeAdapter = new MultiItemTypeAdapter(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)
void setInterval(int interval)
setInAndOutAnimation(Animation inAnimation, Animation outAnimation)
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 的方式。
如果觉得效果还不错,请 star,谢谢。
MarqueeView:https://github.com/gdutxiaoxu/MarqueeView
一步步拆解 LeakCanary
Android 面试必备 - http 与 https 协议
Android 面试必备 - 计算机网络基本知识(TCP,UDP,Http,https)
Android 面试必备 - 线程
Android_interview github 地址
扫一扫,欢迎关注我的微信公众号 stormjun94, 目前专注于 Android 开发,主要分享 Android开发相关知识和技术人成长历程,包括个人总结,职场经验,面试经验等。