自定义ViewPage+底部指示器(广告图片轮播)

自定义ViewPage+底部指示器(广告图片轮播)

     有不少项目都用到了广告图片的轮播,通过ViewPage来实现。每次都写一遍总感觉特别麻烦,今天就自己封装一个自带底部指示器的广告图片轮播CustomerViewPage。

    整体View是是在一个RelativeLayout中布局,由ViewPage(广告View)+LinearLayout(底部指示器)组成。如下图

 

  底部指示器轮播是通过一个线程来控制,所以实现了Runnable接口。如下代码解析:

CustomerViewPage的成员变量

<span style="font-size:14px;">public class CustomerViewPage extends RelativeLayout implements Runnable {

	/**
	 * 要显示的ViewPage对象
	 */
	private ViewPager viewPager;
	/**
	 * 放置底部指示物的子视图
	 */
	private LinearLayout viewDots;
	/**
	 * viewDots上的指示物
	 */
	private List<ImageView> dots;
	/**
	 * ViewPage项
	 */
	private List<View> views;
	/**
	 * 当前显示第几张图
	 */
	private int position = 0;
	/**
	 * 可不可以自动轮转(为true当手触摸时不轮转)
	 */
	private boolean isContinue = true;
	/**
	 * 切换时间/ms
	 */
	private long changeTime = 1500;

	/**
	 * 底部指示物之间的间距,默认2dp
	 */
	private int dotsSpacing = 2;
	/**
	 * 底部指示物大小,默认7dp
	 */
	private int circleRadio = 10;
	/**
	 * 轮播图片线程是否存活
	 */
	private boolean isAlive = true;
	/**
	 * 底部指示物在父View(即viewDots视图)的gravity属性,默认在中间
	 * Gravity.RIGHT | Gravity.LEFT | Gravity.CENTER
	 */
	private int gravity = Gravity.RIGHT;
}</span>
这里有三个构造函数,以防在代码中动态添加使用:
	public CustomerViewPage(Context context) {
		this(context, null);
	}

	public CustomerViewPage(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public CustomerViewPage(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		initView();
	}
初始化ViewPage和底部LinearLayout:
	private void initView() {
		/**
		 * 初始化ViewPage视图
		 */
		viewPager = new ViewPager(getContext());
		LayoutParams viewPagerlp = new LayoutParams(LayoutParams.MATCH_PARENT,
				LayoutParams.MATCH_PARENT);
		addView(viewPager, viewPagerlp);
		/**
		 * 初始化底部指示器视图
		 */
		viewDots = new LinearLayout(getContext());
		LayoutParams viewDotslp = new LayoutParams(LayoutParams.MATCH_PARENT,
				LayoutParams.WRAP_CONTENT);
		viewDotslp.addRule(ALIGN_PARENT_BOTTOM);
		viewDotslp.bottomMargin = dpTopx(5);
		viewDots.setGravity(gravity);
		addView(viewDots, viewDotslp);
	}
在内部有一个CustomerViewPageAdapter内部适配器:
	class CustomerViewPageAdapter extends PagerAdapter {

		@Override
		public int getCount() {
			return views == null ? 0 : views.size();
		}

		@Override
		public boolean isViewFromObject(View arg0, Object arg1) {
			return arg0 == arg1;
		}

		@Override
		public Object instantiateItem(ViewGroup container, int position) {
			if (views.get(position).getParent() != null) {
				((ViewGroup) views.get(position).getParent()).removeView(views
						.get(position));
			}
			container.addView(views.get(position));
			return views.get(position);
		}

		@Override
		public void destroyItem(ViewGroup container, int position, Object object) {
			container.removeView(views.get(position));
		}

	}
     这里特别要注意 instantiateItem这个方法。里面一定要写上下面这段代码,如果不写的话,有可能会抛出非法异常操作。如果只是一个Activity中使用不会有这个异常,但是超过两个地方以上使用这个CustomerViewPage的话就回抛出 illegalStateException(非法操作异常), 这是因为第一次将ViewPage中的VIew添加进去的时候,这个View是没有父控件的,但是当第二次添加的时候,该View已经有了一个父控件了,不允许再有其他的父控件,所以如果该View的父控件不为null,就必须先将该View的从父控件中remove掉,再添加进去。否则就回抛出illegalStateException
			if (views.get(position).getParent() != null) {
				((ViewGroup) views.get(position).getParent()).removeView(views
						.get(position));
			}
控制底部指示器轮播的线程:
	Handler pagerHandler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			viewPager.setCurrentItem(msg.what);
			super.handleMessage(msg);
		};
	};

	@Override
	public void run() {
		while (isAlive) {
			if (isContinue) {
				pagerHandler.sendEmptyMessage(position);
				position = (position + 1) % views.size();
				try {
					Thread.sleep(changeTime);
				} catch (InterruptedException e) {
				}
			}
		}
	}
