图片轮播器是一种交互很好的广告推送的组件,既完成了我们向用户推送信息的目的,同时形式还很优雅,在app中都有使用到,但是android的源生组件中并没有轮播器,这样我们就需要自定义来实现一个了。
先上效果图,分为自动滚动和手动滚动:
首先新建一个布局文件,添加以下代码:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" >
<android.support.v4.view.ViewPager android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/white" />
<LinearLayout android:id="@+id/dotLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginBottom="20dp" android:gravity="center" android:orientation="horizontal" android:padding="8dp" >
<View android:id="@+id/v_dot1" android:layout_width="8dp" android:layout_height="8dp" android:background="@drawable/shape_oval_dot_orage" />
<View android:id="@+id/v_dot2" android:layout_width="8dp" android:layout_height="8dp" android:layout_marginLeft="25dp" android:background="@drawable/shape_oval_dot_dark" />
</LinearLayout>
</RelativeLayout>
轮播器内部其实也是使用了viewPager,首先自定义一个PagerAdapter来初始化每页的View,其实每页只有一个ImageView:
/** * 填充ViewPager的页面适配器 * */
private class MyPagerAdapter extends PagerAdapter{
@Override
public void destroyItem(View container, int position, Object object) {
// TODO Auto-generated method stub
//((ViewPag.er)container).removeView((View)object);
((ViewPager)container).removeView(imageViewsList.get(position));
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
ImageView imageView = imageViewsList.get(position);
if (!StringUtil.isEmpty(imageUrls)) {
if (imageUrls[0].contains("http")) {
bitmapUtils.display(imageView, imageView.getTag().toString());
}else {
imageView.setImageResource(Integer.parseInt(imageUrls[position]));
}
}
imageView.setBackgroundResource(R.color.white);
container.addView(imageView);
return imageView;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return imageViewsList.size();
}
}
在instantiateItem中初始化这个ImageView并返回,这里我做了一些业务处理上的处理,我定义了2中传入图片的方式,如果请求到了一组图片就存放url,如果失败了那么现实默认的本地图片(以Resource方式存放),图片的集合存放的是URL还是Resource。不需要这样处理可以把这里的代码删掉就可以了。
然后给ViewPager设置一个监听器,监听到翻页事件:
private class MyPageChangeListener implements OnPageChangeListener{
boolean isAutoPlay = false;
@Override
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub
switch (arg0) {
case 1:// 手势滑动,空闲中
isAutoPlay = false;
break;
case 2:// 界面切换中
isAutoPlay = true;
break;
case 0:// 滑动结束,即切换完毕或者加载完毕
if (isAutoPlay) {
// 当前为最后一张,此时从右向左滑,则切换到第一张
if (viewPager.getCurrentItem() == viewPager.getAdapter().getCount() - 1 && !isAutoPlay) {
viewPager.setCurrentItem(0);
}
// 当前为第一张,此时从左向右滑,则切换到最后一张
else if (viewPager.getCurrentItem() == 0 && !isAutoPlay) {
viewPager.setCurrentItem(viewPager.getAdapter().getCount() - 1);
}
}
break;
}
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
}
@Override
public void onPageSelected(int pos) {
// TODO Auto-generated method stub
currentItem = pos;
for(int i=0;i < dotViewsList.size();i++){
if(i == pos){
((View)dotViewsList.get(pos)).setBackgroundResource(R.drawable.shape_oval_dot_orage);
}else {
((View)dotViewsList.get(i)).setBackgroundResource(R.drawable.shape_oval_dot_dark);
}
}
}
}
在onPageScrollStateChanged方法中处理最后一张图片和第一张图片的特殊情况:
在onPageSelected方法中处理悬浮的小点点的热点效果,把当前页对应的小点点设置为热点显示高亮状态
定义一个initUI方法,在请求到图片之后调用以初始化控件:
/** * 初始化Views等UI */
private void initUI(Context context){
if(imageUrls == null || imageUrls.length == 0)
return;
LayoutInflater.from(context).inflate(R.layout.layout_slideshow, this, true);
LinearLayout dotLayout = (LinearLayout)findViewById(R.id.dotLayout);
dotLayout.removeAllViews();
// 热点个数与图片特殊相等
for (int i = 0; i < imageUrls.length; i++) {
ImageView view = new ImageView(context);
view.setTag(imageUrls[i]);
view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
imageViewsList.add(view);
ImageView dotView = new ImageView(context);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
params.leftMargin = 15;
params.rightMargin = 15;
dotLayout.addView(dotView, params);
dotViewsList.add(dotView);
}
/////////首次进入的时候初始化点点
for(int i=0;i < dotViewsList.size();i++){
if(i == 0){
((View)dotViewsList.get(0)).setBackgroundResource(R.drawable.shape_oval_dot_orage);
}else {
((View)dotViewsList.get(i)).setBackgroundResource(R.drawable.shape_oval_dot_dark);
}
}
//////////////////////////
if (imageViewsList.size()>0 && goListener!=null) {
imageViewsList.get(imageViewsList.size()-1).setOnClickListener(goListener);
}
viewPager = (ViewPager) findViewById(R.id.viewPager);
viewPager.setFocusable(true);
viewPager.setAdapter(new MyPagerAdapter());
viewPager.setOnPageChangeListener(new MyPageChangeListener());
}
initUI方法中初始化了Viewpager也设置了Adapter,那么只要在获取到图片数据之后调用该方法就OK了
定义一个后台异步任务,用于从服务器获取图片集合,获取完成后调用initUI()方法:
/** * 异步任务,获取数据 * */
class GetListTask extends AsyncTask<String, Integer, Boolean> {
@Override
protected Boolean doInBackground(String... params) {
try {
// 这里一般调用服务端接口获取一组轮播图片,下面是从百度找的几个图片
imageUrls = new String[]{
"http://image.zcool.com.cn/59/54/m_1303967870670.jpg",
"http://image.zcool.com.cn/47/19/1280115949992.jpg",
"http://image.zcool.com.cn/59/11/m_1303967844788.jpg"
};
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (result) {
initUI(context);
}
}
}
这里就模拟了,写一组静态图片,需要请求服务器图片的在这里请求
然后再添加自动播放的功能,在初始化的时候创建一个异步任务,每隔一段时间,进行一次发生一个handler消息,handler接收到消息后进行翻页:
/** * 开始轮播图切换 */
private void startPlay(){
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
scheduledExecutorService.scheduleAtFixedRate(new SlideShowTask(), 4, 4, TimeUnit.SECONDS);
}
/** *执行轮播图切换任务 * */
private class SlideShowTask implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
synchronized (viewPager) {
currentItem = (currentItem+1)%imageViewsList.size();
handler.obtainMessage().sendToTarget();
}
}
}
//Handler
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
viewPager.setCurrentItem(currentItem);
}
};
然后设置一个标志位来决定是否需要自动轮播或者手动翻页,
需要手动翻页就调用startPlay(),不需要就跳过:
if(isAutoPlay){
startPlay();
}
这样功能基本就完了,下面贴上完整代码:
/** * ViewPager实现的轮播图广告自定义视图,如京东首页的广告轮播图效果; * 既支持自动轮播页面也支持手势滑动切换页面 * * */
public class SlideShowView extends FrameLayout {
// 使用universal-image-loader插件读取网络图片,需要工程导入universal-image-loader-1.8.6-with-sources.jar
// private ImageLoader imageLoader = ImageLoader.getInstance();
private BitmapUtils bitmapUtils;
//轮播图图片数量
private final static int IMAGE_COUNT = 4;
//自动轮播的时间间隔
private final static int TIME_INTERVAL = 5;
//自动轮播启用开关
private final static boolean isAutoPlay = false;
//跳转监听器
private OnClickListener goListener;
//自定义轮播图的资源
private String[] imageUrls;
// private int[] imageSrcs;
//放轮播图片的ImageView 的list
private List<ImageView> imageViewsList;
//放圆点的View的list
private List<View> dotViewsList;
private ViewPager viewPager;
//当前轮播页
private int currentItem = 0;
//定时任务
private ScheduledExecutorService scheduledExecutorService;
private Context context;
//Handler
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
viewPager.setCurrentItem(currentItem);
}
};
public SlideShowView(Context context) {
this(context,null);
}
public SlideShowView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlideShowView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
if (!isInEditMode()) {
this.context = context;
this.bitmapUtils = BitmapHelp.getBitmapUtils();
this.bitmapUtils.configDefaultLoadingImage(R.drawable.none);
this.bitmapUtils.configDefaultLoadFailedImage(R.drawable.none);
initData();
// 一步任务获取图片
new GetListTask().execute("");
if(isAutoPlay){
startPlay();
}
}
}
/** * 开始轮播图切换 */
private void startPlay(){
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
scheduledExecutorService.scheduleAtFixedRate(new SlideShowTask(), 4, 4, TimeUnit.SECONDS);
}
/** * 停止轮播图切换 */
private void stopPlay(){
scheduledExecutorService.shutdown();
}
/** * 初始化相关Data */
private void initData(){
imageViewsList = new ArrayList<ImageView>();
dotViewsList = new ArrayList<View>();
}
/** * 初始化Views等UI */
private void initUI(Context context){
if(imageUrls == null || imageUrls.length == 0)
return;
LayoutInflater.from(context).inflate(R.layout.layout_slideshow, this, true);
LinearLayout dotLayout = (LinearLayout)findViewById(R.id.dotLayout);
dotLayout.removeAllViews();
// 热点个数与图片特殊相等
for (int i = 0; i < imageUrls.length; i++) {
ImageView view = new ImageView(context);
view.setTag(imageUrls[i]);
// if(i==0)//给一个默认图
// view.setBackgroundResource(R.drawable.defalt);
view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
// view.setScaleType(ScaleType.FIT_START);
imageViewsList.add(view);
ImageView dotView = new ImageView(context);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
params.leftMargin = 15;
params.rightMargin = 15;
dotLayout.addView(dotView, params);
dotViewsList.add(dotView);
}
/////////首次进入的时候初始化点点
for(int i=0;i < dotViewsList.size();i++){
if(i == 0){
((View)dotViewsList.get(0)).setBackgroundResource(R.drawable.shape_oval_dot_orage);
}else {
((View)dotViewsList.get(i)).setBackgroundResource(R.drawable.shape_oval_dot_dark);
}
}
//////////////////////////
if (imageViewsList.size()>0 && goListener!=null) {
imageViewsList.get(imageViewsList.size()-1).setOnClickListener(goListener);
}
viewPager = (ViewPager) findViewById(R.id.viewPager);
viewPager.setFocusable(true);
viewPager.setAdapter(new MyPagerAdapter());
viewPager.setOnPageChangeListener(new MyPageChangeListener());
}
public void setOnGolistener(OnClickListener goListener) {
this.goListener = goListener;
}
/** * 填充ViewPager的页面适配器 * */
private class MyPagerAdapter extends PagerAdapter{
@Override
public void destroyItem(View container, int position, Object object) {
// TODO Auto-generated method stub
//((ViewPag.er)container).removeView((View)object);
((ViewPager)container).removeView(imageViewsList.get(position));
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
ImageView imageView = imageViewsList.get(position);
if (!StringUtil.isEmpty(imageUrls)) {
if (imageUrls[0].contains("http")) {
bitmapUtils.display(imageView, imageView.getTag().toString());
}else {
imageView.setImageResource(Integer.parseInt(imageUrls[position]));
}
}
imageView.setBackgroundResource(R.color.white);
container.addView(imageView);
return imageView;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return imageViewsList.size();
}
@Override
public boolean isViewFromObject(View arg0, Object arg1) {
// TODO Auto-generated method stub
return arg0 == arg1;
}
@Override
public void restoreState(Parcelable arg0, ClassLoader arg1) {
// TODO Auto-generated method stub
}
@Override
public Parcelable saveState() {
// TODO Auto-generated method stub
return null;
}
@Override
public void startUpdate(View arg0) {
// TODO Auto-generated method stub
}
@Override
public void finishUpdate(View arg0) {
// TODO Auto-generated method stub
}
}
/** * ViewPager的监听器 * 当ViewPager中页面的状态发生改变时调用 * */
private class MyPageChangeListener implements OnPageChangeListener{
boolean isAutoPlay = false;
@Override
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub
switch (arg0) {
case 1:// 手势滑动,空闲中
isAutoPlay = false;
break;
case 2:// 界面切换中
isAutoPlay = true;
break;
case 0:// 滑动结束,即切换完毕或者加载完毕
if (isAutoPlay) {
// 当前为最后一张,此时从右向左滑,则切换到第一张
if (viewPager.getCurrentItem() == viewPager.getAdapter().getCount() - 1 && !isAutoPlay) {
viewPager.setCurrentItem(0);
}
// 当前为第一张,此时从左向右滑,则切换到最后一张
else if (viewPager.getCurrentItem() == 0 && !isAutoPlay) {
viewPager.setCurrentItem(viewPager.getAdapter().getCount() - 1);
}
}
break;
}
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
}
@Override
public void onPageSelected(int pos) {
// TODO Auto-generated method stub
currentItem = pos;
for(int i=0;i < dotViewsList.size();i++){
if(i == pos){
((View)dotViewsList.get(pos)).setBackgroundResource(R.drawable.shape_oval_dot_orage);
}else {
((View)dotViewsList.get(i)).setBackgroundResource(R.drawable.shape_oval_dot_dark);
}
}
}
}
/** *执行轮播图切换任务 * */
private class SlideShowTask implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
synchronized (viewPager) {
currentItem = (currentItem+1)%imageViewsList.size();
handler.obtainMessage().sendToTarget();
}
}
}
/** * 销毁ImageView资源,回收内存 * */
private void destoryBitmaps() {
for (int i = 0; i < IMAGE_COUNT; i++) {
ImageView imageView = imageViewsList.get(i);
Drawable drawable = imageView.getDrawable();
if (drawable != null) {
//解除drawable对view的引用
drawable.setCallback(null);
}
}
}
/** * 释放资源,在activity结束时请调用 */
public void destory() {
stopPlay();
destoryBitmaps();
}
public void setImageSrcs(int[] imageSrcs) {
if (StringUtil.isEmpty(imageSrcs)) {
return;
}
imageUrls = new String[imageSrcs.length];
for (int i = 0; i < imageSrcs.length; i++) {
imageUrls[i] = imageSrcs[i]+"";
}
}
public void setImageUrls(String[] imageUrls) {
this.imageUrls = imageUrls;
}
/** * 异步任务,获取数据 * */
class GetListTask extends AsyncTask<String, Integer, Boolean> {
@Override
protected Boolean doInBackground(String... params) {
try {
// 这里一般调用服务端接口获取一组轮播图片,下面是从百度找的几个图片
imageUrls = new String[]{
"http://image.zcool.com.cn/59/54/m_1303967870670.jpg",
"http://image.zcool.com.cn/47/19/1280115949992.jpg",
"http://image.zcool.com.cn/59/11/m_1303967844788.jpg"
};
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (result) {
initUI(context);
}
}
}
}