实现这个功能的目前已经有很多种方式了,譬如说继承ViewGroup仿ViewPager通过adapter循环add、dstory指定的item,使用fragment添加移除view的方式,设置viewpager的索引条目达到上限==。以上几种方式属于Banner的常用选择,其中动效做的非常出色的如代码家的ImageSlider,曾经在项目中也引用过,不过在使用的过程中出现了内存开销过大的问题,1张图片也可以左右轮回切换的问题,不得不说是个硬伤。后来遇到了一种新的方式,于是分享出来给大家。
Banner和viewpgaer的最大区别是Banner可以在itemPosition=0和itemPosition=size-1之间来回无缝切换,因此我们理想状态是作如下处理的:当滑动到itemPosition=0的时候,左侧有对应itemPosition=0到itemPosition=size-1的view;当滑动到itemPosition=size-1的时候,右侧有对应itemPosition=0到itemPosition=size-1的view。这是我们一般直接的做法:设置viewpager的索引条目达到上限。然而开销的问题让我们会想到复用,结合listview的adapter的viewholder形式,可能会采取第i屏的第itemPosition=position的view复用第1屏的itemPosition=position的view。这样的做法看似不错,但是对于拥有着程序员思维的我们觉得这仅仅是一个逻辑不够严密,过程不够完美,存在潜藏的隐患的idea。于是两条道路,优化or另辟蹊径。
其实再做上述过程中的考虑时候,我们想到了复用,想到了设置size=Integer.MAX_VALUE实现itemPosition=0和itemPosition=size-1之间的切换,但是仔细考虑一下,我们也仅仅需要实现itemPosition=0和itemPosition=size-1之间的切换,就可以交给viewpager本身的机制来完成剩下页卡之间的轮播。假设一共需要展示4个view,于是画图如下:
... item=2 -> item=3 -> item=0 -> item=1 -> item=2 -> item=3 -> item=0 -> item=1 ...
也就是说我们发现可以考虑当itemPosition=3的时候下一屏itemPosition=0的view就是当前屏被滑动过去itemPosition=0的View,用来代替无限size=Integer.MAX_VALUE的实现方式,然而这样难点又回到了如和实现两者之间的无动画切换。于是我们想到实现size=Integer.MAX_VALUE的本质其实就是对viewpager不停的addView和destoryView,当滑动到itemPosition=0的时候在左侧添加一个itemPosition=3的view,当滑动到itemPosition=3的时候在右侧添加一个itemPosition=0的view,但是后续的itemPosition=1和itemPosition=2的view就可以不用我们手动add进去,因为viewpager的本质会自动默认itemPosition=3左侧、itemPosition=0右侧存在itemPosition=1、2的view,因此上述图我们可以简化成:
|<—————————————|
itemPosition 3 -> 0 -> 1 -> 2 -> 3 -> 0
|—————————————>|
于是我们把addView简化成了addView(0,item3View)和addView(5,item0View),而当加载到这两个view的时候我们认为它加载的其实就是itemPosition=3和itemPosition=0的view。认为这个词语在代码中的体现就是set,因此当我们在首尾各添加了一个view之后可以写出如下代码:
if(getCurrentPosition() == 0){
setCurrentPosition(4);
}
if(getCurrentPosition() == 5){
setCurrentPosition(1);
}
注:getCurrentPosition() == 0的时候其实相当于把item4的布局加到了item0的前面,此时数据就是item4的数据,所以把页面设置到item4;getCurrentPosition() == 5的时候其实相当于把item0的布局加到了item4的后面,此时数据就是item0的数据,所以把页面设置到item0
完整代码:
package com.systoon.business.home.holder;
import android.app.Activity;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.systoon.business.home.bean.HomeActivityBean;
import com.systoon.business.home.utils.CommonUtils;
import com.systoon.business.home.view.HomeNewsListActivity;
import com.systoon.toon.R;
import com.systoon.toon.common.utils.DateUtils;
import com.systoon.toon.common.utils.ScreenUtil;
import java.util.ArrayList;
import java.util.List;
public class QlLHomeNewsViewHolder extends BaseMainAppViewHolder {
private final Activity mActivity;
private ViewPager mViewpager;
private LinearLayout mIndicateRoot;
private LinearLayout mRootEmpty;
private LinearLayout contentRoot;
private TextView mTitle;
private TextView mTime;
private int currentItem;
private TextView mMore;
private List<HomeActivityBean> datas;
private ArrayList<View> viewList;
private int count;
private long delayTime = 3000;
private ViewPager.OnPageChangeListener mOnPageChangeListener;
private WeakHandler handler = new WeakHandler();
public QlLHomeNewsViewHolder(View itemView, ViewGroup parent, List<HomeActivityBean> mList,
Activity activity) {
super(itemView);
mViewpager = (ViewPager) itemView.findViewById(R.id.news_banner);
mIndicateRoot = (LinearLayout) itemView.findViewById(R.id.dot_indicate_root);
contentRoot = (LinearLayout) itemView.findViewById(R.id.content_root);
mTitle = (TextView) itemView.findViewById(R.id.news_title);
mTime = (TextView) itemView.findViewById(R.id.time);
mMore = (TextView) itemView.findViewById(R.id.btn_more);
mActivity = activity;
datas = mList;
}
@Override
public void bindHolder(HomeActivityBean bean, int position) {
super.bindHolder(bean, position);
viewList = new ArrayList<>();
final List<HomeActivityBean.ExtrasData> list = bean.getDataList();
if (list != null) {
count = list.size();
mIndicateRoot.removeAllViews();
View dot;
if (count > 1) {// 多于一个轮播
for (int i = 0; i < count + 2; i++) {
viewList.add(LayoutInflater.from(itemView.getContext()).inflate(R.layout
.ql_home_show_type_banner_item_img,
null, false));
if (i < count) {
dot = new View(itemView.getContext());
mIndicateRoot.addView(dot, ScreenUtil.dp2px(6), ScreenUtil.dp2px(6));
setMargins(dot, ScreenUtil.dp2px(3), 0, ScreenUtil.dp2px(3), 0);
}
}
} else if (count == 1) {
viewList.add(LayoutInflater.from(itemView.getContext()).inflate(R.layout
.ql_home_show_type_banner_item_img,
null, false));
} else {// TODO 空界面处理
}
/**
* banner
*/
if (count > 0)
mViewpager.setAdapter(new PagerAdapter() {
@Override
public int getCount() {
return count > 1 ? count + 2 : count;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(final ViewGroup container, int position)
{//必须实现,实例化
View view = viewList.get(position);
final ImageView img = (ImageView) view.findViewById(R.id.img_content);
final HomeActivityBean.ExtrasData items;
if (count > 1) {
if (position == 0) {
items = list.get(count - 1);
} else if (position == count + 1) {
items = list.get(0);
} else {
items = list.get(position - 1);
}
} else {
items = list.get(position);
}
showImgRoundCrop("", img, img.getContext());
img.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (TextUtils.isEmpty(items.getDescription())) {
CommonUtils.goActivityH5(mActivity, items.getActivityId());
} else {
CommonUtils.toNewsDetail(mActivity, items.getNewsId());
}
// 跳转
}
});
container.addView(view);
return view;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object)
{//必须实现,销毁
container.removeView(viewList.get(position));
}
});
mViewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int
positionOffsetPixels) {
if (mOnPageChangeListener != null) {
mOnPageChangeListener.onPageScrolled(toRealPosition(position, count),
positionOffset, positionOffsetPixels);
}
}
@Override
public void onPageSelected(int position) {
currentItem = position;
if (mOnPageChangeListener != null) {
mOnPageChangeListener.onPageSelected(toRealPosition(position, count));
}
// mPageIndex.setText(new StringBuilder().append(toRealPosition(position,
// count) + 1).append("/").append(count).toString());
if (mIndicateRoot.getChildCount() > 1) {// 多余一个
HomeActivityBean.ExtrasData items = list.get(toRealPosition(position,
count));
for (int i = 0; i < mIndicateRoot.getChildCount(); i++) {
if (i == toRealPosition(position, count)) {
mIndicateRoot.getChildAt(i).setBackgroundResource(R.drawable
.icon_interact_news_banner_white_dot);
mTitle.setText(TextUtils.isEmpty(items.getTitle()) ? "" : items
.getTitle());
mTime.setText(TextUtils.isEmpty(items.getUpdateTime()) ? "" :
DateUtils.convertToTime(Long.parseLong(items
.getUpdateTime())
, "yyyy年MM月dd日"));
} else {
mIndicateRoot.getChildAt(i).setBackgroundResource(R.drawable
.icon_interact_news_banner_gray_dot);
}
}
}
}
@Override
public void onPageScrollStateChanged(int state) {
if (mOnPageChangeListener != null) {
mOnPageChangeListener.onPageScrollStateChanged(state);
}
switch (state) {
case 0://No operation
if (currentItem == 0) {
// viewPager.setCurrentItem(count+1, false);
mViewpager.setCurrentItem(count, false);
} else if (currentItem == count + 1) {
// viewPager.setCurrentItem(0, false);
mViewpager.setCurrentItem(1, false);
}
break;
case 1://start Sliding
if (currentItem == count + 1) {
// viewPager.setCurrentItem(0, false);
mViewpager.setCurrentItem(1, false);
} else if (currentItem == 0) {
// viewPager.setCurrentItem(count+1, false);
mViewpager.setCurrentItem(count, false);
}
break;
case 2://end Sliding
break;
}
}
});
/* if (datas.size() - 1 == position) {
mLine.setVisibility(View.GONE);
} else {
mLine.setVisibility(View.VISIBLE);
}
*/
mViewpager.setCurrentItem(1);
//TODO 设置偏移 mViewpager.setOffscreenPageLimit(count + 2);
mMore.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
HomeNewsListActivity.launcher(mMore.getContext());
}
});
if (count > 1)
startAutoPlay();
}
}
public void setOnPageChangeListener(ViewPager.OnPageChangeListener onPageChangeListener) {
mOnPageChangeListener = onPageChangeListener;
}
private int toRealPosition(int position, int count) {
int realPosition = (position - 1) % count;
if (realPosition < 0)
realPosition += count;
return realPosition;
}
public void startAutoPlay() {
handler.removeCallbacks(task);
handler.postDelayed(task, delayTime);
}
private final Runnable task = new Runnable() {
@Override
public void run() {
if (count > 1) {
currentItem = currentItem % (count + 1) + 1;
if (currentItem == 1) {
mViewpager.setCurrentItem(currentItem, false);
handler.postDelayed(task, delayTime);
} else {
mViewpager.setCurrentItem(currentItem);
handler.postDelayed(task, delayTime);
}
}
}
};
public void stopAutoPlay() {
handler.removeCallbacks(task);
}
}