首页 > 学院 > 开发设计 > 正文

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

2019-11-07 23:39:30
字体:
来源:转载
供稿:网友

思路

在 循环 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<View> mScrapViews = new LinkedList<View>(); // 最多缓存两个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 这里了。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表