android 广告重叠控件,打造一个简单实用的安卓广告栏控件

思路

在循环 ViewPager 的两种实现方法这篇文章中介绍了广告栏的两种实现思路,但是直接用到项目中还是会有不少问题。

方法1:将 count 设为无限大,制造一种假的循环

这种方法在实际的项目中容易导致anr,在调用setCurrentItem或者在数据集发生改变时调用notifyDataSetChanged时有发生。

方法2:在 ViewPager 的首尾添加一个重复的 View

这种做法的问题是每循环一次会额外的多调用一次setCurrentItem,性能不佳,尤其是用户快速滚动时表现不够流畅。

能否将两种方法结合起来呢,比如我将count设为200个,每次滑动到最后一页或者第一页的时候再执行setCurrentItem(middleItem)。当然,我还需要对滑出去的View做好回收,这点仿照ListView去做即可。

说干就干。

实现我们的PagerAdapter

看码说话

public abstract class CyclePagerAdapter extends PagerAdapter {

private final int MAX_PAGES = 200;

// 对View做缓存,防止每次都去inflate

protected LinkedList mScrapViews = new LinkedList();

// 最多缓存两个View

protected int mMaxScrapViewSize = 2;

@Override

public Object instantiateItem(ViewGroup container, int position) {

View scrap = retrieveFromScrap();

View view = getView(position, scrap, container);

container.addView(view);

return view;

}

private View retrieveFromScrap() {

if (mScrapViews.size() > 0) {

return mScrapViews.removeLast();

}

return null;

}

@Override

public void destroyItem(ViewGroup container, int position, Object object) {

View view = (View) object;

container.removeView(view);

if (mScrapViews.size() < mMaxScrapViewSize) {

mScrapViews.add(view);

}

}

// 返回 getRealCount 的整数倍,该数最大值为 MAX_PAGES,这里将MAX_PAGES设为200。

@Override

public int getCount() {

if (getRealCount() < 2) {

return getRealCount();

}

return getRealCount() * (MAX_PAGES / getRealCount());

}

public View getView(int position, View convertView, ViewGroup container) {

int realPosition = getRealPosition(position);

return getViewAtRealPosition(realPosition, convertView, container);

}

@Override

public final boolean isViewFromObject(View view, Object object) {

return view == object;

}

@Override

public int getItemPosition(Object object) {

return POSITION_NONE;

}

public int getRealPosition(int position) {

if (getRealCount() == 0) {

return 0;

}

return position % getRealCount();

}

public abstract int getRealCount();

public abstract View getViewAtRealPosition(int position, View convertView, ViewGroup container);

}

在CyclePagerAdapter中,getCount返回值最大为200,并且该数是getRealCount的整数倍。这里我们还添加了一个回收机制,防止多次创建View导致性能损耗。

使用时只需要继承CyclePagerAdapter即可。

public class SimpleBannerAdapter extends CyclePagerAdapter {

private static final int[] drawableIds = new int[]{R.drawable.desert, R.drawable.koala,

R.drawable.jellyfish, R.drawable.hydrangeas};

private Context mContext;

public SimpleBannerAdapter(Context context) {

this.mContext = context;

}

@Override

public int getRealCount() {

return drawableIds.length;

}

@Override

public View getViewAtRealPosition(final int position, View convertView, ViewGroup container) {

if (convertView == null) {

convertView = LayoutInflater.from(mContext).inflate(R.layout.banner_item, container, false);

}

ImageView imageView = (ImageView) convertView.findViewById(R.id.imageView);

imageView.setImageResource(drawableIds[position]);

return convertView;

}

}

监听ViewPager滚动

public class CycleViewPager extends ViewPager {

private CyclePagerAdapter mCyclePagerAdapter;

@Override

public void setAdapter(PagerAdapter adapter) {

super.setAdapter(adapter);

if (adapter instanceof CyclePagerAdapter) {

mCyclePagerAdapter = (CyclePagerAdapter) adapter;

addOnPageChangeListener(mOnPageChangeListener);

setMiddleItemInner(false, true);

}

}

private OnPageChangeListener mOnPageChangeListener = new ViewPager.SimpleOnPageChangeListener() {

@Override

public void onPageScrolled(int position, float offset, int offsetPixels) {

if (offset != 0) {

return;

}

if (mCyclePagerAdapter == null || mCyclePagerAdapter.getRealCount() <= 1) {

return;

}

// 第一页

if (position == 0) {

setMiddleItemInner(false, false);

//最后一页

} else if (position == mCyclePagerAdapter.getCount() - 1) {

setMiddleItemInner(false, false);

}

}

@Override

public void onPageSelected(int position) {

}

};

// 设置到中间的item。当ViewPager滚动到第一页或者最后一页的时候调用。

public void setMiddleItem() {

setMiddleItemInner(true, true);

}

private void setMiddleItemInner(boolean setToFirstItem, boolean immediately) {

if (mCyclePagerAdapter != null && mCyclePagerAdapter.getRealCount() > 1) {

int currentItem = setToFirstItem ? 0 : getCurrentItem();

final int middleItem = mCyclePagerAdapter.getCount() / 2 + mCyclePagerAdapter.getRealPosition(currentItem);

if (immediately) {

setCurrentItem(middleItem, false);

} else {

post(new Runnable() {

@Override

public void run() {

setCurrentItem(middleItem, false);

}

});

}

}

}

}

至此,我们已经实现了一个可以循环滚动的ViewPager了,当然,自动滚动以及ViewPager指示器我们都还没有实现,如果想了解这部分可以参考我的github。我已经将这个项目上传到 https://github.com/leandom/CycleViewPager 这里了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值