最近看UI再设计新的APP效果图,发现首页使用了类似京东首页京东日报模块类似的广告自动滚动效果,于是就想着提前准备,写个通用组件。网上查阅了很多的文章,绝大多数都是说使用TextSwitcher来实现这个效果。我也用TextSwitcher实践了一下,确实能实现,然后也很简单只需要给TextSwitcher设置inAnimation(进入动画),outAnimation(离开动画),factory(在他的makeView中生成View),然后需要一个Handler定时切换即可完成,不理解的同学可以去看一下TextSwitcher的用法。
但是,无意间,发现淘宝首页也有这么一个功能,叫淘宝头条,虽然说都是广告的上下滚动,但是淘宝头条的内容还有有区别的,他包含了两条文本广告信息,以及一张图片。
此时,这样子的需求,使用TextSwitcher是无法满足我们的需求了,原因很简单,TextSwitcher中的子View是由我们设置的Factory的makeView方法创建的,而这个方法返回的,必须是TextView对象,这一点再TextSwitcher的源码中可以证实.
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
if (!(child instanceof TextView)) {
throw new IllegalArgumentException(
"TextSwitcher children must be instances of TextView");
}
super.addView(child, index, params);
}
其实,android SDK中不仅仅提供了用于文本滚动的TextSwitcher,同时还提供了用于图片滚动的ImageSwitcher,但是他们都无法满足我们的需求,因此,就寻思着是否可以有一个自定义布局的组件,来实现这个功能呢,这时候我查阅了一下TextSwitcher与ImageSwitcher,发现他们都是ViewSwitcher的子类,那就通过一个集成ViewSwitcher来实现我们的需求吧。
废话不多说,先把效果图拿上来(似乎已经扯了很多废话了。。。)
整个流程其实很简单,我们只需要将一开始讲述的几点封装到我们的Switcher中就可以了,首先我们定义一个Adapter,让他作为数据与Switcher之间的桥梁,同时,这边使用了泛型,让我们使用过程中更加灵活
public interface IAdvertAdapter {
/**
* 获取当前数据个数
* @return
*/
public int getCount();
/**
* 获取position位置对应的数据
* @param position
* @return
*/
public T getItem(int position);
/**
* 创建View
* 这边仅仅是创建View,不要给View绑定数据,或者添加事件监听之类
* Only new View or inflate layout
* @return
*/
public View makeView();
/**
* 为View绑定数据,同时添加对应的事件监听
* @param view
* @param data
*/
public void bindView(View view, T data);
}
然后,重点来了,创建我们的组件,继承ViewSwitcher,同时也实现ViewSwitcher.ViewFactory接口,该类中其实也没有太多动心,就不贴出来了,下面会给出源码地址,比较核心的还是Handler,实现自动轮播,定时发送一条消息,然后调用ViewSwitcher的showNext()方法,就能实现自动轮播功能了。
private class ScrollHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case FLAG_START_SCROLL:
View view = getNextView();
mAdapter.bindView(view, mAdapter.getItem(currentIndex));
Log.e(TAG, "Index:" + currentIndex + " //" + mAdapter.getItem(currentIndex).toString());
showNext();
currentIndex = ++currentIndex % mAdapter.getCount();
mHandler.sendEmptyMessageDelayed(FLAG_START_SCROLL, mTimeSpan);
break;
case FLAG_STOP_SCROLL:
mHandler.removeMessages(FLAG_START_SCROLL);
break;
case FLAG_REFRESH:
currentIndex = 0;
removeAllViews();
start();
break;
}
}
}
使用起来也非常简单,类似与列表的使用,分一下几步走
1.实现IAdvertAdapter,关键实现makeView(),再这个方法中加载我们自定义的布局,或者自己创建的View,当然,这边不要去做数据绑定相关的操作。
2.实现IAdvertAdapter中的bindView,做一下数据绑定,事件绑定相关的操作
3.switchers.setAdapter(new IAdvertAdapter(context, data));
这边只是写了大体的使用步骤,具体还是看源码吧,非常简单,喜欢的加伙伴可以帮忙加一个小星星,算是一点鼓励吧_
https://github.com/yunzhouhua/AdvertSwitcher
疑问
1.如果广告很多创建了很多的View,而最终也就显示两个,是否浪费性能?有优化嘛?
开始我就有这样的疑问,以为makeView每次都会创建新的View添加带ViewSwitcher中,也准备做一下View复用,但是看了ViewSwitcher的源码后,发现多费心了,google爸爸早就帮我们想到了。ViewSwitcher中只会有两个View,makeView也只会调用两次,如果有第三次的情况,程序肯定会奔溃,不信嘛?看下下面的代码,ViewSwitcher-addView
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
if (getChildCount() >= 2) {
throw new IllegalStateException("Can't add more than 2 views to a ViewSwitcher");
}
super.addView(child, index, params);
}
ViewSwitcher内部再setFactory的时候连续调用了两次obtainView创建出了对应的View并添加到ViewSwitcher,而之后的滚动效果完全就是这两个view通过动画,控制显示/隐藏实现的。感兴趣的可以看一下源码。