一、简介
项目中曾几次用到了轮播图,即用ViewPager实现自动播放、以同一方向的动画效果循环播放的效果。曾经在网络搜集了很多资料,但是都有大大小小的问题,这里对网络的思路进行总结改进并封装到相关类中。
1、自动播放 - 相比于Thread-Handler、CountDownTimer以及TimerTask的方式,这里采用纯Handler发送消息的方式实现循环
2、循环播放 - 保证以同一方向的动画进行滑动,这里还是采用了网络思路,即设置最大值的办法。但是网络的设置最 大值时,对于低于4张的图片都会伴随着崩溃问题的产生,这里采用Double方式进行处理
该效果实现总共有三个类:LooperViewPager、LooperLayout、LooperViewPagerHandle。
LooperViewPager:主要对ViewPager以及ViewPager所使用的Adapter进行整合以及处理
LooperLayout:主要对自动播放功能、底部指示点功能进行整合与处理
LooperViewPagerHandle:主要对以上两个类所需要的方法进行
备注: 加载图片采用的是Facebook新出的Fresco框架,保证图片的加载不会内存溢出,所以需要提前引入Fresco框架的库。
二、 代码如下:
1、LooperViewPager类
public class LooperViewPager extends ViewPager { public LooperViewPager(Context context, AttributeSet attrs) { super(context, attrs); } public LooperViewPager(Context context) { super(context); } /** * 设置适配器 * * @param views */ public void setPagerAdapter(Context context, List<String> urls, DisplayImageOptions opts, OnPagerClickListener listener) { List<View> mViews = LooperViewPagerHandle.getHandledViews(context, urls, opts, listener); if (mViews != null && mViews.size() > 0) { LoopedAdapter mAdapter = new LoopedAdapter(mViews); setAdapter(mAdapter); } } /** * 适配器 * * @author Administrator * */ class LoopedAdapter extends PagerAdapter { private List<View> mViews; public LoopedAdapter(List<View> data) { mViews = data; } @Override public int getCount() { // TODO Auto-generated method stub if (mViews.size() == 1) { return 1; } return Integer.MAX_VALUE; } @Override public boolean isViewFromObject(View arg0, Object arg1) { // TODO Auto-generated method stub return arg0 == (View) arg1; } @Override public void destroyItem(ViewGroup container, int position, Object object) { // TODO Auto-generated method stub container.removeView(mViews.get(position % mViews.size())); } @Override public Object instantiateItem(ViewGroup container, int position) { // TODO Auto-generated method stub View view = mViews.get(position % mViews.size()); ViewGroup parent = (ViewGroup) view.getParent(); if (parent != null) {// 存在父类布局,表示界面已经加载过 parent.removeAllViews(); } container.addView(view); return mViews.get(position % mViews.size()); } } public interface OnPagerClickListener { /** * ViewPager页面点击事件 * * @param v * @param position * @param url */ public void onPagerClick(View v, int position, String url); } }
2、LooperLayout类
public class LooperLayout extends LinearLayout implements OnPageChangeListener { private Context mContext; private LayoutInflater mInflater; // 内容页面View private View mContentView; // 滑动页面 private LooperViewPager mLoopViewPager; // 滑动指示器 private LinearLayout mLoopIndicator; // 指示器长度 private int mIndicatorLength; // 指示器数组 private List<View> mIndicators = new ArrayList<View>(); // 默认指示器大小 private static final int DEFAULT_INDICATOR_WIDTH = 20; // 默认指示器间隔 private static final int DEFAULT_INDICATOR_MARGIN = 15; // 默认播放周期 private static final long DEFAULT_PLAY_INTERVAL = 5000; // 发送What private int MESSAGE_WHAT = 0; // 指示器默认图标 private static final int DEFAULT_INDICATOR_CHECK = R.drawable.indicator_checked; private static final int DEFAULT_INDICATOR_UNCHECK = R.drawable.indicator_unchecked; // 指示点大小 private int mIndicatorWidth = DEFAULT_INDICATOR_WIDTH; // 指示点间隔 private int mIndicatorMargin = DEFAULT_INDICATOR_MARGIN; // 默认播放时差 private long mPlayInterval = DEFAULT_PLAY_INTERVAL; // 指示器资源 private int mIndicatorChecked = DEFAULT_INDICATOR_CHECK; private int mIndicatorUnChecked = DEFAULT_INDICATOR_UNCHECK; // 当前显示的指示器 private int mCurrentIndicatorPosition = 0; // 是否开始播放 private boolean hasStartPlaying; // 计时 private Handler mTimerHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == MESSAGE_WHAT) { if (hasStartPlaying) { int index = mLoopViewPager.getCurrentItem() + 1; mLoopViewPager.setCurrentItem(index); } } } }; public LooperLayout(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public LooperLayout(Context context) { super(context); init(context); } private void init(Context context) { this.mContext = context; mInflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); onCreateView(); } private void onCreateView() { mContentView = mInflater.inflate(R.layout.view_loop_pager, null); mLoopViewPager = (LooperViewPager) mContentView .findViewById(R.id.mLooperViewPager); mLoopIndicator = (LinearLayout) mContentView .findViewById(R.id.mLooperIndicator); mLoopViewPager.setOnPageChangeListener(this); addView(mContentView, new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)); } /** * 设置适配显示 * * @param context * @param urls * @param opts * @param listener */ public void setPagerAdapter(Context context, List<String> urls, DisplayImageOptions opts, OnPagerClickListener listener) { mLoopViewPager.setPagerAdapter(context, urls, opts, listener); // 生成指示器 if (!hasDoubled(urls)) { mIndicatorLength = urls.size(); } else { // 执行double处理后,指示器数减半 mIndicatorLength = urls.size() / 2; } initIndicators(); // 默认指示器显示 mLoopViewPager.setCurrentItem(0); setCurrentIndicator(0); } /** * 根据数据长度,生成指示器 */ private void initIndicators() { for (int i = 0; i < mIndicatorLength; i++) { View v = getIndicator(); mLoopIndicator.addView(v); mIndicators.add(v); } } /** * 生成一个指示器 */ private View getIndicator() { View v = new View(mContext); LayoutParams params = new LayoutParams(mIndicatorWidth, mIndicatorWidth); params.leftMargin = mIndicatorMargin; v.setLayoutParams(params); v.setBackgroundResource(mIndicatorUnChecked); return v; } /** * 设置当前指示器显示 * * @param index */ private void setCurrentIndicator(int index) { clearIndicators(); View v = mIndicators.get(index); v.setBackgroundResource(mIndicatorChecked); mCurrentIndicatorPosition = index; } /** * 重置所有指示器显示 */ private void clearIndicators() { for (View v : mIndicators) { v.setBackgroundResource(mIndicatorUnChecked); } } /** * 重置指示器样式 */ private void resetIndicators() { for (View v : mIndicators) { LayoutParams params = (LayoutParams) v.getLayoutParams(); params.width = mIndicatorWidth; params.height = mIndicatorWidth; params.leftMargin = mIndicatorMargin; v.setLayoutParams(params); } } /** * 重置指示器资源 */ private void resetIndicatorsBg() { int len = mIndicators.size(); for (int i = 0; i < len; i++) { if (i == mCurrentIndicatorPosition) { mIndicators.get(i).setBackgroundResource(mIndicatorChecked); } else { mIndicators.get(i).setBackgroundResource(mIndicatorUnChecked); } } } /** * 设置指示器位置 * * @param mode */ public void setIndicatorGravity(Activity activity, int mode) { DisplayMetrics outMetrics = new DisplayMetrics(); activity.getWindowManager().getDefaultDisplay().getMetrics(outMetrics); float density = outMetrics.density; switch (mode) { case 0:// 右下角 RelativeLayout.LayoutParams params1 = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT); params1.alignWithParent = true; params1.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); params1.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); params1.bottomMargin = (int) (10 * density); params1.rightMargin = (int) (20 * density); mLoopIndicator.setLayoutParams(params1); break; case 1:// 底部居中 RelativeLayout.LayoutParams params2 = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT); params2.alignWithParent = true; params2.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); params2.addRule(RelativeLayout.CENTER_HORIZONTAL); params2.bottomMargin = (int) (10 * density); mLoopIndicator.setLayoutParams(params2); break; } } /** * 判断是否执行过Double处理 * * @param data * @return */ private boolean hasDoubled(List<String> data) { // 只有2或3条数据,即double后4或6条数据,才会有double的可能 if (data != null && (data.size() == 4 || data.size() == 6)) { int len = data.size() / 2; for (int i = 0; i < len; i++) { // 前半数据与后半数据完全相同,则为double处理过 if (!data.get(i).equals(data.get(i + len))) { return false; } } return true; } return false; } @Override public void onPageScrollStateChanged(int arg0) { // TODO Auto-generated method stub } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { // TODO Auto-generated method stub } @Override public void onPageSelected(int arg0) { // TODO Auto-generated method stub int currentIndex = arg0 % mIndicatorLength; setCurrentIndicator(currentIndex); // 定时跳转 if (mIndicatorLength > 1) { mTimerHandler.removeMessages(MESSAGE_WHAT); mTimerHandler.sendEmptyMessageDelayed(MESSAGE_WHAT, mPlayInterval); } } // /////////PUBLIC METHOD /** * 设置播放间隔 * * @param time */ public void setAutoPlayInterval(long time) { if (time > 0) { this.mPlayInterval = time; } } /** * 设置指示器样式 * * @param size * @param margin */ public void setIndicatorStyle(int size, int margin) { if (size > 0) { this.mIndicatorWidth = size; } if (margin > 0) { this.mIndicatorMargin = margin; } resetIndicators(); } /** * 设置指示器资源 * * @param check * @param uncheck */ public void setIndicatorResource(int check, int uncheck) { this.mIndicatorChecked = check; this.mIndicatorUnChecked = uncheck; resetIndicatorsBg(); } /** * 开始播放 */ public void startPlay() { mTimerHandler.sendEmptyMessageDelayed(MESSAGE_WHAT, mPlayInterval); hasStartPlaying = true; } /** * 停止播放 */ public void stopPlay() { mTimerHandler.removeMessages(MESSAGE_WHAT); hasStartPlaying = false; } }
3、LooperViewPagerHandle类
public class LooperViewPagerHandle { /** * 根据URL生产对应的View * * @param url * @param listener * @return */ private static ImageView getView(Context context, final String url, final int position, DisplayImageOptions opts, final OnPagerClickListener listener) { if (TextUtils.isEmpty(url.trim())) { return null; } final FrescoImageView view = new FrescoImageView(context); LayoutParams params = new LayoutParams(); params.width = LayoutParams.MATCH_PARENT; params.height = LayoutParams.MATCH_PARENT; view.setLayoutParams(params); view.setAdjustViewBounds(true); FrescoDisplayUtils.setFrescoParam(view, R.drawable.money_default_img, com.facebook.drawee.drawable.ScalingUtils.ScaleType.FIT_XY); FrescoDisplayUtils.displayImage(url, view); if (listener != null) { view.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub listener.onPagerClick(view, position, url); } }); } return view; } /** * 将URL数组转化为View适配数组 * * @param urls * @param listener * @return */ private static List<View> getViews(Context context, List<String> urls, DisplayImageOptions opts, OnPagerClickListener listener) { if (urls == null) { return null; } boolean hasDoubled = hasDoubled(urls); int len = urls.size(); if (hasDoubled) { len = len / 2; } List<View> mViews = new ArrayList<View>(); for (String url : urls) { ImageView view = getView(context, url, mViews.size() % len, opts, listener); if (view != null) { mViews.add(view); } } return mViews; } /** * 针对String数组特殊情况进行Double处理 * * @param views * @return */ private static List<String> handleUrl(List<String> urls) { if (urls == null || urls.size() == 0 || urls.size() == 1 || urls.size() > 3) { return urls; } List<String> mTempUrls = urls; urls.addAll(mTempUrls); return urls; } /** * <外部调用>获取处理后的View数组 * * @param context * @param urls * @param listener * @return */ public static List<View> getHandledViews(Context context, List<String> urls, DisplayImageOptions opts, OnPagerClickListener listener) { List<String> mHandledUrls = handleUrl(urls); return getViews(context, mHandledUrls, opts, listener); } /** * 判断是否执行过Double处理 * * @param data * @return */ private static boolean hasDoubled(List<String> data) { // 只有2或3条数据,即double后4或6条数据,才会有double的可能 if (data != null && (data.size() == 4 || data.size() == 6)) { int len = data.size() / 2; for (int i = 0; i < len; i++) { // 前半数据与后半数据完全相同,则为double处理过 if (!data.get(i).equals(data.get(i + len))) { return false; } } return true; } return false; } }
三、调用方式(setPagerAdaper方法中的Options参数为预留参数,直接传null即可,或者删掉)
1、添加数据显示
mLooperLayout.setPagerAdapter(MainActivity.this, data, null, listener).
参数1:context 参数2:List<String> 要显示的图片url 参数3:null即可 参数4:每张图片的点击监听
2、开始播放
mLooperLayout.startPlay().
3、停止播放
mLooperLayout.stopPlay().
4、设置指示点的类型
mLooperLayout.setIndicatorGravity(context, 0).
mode为0,则指示点显示在右下角;mode为1,则指示点显示在底部中央
5、设置播放间隔
mLooperLayout.setAutoPlayInterval(5000).
本demo仅在于网上思路修改完成,不喜勿喷。如果发现问题,欢迎回复!