平时开发软件的时候,经常会碰到轮播图这样的需求,下面介绍一种轮播图的实现方式:
我把轮播图写成了一个自定义控件,需要集成Android Support Library v4包和谷歌推荐的一个图片加载库Glide:
import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.LayerDrawable; import android.os.Handler; import android.os.Message; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.Interpolator; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.Scroller; import com.bumptech.glide.Glide; public class BannerLayout extends RelativeLayout { private ViewPager pager; // 指示器容器 private LinearLayout indicatorContainer; // 未选中图片样式 private Drawable unSelectedDrawable; // 当前选中图片样式 private Drawable selectedDrawable; private int WHAT_AUTO_PLAY = 1000; // 是否自动轮播 private boolean isAutoPlay = true; private int itemCount; private int selectedIndicatorColor = 0xffff0000; private int unSelectedIndicatorColor = 0x88888888; private Shape indicatorShape = Shape.oval; private int selectedIndicatorHeight = 6; private int selectedIndicatorWidth = 6; private int unSelectedIndicatorHeight = 6; private int unSelectedIndicatorWidth = 6; private Position indicatorPosition = Position.centerBottom; // 轮播的间隔时间 private int autoPlayDuration = 4000; private int scrollDuration = 900; private int indicatorSpace = 3; private int indicatorMargin = 10; private int defaultImage; // 指示器的样式 private enum Shape { rect, oval } // 指示器的位置 private enum Position { centerBottom, rightBottom, leftBottom, centerTop, rightTop, leftTop } private OnBannerItemClickListener onBannerItemClickListener; private Handler handler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { if (msg.what == WHAT_AUTO_PLAY) { if (pager != null) { pager.setCurrentItem(pager.getCurrentItem() + 1, true); handler.sendEmptyMessageDelayed(WHAT_AUTO_PLAY, autoPlayDuration); } } return false; } }); public BannerLayout(Context context) { super(context); init(null, 0); } public BannerLayout(Context context, AttributeSet attrs) { super(context, attrs); init(attrs, 0); } public BannerLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(attrs, defStyleAttr); } private void init(AttributeSet attrs, int defStyle) { TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.BannerLayoutStyle, defStyle, 0); selectedIndicatorColor = array.getColor( R.styleable.BannerLayoutStyle_selectedIndicatorColor, selectedIndicatorColor); unSelectedIndicatorColor = array.getColor( R.styleable.BannerLayoutStyle_unSelectedIndicatorColor, unSelectedIndicatorColor); int shape = array.getInt(R.styleable.BannerLayoutStyle_indicatorShape, Shape.oval.ordinal()); for (Shape shape1 : Shape.values()) { if (shape1.ordinal() == shape) { indicatorShape = shape1; break; } } selectedIndicatorHeight = (int) array.getDimension( R.styleable.BannerLayoutStyle_selectedIndicatorHeight, selectedIndicatorHeight); selectedIndicatorWidth = (int) array.getDimension( R.styleable.BannerLayoutStyle_selectedIndicatorWidth, selectedIndicatorWidth); unSelectedIndicatorHeight = (int) array.getDimension( R.styleable.BannerLayoutStyle_unSelectedIndicatorHeight, unSelectedIndicatorHeight); unSelectedIndicatorWidth = (int) array.getDimension( R.styleable.BannerLayoutStyle_unSelectedIndicatorWidth, unSelectedIndicatorWidth); int position = array.getInt( R.styleable.BannerLayoutStyle_indicatorPosition, Position.centerBottom.ordinal()); for (Position position1 : Position.values()) { if (position == position1.ordinal()) { indicatorPosition = position1; } } indicatorSpace = (int) array.getDimension( R.styleable.BannerLayoutStyle_indicatorSpace, indicatorSpace); indicatorMargin = (int) array.getDimension( R.styleable.BannerLayoutStyle_indicatorMargin, indicatorMargin); autoPlayDuration = array.getInt( R.styleable.BannerLayoutStyle_autoPlayDuration, autoPlayDuration); scrollDuration = array.getInt( R.styleable.BannerLayoutStyle_scrollDuration, scrollDuration); isAutoPlay = array.getBoolean(R.styleable.BannerLayoutStyle_isAutoPlay, isAutoPlay); defaultImage = array.getResourceId( R.styleable.BannerLayoutStyle_defaultImage, defaultImage); array.recycle(); // 绘制未选中状态图形 LayerDrawable unSelectedLayerDrawable; LayerDrawable selectedLayerDrawable; GradientDrawable unSelectedGradientDrawable; unSelectedGradientDrawable = new GradientDrawable(); // 绘制选中状态图形 GradientDrawable selectedGradientDrawable; selectedGradientDrawable = new GradientDrawable(); switch (indicatorShape) { case rect: unSelectedGradientDrawable.setShape(GradientDrawable.RECTANGLE); selectedGradientDrawable.setShape(GradientDrawable.RECTANGLE); break; case oval: unSelectedGradientDrawable.setShape(GradientDrawable.OVAL); selectedGradientDrawable.setShape(GradientDrawable.OVAL); break; } unSelectedGradientDrawable.setColor(unSelectedIndicatorColor); unSelectedGradientDrawable.setSize(unSelectedIndicatorWidth, unSelectedIndicatorHeight); unSelectedLayerDrawable = new LayerDrawable( new Drawable[] { unSelectedGradientDrawable }); unSelectedDrawable = unSelectedLayerDrawable; selectedGradientDrawable.setColor(selectedIndicatorColor); selectedGradientDrawable.setSize(selectedIndicatorWidth, selectedIndicatorHeight); selectedLayerDrawable = new LayerDrawable( new Drawable[] { selectedGradientDrawable }); selectedDrawable = selectedLayerDrawable; } // 添加本地图片路径 public void setViewRes(List<Integer> viewRes) { List<View> views = new ArrayList<View>(); itemCount = viewRes.size(); // 主要是解决当item为小于3个的时候滑动有问题,这里将其拼凑成3个以上 if (itemCount < 1) {// 当item个数0 throw new IllegalStateException("item count not equal zero"); } else if (itemCount < 2) {// 当item个数为1 views.add(getImageView(viewRes.get(0), 0)); views.add(getImageView(viewRes.get(0), 0)); views.add(getImageView(viewRes.get(0), 0)); } else if (itemCount < 3) {// 当item个数为2 views.add(getImageView(viewRes.get(0), 0)); views.add(getImageView(viewRes.get(1), 1)); views.add(getImageView(viewRes.get(0), 0)); views.add(getImageView(viewRes.get(1), 1)); } else { for (int i = 0; i < viewRes.size(); i++) { views.add(getImageView(viewRes.get(i), i)); } } setViews(views); } private ImageView getImageView(Integer res, final int position) { ImageView imageView = new ImageView(getContext()); imageView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (onBannerItemClickListener != null) { onBannerItemClickListener.onItemClick(position); } } }); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); Glide.with(getContext()).load(res).centerCrop().into(imageView); return imageView; } // 添加网络图片路径 public void setViewUrls(List<String> urls) { List<View> views = new ArrayList<View>(); itemCount = urls.size(); // 主要是解决当item为小于3个的时候滑动有问题,这里将其拼凑成3个以上 if (itemCount < 1) {// 当item个数0 throw new IllegalStateException("item count not equal zero"); } else if (itemCount < 2) { // 当item个数为1 views.add(getImageView(urls.get(0), 0)); views.add(getImageView(urls.get(0), 0)); views.add(getImageView(urls.get(0), 0)); } else if (itemCount < 3) {// 当item个数为2 views.add(getImageView(urls.get(0), 0)); views.add(getImageView(urls.get(1), 1)); views.add(getImageView(urls.get(0), 0)); views.add(getImageView(urls.get(1), 1)); } else { for (int i = 0; i < urls.size(); i++) { views.add(getImageView(urls.get(i), i)); } } setViews(views); } private ImageView getImageView(String url, final int position) { ImageView imageView = new ImageView(getContext()); imageView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (onBannerItemClickListener != null) { onBannerItemClickListener.onItemClick(position); } } }); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); if (defaultImage != 0) { Glide.with(getContext()).load(url).placeholder(defaultImage) .centerCrop().into(imageView); } else { Glide.with(getContext()).load(url).centerCrop().into(imageView); } return imageView; } // 添加任意View视图 private void setViews(final List<View> views) { // 初始化pager pager = new ViewPager(getContext()); // 添加viewpager到SliderLayout addView(pager); setSliderTransformDuration(scrollDuration); // 初始化indicatorContainer indicatorContainer = new LinearLayout(getContext()); indicatorContainer.setGravity(Gravity.CENTER_VERTICAL); RelativeLayout.LayoutParams params = new LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); switch (indicatorPosition) { case centerBottom: params.addRule(RelativeLayout.CENTER_HORIZONTAL); params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); break; case centerTop: params.addRule(RelativeLayout.CENTER_HORIZONTAL); params.addRule(RelativeLayout.ALIGN_PARENT_TOP); break; case leftBottom: params.addRule(RelativeLayout.ALIGN_PARENT_LEFT); params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); break; case leftTop: params.addRule(RelativeLayout.ALIGN_PARENT_LEFT); params.addRule(RelativeLayout.ALIGN_PARENT_TOP); break; case rightBottom: params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); break; case rightTop: params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); params.addRule(RelativeLayout.ALIGN_PARENT_TOP); break; } // 设置margin params.setMargins(indicatorMargin, indicatorMargin, indicatorMargin, indicatorMargin); // 添加指示器容器布局到SliderLayout addView(indicatorContainer, params); // 初始化指示器,并添加到指示器容器布局 for (int i = 0; i < itemCount; i++) { ImageView indicator = new ImageView(getContext()); indicator.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); indicator.setPadding(indicatorSpace, indicatorSpace, indicatorSpace, indicatorSpace); indicator.setImageDrawable(unSelectedDrawable); indicatorContainer.addView(indicator); } LoopPagerAdapter pagerAdapter = new LoopPagerAdapter(views); pager.setAdapter(pagerAdapter); // 设置当前item到Integer.MAX_VALUE中间的一个值,看起来像无论是往前滑还是往后滑都是ok的 // 如果不设置,用户往左边滑动的时候已经划不动了 int targetItemPosition = Integer.MAX_VALUE / 2 - Integer.MAX_VALUE / 2 % itemCount; pager.setCurrentItem(targetItemPosition); switchIndicator(targetItemPosition % itemCount); pager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { @Override public void onPageSelected(int position) { switchIndicator(position % itemCount); } }); startAutoPlay(); } public void setSliderTransformDuration(int duration) { try { Field mScroller = ViewPager.class.getDeclaredField("mScroller"); mScroller.setAccessible(true); FixedSpeedScroller scroller = new FixedSpeedScroller( pager.getContext(), null, duration); mScroller.set(pager, scroller); } catch (Exception e) { e.printStackTrace(); } } /** * 开始自动轮播 */ public void startAutoPlay() { stopAutoPlay(); // 避免重复消息 if (isAutoPlay) { handler.sendEmptyMessageDelayed(WHAT_AUTO_PLAY, autoPlayDuration); } } @Override protected void onWindowVisibilityChanged(int visibility) { super.onWindowVisibilityChanged(visibility); if (visibility == VISIBLE) { startAutoPlay(); } else { stopAutoPlay(); } } /** * 停止自动轮播 */ public void stopAutoPlay() { if (isAutoPlay) { handler.removeMessages(WHAT_AUTO_PLAY); } } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: stopAutoPlay(); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: startAutoPlay(); break; } return super.dispatchTouchEvent(ev); } /** * 切换指示器状态 * * @param currentPosition * 当前位置 */ private void switchIndicator(int currentPosition) { for (int i = 0; i < indicatorContainer.getChildCount(); i++) { ((ImageView) indicatorContainer.getChildAt(i)) .setImageDrawable(i == currentPosition ? selectedDrawable : unSelectedDrawable); } } public void setOnBannerItemClickListener( OnBannerItemClickListener onBannerItemClickListener) { this.onBannerItemClickListener = onBannerItemClickListener; } public interface OnBannerItemClickListener { void onItemClick(int position); } public class LoopPagerAdapter extends PagerAdapter { private List<View> views; public LoopPagerAdapter(List<View> views) { this.views = views; } @Override public int getCount() { // Integer.MAX_VALUE = 2147483647 return Integer.MAX_VALUE; } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } @Override public Object instantiateItem(ViewGroup container, int position) { if (views.size() > 0) { // position % view.size()是指虚拟的position会在[0,view.size())之间循环 View view = views.get(position % views.size()); if (container.equals(view.getParent())) { container.removeView(view); } container.addView(view); return view; } return null; } @Override public void destroyItem(ViewGroup container, int position, Object object) { } } public class FixedSpeedScroller extends Scroller { private int mDuration = 1000; public FixedSpeedScroller(Context context) { super(context); } public FixedSpeedScroller(Context context, Interpolator interpolator) { super(context, interpolator); } public FixedSpeedScroller(Context context, Interpolator interpolator, int duration) { this(context, interpolator); mDuration = duration; } @Override public void startScroll(int startX, int startY, int dx, int dy, int duration) { // Ignore received duration, use fixed one instead super.startScroll(startX, startY, dx, dy, mDuration); } @Override public void startScroll(int startX, int startY, int dx, int dy) { // Ignore received duration, use fixed one instead super.startScroll(startX, startY, dx, dy, mDuration); } } }
自定义控件的属性定义:
<resources> <declare-styleable name="BannerLayoutStyle"> <attr name="selectedIndicatorColor" format="color|reference" /> <attr name="unSelectedIndicatorColor" format="color|reference" /> <attr name="indicatorShape" format="enum"> <enum name="rect" value="0" /> <enum name="oval" value="1" /> </attr> <attr name="selectedIndicatorHeight" format="dimension|reference" /> <attr name="selectedIndicatorWidth" format="dimension|reference" /> <attr name="unSelectedIndicatorHeight" format="dimension|reference" /> <attr name="unSelectedIndicatorWidth" format="dimension|reference" /> <attr name="indicatorPosition" format="enum"> <enum name="centerBottom" value="0" /> <enum name="rightBottom" value="1" /> <enum name="leftBottom" value="2" /> <enum name="centerTop" value="3" /> <enum name="rightTop" value="4" /> <enum name="leftTop" value="5" /> </attr> <attr name="indicatorSpace" format="dimension|reference" /> <attr name="indicatorMargin" format="dimension|reference" /> <attr name="autoPlayDuration" format="integer|reference" /> <attr name="scrollDuration" format="integer|reference" /> <attr name="isAutoPlay" format="boolean"/> <attr name="defaultImage" format="integer|reference"/> </declare-styleable> </resources>
轮播图控件的使用:
BannerLayout bannerLayout = (BannerLayout) findViewById(R.id.banner); BannerLayout bannerLayout2 = (BannerLayout) findViewById(R.id.banner2); // 加载网络图片 final List<String> urls = new ArrayList<String>(); urls.add("http://img4.duitang.com/uploads/item/201408/30/20140830185433_FnJLA.jpeg"); urls.add("http://img0.imgtn.bdimg.com/it/u=3379522155,2601893953&fm=21&gp=0.jpg"); urls.add("http://img3.imgtn.bdimg.com/it/u=3269141824,887330507&fm=21&gp=0.jpg"); urls.add("http://img4.imgtn.bdimg.com/it/u=3099510203,1072263599&fm=21&gp=0.jpg"); urls.add("http://img4.imgtn.bdimg.com/it/u=2943793272,3641314298&fm=21&gp=0.jpg"); bannerLayout.setViewUrls(urls); // 添加监听事件 bannerLayout .setOnBannerItemClickListener(new BannerLayout.OnBannerItemClickListener() { @Override public void onItemClick(int position) { Toast.makeText(MainActivity.this, String.valueOf(position), Toast.LENGTH_SHORT) .show(); } }); // 低于三张,加载本地图片 final List<Integer> urls2 = new ArrayList<Integer>(); urls2.add(R.drawable.loading_bg_guid1); urls2.add(R.drawable.loading_bg_guid2); bannerLayout2.setViewRes(urls2);