轮播图的实现

  平时开发软件的时候,经常会碰到轮播图这样的需求,下面介绍一种轮播图的实现方式:

我把轮播图写成了一个自定义控件,需要集成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);

 

猜你喜欢

转载自yaolinnan.iteye.com/blog/2297992
今日推荐