Androd自定义View实现竖直跑马灯效果,对其用自定义控件进行包装;实现其点击回调和自定义视图等功能
跑马灯在我们日常使用的app中还是很常见的,以前做外卖app的时候商家公告就使用了此效果,但是它是横向滚动的,横向滚动多适用于单条信息;但凡涉及到多条信息的滚动展示,用纵向滚动效果会有更好的用户体验,今天我们通过自定义View来看看如何实现纵向跑马灯效果。
MarqueeBean类
public class MarqueeBean<E> {
private String title;
private E img;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public E getImg() {
return img;
}
public void setImg(E img) {
this.img = img;
}
}
**VerMarqueeView类**
public class VerMarqueeView extends ViewFlipper {
private Context mContext;
private List<MarqueeBean> marquees;
private boolean isSetAnimDuration = false;
private OnItemClickListener onItemClickListener;
private int interval = 2000;//播放时间间隔
private int animDuration = 500;//动画时长
private int textSize = 14;//文字大小
private boolean isImage = true;//是否显示图片
private boolean isImageResource = true;//是否是图片资源(还是网络图片Url)
public VerMarqueeView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
this.mContext = context;
if (marquees == null) {
marquees = new ArrayList<>();
}
TypedArray typedArray = getContext().obtainStyledAttributes(attrs,
R.styleable.MarqueeViewStyle, defStyleAttr, 0);
interval = typedArray.getInteger(R.styleable.MarqueeViewStyle_mvInterval, interval);
isSetAnimDuration = typedArray.hasValue(R.styleable.MarqueeViewStyle_mvAnimDuration);
animDuration = typedArray.getInteger(R.styleable.MarqueeViewStyle_mvAnimDuration, animDuration);
if (typedArray.hasValue(R.styleable.MarqueeViewStyle_mvTextSize)) {
textSize = (int) typedArray.getDimension(R.styleable.MarqueeViewStyle_mvTextSize, textSize);
textSize = px2sp(mContext, textSize);
}
typedArray.recycle();
setFlipInterval(interval);
}
/**
* @param notice
* @description: 根据公告字符串启动轮播
*/
public void startWithText(final String notice) {
if (TextUtils.isEmpty(notice)) return;
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
getViewTreeObserver().addOnGlobalLayoutListener(this);
startWithFixedWidth(notice, getWidth());
}
});
}
/**
* @param marquees
* @description: 根据公告字符串列表启动轮播
*/
public void startWithList(List<MarqueeBean> marquees) {
setMarquees(marquees);
start();
}
//
/**
* @param notice
* @description: 根据宽度和公告字符串启动轮播
*/
private void startWithFixedWidth(String notice, int width) {
int noticeLength = notice.length();
int dpW = px2dp(mContext, width);
int limit = dpW / textSize;
if (dpW == 0) {
throw new RuntimeException("Please set MarqueeView width !");
}
List list = new ArrayList();
if (noticeLength <= limit) {
list.add(notice);
} else {
int size = noticeLength / limit + (noticeLength % limit != 0 ? 1 : 0);
for (int i = 0; i < size; i++) {
int startIndex = i * limit;
int endIndex = ((i + 1) * limit >= noticeLength ? noticeLength : (i + 1) * limit);
list.add(notice.substring(startIndex, endIndex));
}
}
marquees.addAll(list);
start();
}
/**
* @description 启动轮播
*/
public boolean start() {
if (marquees == null || marquees.size() == 0) return false;
removeAllViews();
resetAnimation();
for (int i = 0; i < marquees.size(); i++) {
final View view = createView(i);
final int finalI = i;
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onItemClickListener != null) {
onItemClickListener.onItemClick(finalI, view);
}
}
});
addView(view);
}
if (marquees.size() > 1) {
startFlipping();
} else {
stopFlipping();
}
return true;
}
/**
* @description 重置动画
*/
private void resetAnimation() {
clearAnimation();
Animation animIn = AnimationUtils.loadAnimation(mContext, R.anim.anim_marquee_in);
if (isSetAnimDuration) animIn.setDuration(animDuration);
setInAnimation(animIn);
Animation animOut = AnimationUtils.loadAnimation(mContext, R.anim.anim_marquee_out);
if (isSetAnimDuration) animOut.setDuration(animDuration);
setOutAnimation(animOut);
}
/**
* @param position
* @return
* @description 创建ViewFlipper下的View
*/
private View createView(int position) {
MarqueeBean marquee = marquees.get(position);
View view = LayoutInflater.from(mContext).inflate(R.layout.view_marquee, null);
ImageView ivMarquee = view.findViewById(R.id.ivMarquee);
TextView tvMarquee = view.findViewById(R.id.tvMarquee);
tvMarquee.setText(marquee.getTitle());
if (isImage) {
ivMarquee.setVisibility(VISIBLE);
if (isImageResource) {
ivMarquee.setImageResource((Integer) marquee.getImg());
} else {
Glide.with(mContext)
.load(marquee.getImg())
.placeholder(R.mipmap.ic_launcher)
.dontAnimate()
.into(ivMarquee);
}
}
tvMarquee.setTextSize(textSize);
view.setTag(position);
return view;
}
public int getPosition() {
return (int) getCurrentView().getTag();
}
public List<MarqueeBean> getMarquees() {
return marquees;
}
public void setMarquees(List<MarqueeBean> marquees) {
this.marquees = marquees;
}
/**
* @param isImage
* @description 是否显示图片
*/
public void setShowImage(boolean isImage) {
this.isImage = isImage;
}
/**
* @param isImageResource
* @description 是否是图片资源(还是网络图片Url)
*/
public void setShowImageResource(boolean isImageResource) {
this.isImageResource = isImageResource;
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
public interface OnItemClickListener {
void onItemClick(int position, View view);
}
public int px2dp(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
public int px2sp(Context context, float pxValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (pxValue / fontScale + 0.5f);
}
}
**MainActivity类**
public class MainActivity extends AppCompatActivity {
private VerMarqueeView vmText, vmTextImgResource, vmTextImgUrl;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
showTextMarquee();
showTextImgResourceMarquee();
showTextImgUrlMarquee();
}
private void showTextImgResourceMarquee() {
List<MarqueeBean> marquees = new ArrayList<>();
for (int i = 0; i < 3; i++) {
MarqueeBean marquee = new MarqueeBean();
marquee.setImg(R.drawable.head_man);
marquee.setTitle("我是本地图片和文字的跑马灯" + i);
marquees.add(marquee);
}
vmTextImgResource.setShowImage(true);
vmTextImgUrl.setShowImageResource(true);
vmTextImgResource.startWithList(marquees);
}
private void initView() {
vmText = findViewById(R.id.vm_main_text);
vmTextImgResource = findViewById(R.id.vm_main_text_img_resource);
vmTextImgUrl = findViewById(R.id.vm_main_text_img_url);
}
private void showTextImgUrlMarquee() {
String[] imgs = new String[]{
"https://ws1.sinaimg.cn/large/610dc034ly1fhovjwwphfj20u00u04qp.jpg",
"https://ws1.sinaimg.cn/large/610dc034ly1fhnqjm1vczj20rs0rswia.jpg",
"https://ws1.sinaimg.cn/large/610dc034ly1fhj5228gwdj20u00u0qv5.jpg"};
List<MarqueeBean> marqueeBeanList = new ArrayList<>();
for (int i = 0; i < 3; i++) {
MarqueeBean marquee = new MarqueeBean();
marquee.setImg(imgs[i]);
marquee.setTitle("我是网络图片和文字的跑马灯" + i);
marqueeBeanList.add(marquee);
}
vmTextImgUrl.setShowImage(true);
vmTextImgUrl.setShowImageResource(false);
vmTextImgUrl.startWithList(marqueeBeanList);
vmTextImgUrl.setOnItemClickListener(new VerMarqueeView.OnItemClickListener() {
@Override
public void onItemClick(int position, View textView) {
Toast.makeText(MainActivity.this, "======" + position, Toast.LENGTH_SHORT).show();
}
});
}
private void showTextMarquee() {
List<MarqueeBean> marqueeBeanList = new ArrayList<>();
for (int i = 0; i < 3; i++) {
MarqueeBean marquee1 = new MarqueeBean();
marquee1.setTitle("我是没有图片的跑马灯" + i);
marqueeBeanList.add(marquee1);
}
vmText.setShowImage(false);
vmText.startWithList(marqueeBeanList);
}
}