这个就是暴露在外部的一个接口,设置ViewPage的View:
	public void setViewPageViews(List<View> views) {
		this.views = views;
		// 初始化底部的圆
		addDots(views.size());

		viewPager.setAdapter(new CustomerViewPageAdapter());
		viewPager.setOnPageChangeListener(new OnPageChangeListener() {

			@Override
			public void onPageSelected(int index) {
				position = index;
				for (int i = 0; i < dots.size(); i++) {
					if (position == i) {
						dots.get(i).setBackgroundResource(
								R.drawable.dot_focused);
					} else {
						dots.get(i)
								.setBackgroundResource(R.drawable.dot_normal);
					}
				}
			}

			@Override
			public void onPageScrolled(int arg0, float arg1, int arg2) {
			}

			@Override
			public void onPageScrollStateChanged(int arg0) {
			}
		});
		viewPager.setOnTouchListener(new OnTouchListener() {

			public boolean onTouch(View v, MotionEvent event) {
				switch (event.getAction()) {
				case MotionEvent.ACTION_DOWN:
				case MotionEvent.ACTION_MOVE:
					isContinue = false;
					break;
				case MotionEvent.ACTION_UP:
					isContinue = true;
					break;
				default:
					isContinue = true;
					break;
				}
				return false;
			}
		});
		new Thread(this).start();
	}

	private void addDots(int size) {
		dots = new ArrayList<ImageView>();
		for (int i = 0; i < size; i++) {
			ImageView dot = new ImageView(getContext());
			LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
					dpTopx(circleRadio), dpTopx(circleRadio));
			params.setMargins(dpTopx(dotsSpacing), 0, dpTopx(dotsSpacing), 0);
			dot.setLayoutParams(params);
			if (i == 0) {
				dot.setBackgroundResource(R.drawable.dot_focused);
			} else {
				dot.setBackgroundResource(R.drawable.dot_normal);
			}
			dots.add(dot);
			viewDots.addView(dot);
		}
	}
当该页面不可见的时候可以调用stop()方法停止掉该线程,否则她还是会一直跑下去。。。
	public void stop() {
		isAlive = false;
	}
将dp转化为px:
	private int dpTopx(int dp) {
		float scale = getResources().getDisplayMetrics().density;
		return (int) (dp * scale + 0.5f);
	}
基本上就没什么了,代码中都有解释,下面贴上CustomerViewPage整个代码:
package com.tz.viewpagedemo;

import java.util.ArrayList;
import java.util.List;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;

/**
 * 
 * @author TianZhao
 * Time 2015年11月26日
 * xiyouMobile
 */
@SuppressLint("HandlerLeak")
public class CustomerViewPage extends RelativeLayout implements Runnable {

	/**
	 * 要显示的ViewPage对象
	 */
	private ViewPager viewPager;
	/**
	 * 放置底部指示物的子视图
	 */
	private LinearLayout viewDots;
	/**
	 * viewDots上的指示物
	 */
	private List<ImageView> dots;
	/**
	 * ViewPage项
	 */
	private List<View> views;
	/**
	 * 当前显示第几张图
	 */
	private int position = 0;
	/**
	 * 可不可以自动轮转(为true当手触摸时不轮转)
	 */
	private boolean isContinue = true;
	/**
	 * 切换时间/ms
	 */
	private long changeTime = 1500;

	/**
	 * 底部指示物之间的间距,默认2dp
	 */
	private int dotsSpacing = 2;

	/**
	 * 底部指示物大小,默认7dp
	 */
	private int circleRadio = 10;

	/**
	 * 轮播图片线程是否存活
	 */
	private boolean isAlive = true;
	/**
	 * 底部指示物在父View(即viewDots视图)的gravity属性,默认在中间
	 * Gravity.RIGHT | Gravity.LEFT | Gravity.CENTER
	 */
	private int gravity = Gravity.RIGHT;

	public CustomerViewPage(Context context) {
		this(context, null);
	}

	public CustomerViewPage(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public CustomerViewPage(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		initView();
	}

	private void initView() {
		/**
		 * 初始化ViewPage视图
		 */
		viewPager = new ViewPager(getContext());
		LayoutParams viewPagerlp = new LayoutParams(LayoutParams.MATCH_PARENT,
				LayoutParams.MATCH_PARENT);
		addView(viewPager, viewPagerlp);
		/**
		 * 初始化底部指示器视图
		 */
		viewDots = new LinearLayout(getContext());
		LayoutParams viewDotslp = new LayoutParams(LayoutParams.MATCH_PARENT,
				LayoutParams.WRAP_CONTENT);
		viewDotslp.addRule(ALIGN_PARENT_BOTTOM);
		viewDotslp.bottomMargin = dpTopx(5);
		viewDots.setGravity(gravity);
		addView(viewDots, viewDotslp);
	}

	public void setViewPageViews(List<View> views) {
		this.views = views;
		// 初始化底部的圆
		addDots(views.size());

		viewPager.setAdapter(new CustomerViewPageAdapter());
		viewPager.setOnPageChangeListener(new OnPageChangeListener() {

			@Override
			public void onPageSelected(int index) {
				position = index;
				for (int i = 0; i < dots.size(); i++) {
					if (position == i) {
						dots.get(i).setBackgroundResource(
								R.drawable.dot_focused);
					} else {
						dots.get(i)
								.setBackgroundResource(R.drawable.dot_normal);
					}
				}
			}

			@Override
			public void onPageScrolled(int arg0, float arg1, int arg2) {
			}

			@Override
			public void onPageScrollStateChanged(int arg0) {
			}
		});
		viewPager.setOnTouchListener(new OnTouchListener() {

			public boolean onTouch(View v, MotionEvent event) {
				switch (event.getAction()) {
				case MotionEvent.ACTION_DOWN:
				case MotionEvent.ACTION_MOVE:
					isContinue = false;
					break;
				case MotionEvent.ACTION_UP:
					isContinue = true;
					break;
				default:
					isContinue = true;
					break;
				}
				return false;
			}
		});
		new Thread(this).start();
	}

	private void addDots(int size) {
		dots = new ArrayList<ImageView>();
		for (int i = 0; i < size; i++) {
			ImageView dot = new ImageView(getContext());
			LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
					dpTopx(circleRadio), dpTopx(circleRadio));
			params.setMargins(dpTopx(dotsSpacing), 0, dpTopx(dotsSpacing), 0);
			dot.setLayoutParams(params);
			if (i == 0) {
				dot.setBackgroundResource(R.drawable.dot_focused);
			} else {
				dot.setBackgroundResource(R.drawable.dot_normal);
			}
			dots.add(dot);
			viewDots.addView(dot);
		}
	}

	class CustomerViewPageAdapter extends PagerAdapter {

		@Override
		public int getCount() {
			return views == null ? 0 : views.size();
		}

		@Override
		public boolean isViewFromObject(View arg0, Object arg1) {
			return arg0 == arg1;
		}

		@Override
		public Object instantiateItem(ViewGroup container, int position) {
			if (views.get(position).getParent() != null) {
				((ViewGroup) views.get(position).getParent()).removeView(views
						.get(position));
			}
			container.addView(views.get(position));
			return views.get(position);
		}

		@Override
		public void destroyItem(ViewGroup container, int position, Object object) {
			container.removeView(views.get(position));
		}

	}

	Handler pagerHandler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			viewPager.setCurrentItem(msg.what);
			super.handleMessage(msg);
		};
	};

	@Override
	public void run() {
		while (isAlive) {
			if (isContinue) {
				pagerHandler.sendEmptyMessage(position);
				position = (position + 1) % views.size();
				try {
					Thread.sleep(changeTime);
				} catch (InterruptedException e) {
				}
			}
		}
	}

	private int dpTopx(int dp) {
		float scale = getResources().getDisplayMetrics().density;
		return (int) (dp * scale + 0.5f);
	}

	public void stop() {
		isAlive = false;
	}
}
底部的小圆点(指示物)是通过xml加载的:

dot_focused.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval" >
    <solid android:color="#aaFFFFFF" />
    <corners android:radius="10dip" />
</shape>
dot_normal.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval" >
    <solid android:color="#33000000" />
    <corners android:radius="10dip" />
</shape>
接下来就是使用了:
package com.tz.viewpagedemo;

import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;

/**
 * 
 * @author TianZhao
 * Time 2015年11月26日
 * xiyouMobile
 */
public class MainActivity extends Activity {

	private CustomerViewPage viewPage;
	private List<View> views;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		viewPage = (CustomerViewPage) findViewById(R.id.vp);
		initViews();
		viewPage.setViewPageViews(views);
	}

	private void initViews() {
		views = new ArrayList<>();
		ImageView imageView1 = new ImageView(this);
		ImageView imageView2 = new ImageView(this);
		ImageView imageView3 = new ImageView(this);
		ImageView imageView4 = new ImageView(this);
		ImageView imageView5 = new ImageView(this);
		imageView1.setBackgroundColor(Color.parseColor("#123456"));
		views.add(imageView1);
		imageView2.setBackgroundColor(Color.parseColor("#145826"));
		views.add(imageView2);
		imageView3.setBackgroundColor(Color.parseColor("#874592"));
		views.add(imageView3);
		imageView4.setBackgroundColor(Color.parseColor("#658415"));
		views.add(imageView4);
		imageView5.setBackgroundColor(Color.parseColor("#845163"));
		views.add(imageView5);
	}
}
下面是运行效果图:



源码地址:http://download.csdn.net/detail/tianzhaoai/9300821

GitHub源码地址:https://github.com/xyTianZhao/CustomerViewPage









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值