Android广告条效果--使用Volley实现网络图片的自动轮播(仿淘宝、网易广告页轮播效果)

最近在做自定义控件时,发现一个极其常用的效果--广告条,即图片的自动轮播效果。现在市面上大多数的APP软件都在使用这种展示广告的效果。闲来无事,我简单翻看了一下自己的手机软件,几乎都使用了这种图片自动轮播的策略来实现展示广告的效果:

                                        

结合自己所掌握的技术,不难分析这种广告轮播策略的实现原理:

1、利用ViewPager来接收来自网络的图片

2、定时的切换ViewPager里展示的图片

想要实现上面的效果,我们需要掌握三方面的知识:ViewPager的使用原理、网络图片的读取原理、定时器的原理。OK,下面就让我们一起去剖析每一部分的原理,进而实现我们想要的效果吧。先上一个效果图看看效果:

                                  

1、ViewPager的基本使用

ViewPager是Android扩展包V4包中的类,这个类可以让用户左右切换当前的View。

1、ViewPager类直接继承了ViewGroup类,所以它是一个容器类,可以在其中添加其他的view对象。

2、ViewPager类需要一个PagerAdapter适配器来给它提供数据。

3、ViewPager经常和Fragment一起使用,并且提供了专门的FragmentPagerAdapter和FragmentStatePagerAdapter类供Fragment中的ViewPager使用。

在使用ViewPager的时候,需要现在布局文件中定义该控件,我们项目中的布局如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="200dp" />

    <LinearLayout
        android:id="@+id/desc_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@id/viewPager"
        android:background="#33000000"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/tv_image_desc"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="@string/app_name"
            android:textColor="@android:color/white"
            android:textSize="18sp" />

        <!-- 小点点动态的通过代码区添加,这里只提供一个容器,用来存储小点点 -->

        <LinearLayout
            android:id="@+id/point_group"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:orientation="horizontal" >
        </LinearLayout>
    </LinearLayout>

</RelativeLayout>
经过我们的分析可知,ViewPager其实就是一个容器,我们向容器中添加的是View对象,显然我们需要展示的 广告图片不会只有一条,所以我们采用一个List<View>集合来存放需要展示的广告的View对象,然后通过PagerAdapter将数据设置给ViewPager来完成展示工作。在实际开发中,我们要展示的广告图片一般都是来自互联网的,所以我采用了Volley来获取网络图片。

2、Volley方式获取网络图片

volley获取网络图片的基本方法请参考Android Volley完全解析(二),使用Volley加载网络图片

在这个Demo中,根据图片轮播的需求,我对Volley进行了一些简单的封装,集成了一个处理网络图片的工具,代码如下:

/**
 * 使用Volley框架获得网络图片
 * 
 * @author Administrator
 * 
 */
public class ImageUtils {
	/**
	 * 如果宽高不指定,会根据ImageView的设置进行图片压缩
	 * 
	 * @param context
	 * @param imageView
	 *            将图片设置到哪一个ImageView上
	 * @param iamgeUrl
	 *            图片地址
	 * @param defaultImageResId
	 *            默认显示的图片资源id
	 * @param errorImageResId
	 *            加载失败时显示的图片资源id
	 */
	public static void getImage(Context context, ImageView imageView, String iamgeUrl, int defaultImageResId, int errorImageResId) {
		getImage(context, imageView, iamgeUrl, defaultImageResId, errorImageResId, 0, 0);
	}

	/**
	 * 
	 * @param context
	 * @param imageView
	 * @param iamgeUrl
	 * @param defaultImageResId
	 * @param errorImageResId
	 * @param maxWidth
	 * @param maxHeight
	 */
	public static void getImage(Context context, ImageView imageView, String iamgeUrl, int defaultImageResId, int errorImageResId, int maxWidth, int maxHeight) {
		try {
			/* volley */
			// 获得请求队列
			RequestQueue mQueue = Volley.newRequestQueue(context);
			// 图片的缓存暂时是一个空实现,没有做任何处理
			ImageLoader imageLoader = new ImageLoader(mQueue, new com.android.volley.toolbox.ImageLoader.ImageCache() {
				@Override
				public void putBitmap(String arg0, Bitmap arg1) {

				}

				@Override
				public Bitmap getBitmap(String arg0) {
					return null;
				}
			});
			// 第一个参数是将图片设置到哪一个ImageView上,第二个参数是默认的图片资源id,第三个参数是失败时显示的图片资源id
			ImageListener listener = ImageLoader.getImageListener(imageView, defaultImageResId, errorImageResId);
			ImageSize imageSize = getImageViewWidth(imageView);

			if (maxWidth != 0 && maxHeight != 0) {
				imageSize.width = maxWidth;
				imageSize.height = maxHeight;
			}

			Log.d("imageSize", "width>>" + imageSize.width + "height>>" + imageSize.height);
			imageLoader.get(iamgeUrl, listener, imageSize.width, imageSize.height);
		} catch (Exception e) {
			// TODO: handle exception
		}

	}

	/**
	 * 根据ImageView获得适当的压缩的宽和高
	 * 
	 * @param imageView
	 * @return
	 */
	private static ImageSize getImageViewWidth(ImageView imageView) {
		ImageSize imageSize = new ImageSize();
		final DisplayMetrics displayMetrics = imageView.getContext().getResources().getDisplayMetrics();
		final LayoutParams params = imageView.getLayoutParams();

		int width = params.width == LayoutParams.WRAP_CONTENT ? 0 : imageView.getWidth(); // Get
																							// actual
																							// image
																							// width
		if (width <= 0)
			width = params.width; // Get layout width parameter
		if (width <= 0)
			width = getImageViewFieldValue(imageView, "mMaxWidth"); // Check
																	// maxWidth
																	// parameter
		if (width <= 0)
			// width = displayMetrics.widthPixels;
			width = 350;
		int height = params.height == LayoutParams.WRAP_CONTENT ? 0 : imageView.getHeight(); // Get
																								// actual
																								// image
																								// height
		if (height <= 0)
			height = params.height; // Get layout height parameter
		if (height <= 0)
			height = getImageViewFieldValue(imageView, "mMaxHeight"); // Check
																		// maxHeight
																		// parameter
		if (height <= 0)
			// height = displayMetrics.heightPixels;
			height = 350;
		imageSize.width = width;
		imageSize.height = height;
		return imageSize;

	}

	private static class ImageSize {
		int width;
		int height;
	}

	/**
	 * 反射获得ImageView设置的最大宽度和高度
	 * 
	 * @param object
	 * @param fieldName
	 * @return
	 */
	private static int getImageViewFieldValue(Object object, String fieldName) {
		int value = 0;
		try {
			Field field = ImageView.class.getDeclaredField(fieldName);
			field.setAccessible(true);
			int fieldValue = (Integer) field.get(object);
			if (fieldValue > 0 && fieldValue < Integer.MAX_VALUE) {
				value = fieldValue;
			}
		} catch (Exception e) {
		}
		return value;
	}

}
代码中的注释已经很详细了,我这里就不做过多的赘述,在后面我也会提供源码下载,需要的朋友可以直接看源码。

PS:本人写代码很烂,所有代码中有好多地方有待优化,期盼着您能及时指出我的不足之处,以便大家共同学习进步。

网络资源的地址采用JSONArray的方式加载,在主Activity的onCreate方法中初始化的时候,就加载网络资源,初始化的代码如下:

/**
	 * 初始化的操作
	 */
	private void initView() {
		viewPager = (ViewPager) findViewById(R.id.viewPager);
		desc_image = (TextView) findViewById(R.id.tv_image_desc);
		point_group = (LinearLayout) findViewById(R.id.point_group);
		// 广告的图片资源Array
		advertiseArray = new JSONArray();
		try {
			JSONObject advertise0 = new JSONObject();
			advertise0.putOpt("advertise", "http://pic1.ooopic.com/uploadfilepic/sheji/2009-09-12/OOOPIC_wenneng837_200909122b2c8368339dd52a.jpg:小泡泡");
			JSONObject advertise1 = new JSONObject();
			advertise1.putOpt("advertise", "http://image.zcool.com.cn/59/54/m_1303967870670.jpg:时代会远去,记忆不会,流行会过时,也会回来");
			JSONObject advertise2 = new JSONObject();
			advertise2.putOpt("advertise", "http://image.zcool.com.cn/47/19/1280115949992.jpg:小心闪电");
			JSONObject advertise3 = new JSONObject();
			advertise3.putOpt("advertise", "http://image.zcool.com.cn/59/11/m_1303967844788.jpg:闪亮的年代");
			JSONObject advertise4 = new JSONObject();
			advertise4.putOpt("advertise", "http://image.zcool.com.cn/56/35/1303967876491.jpg:时光会紧锁,美梦不曾停");
			advertiseArray.put(advertise0);
			advertiseArray.put(advertise1);
			advertiseArray.put(advertise2);
			advertiseArray.put(advertise3);
			advertiseArray.put(advertise4);
			initSource(advertiseArray, true);
		} catch (JSONException e) {
			e.printStackTrace();
		}
	}
网络图片的地址后边跟的是描述信息,在真正的项目开发中,这一部分可以使用配置文件来定义图片的地址和描述信息。

图片下边的小点点是通过代码动态添加的,需要展示几张图片,就添加几个小点点,定义ViewPager的图片滑动的监听,当图片滑动的时候,设置小点点的背景以及显示的描述信息,这一部分对应的代码如下所示:

<span style="white-space:pre">	</span>/**
	 * 初始化资源。将资源Array封装成List<ImageView>集合,传递给ViewPager的适配器
	 * 
	 * @param advertiseArray
	 * @param fitXY
	 *            拉伸展开,适应屏幕的xy,否则水平居中
	 */
	private void initSource(final JSONArray advertiseArray, boolean fitXY) {
		views = new ArrayList<View>();
		for (int i = 0; i < advertiseArray.length(); i++) {
			if (fitXY) {
				views.add(View.inflate(MainActivity.this, R.layout.image_advertise_fit, null));
			} else {
				views.add(View.inflate(MainActivity.this, R.layout.image_advertise_center, null));
			}
			// 添加指示点
			ImageView point = new ImageView(this);
			LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);

			params.rightMargin = 20;
			params.gravity = Gravity.CENTER;
			point.setLayoutParams(params);

			point.setBackgroundResource(R.drawable.point_bg);
			if (i == 0) {
				point.setEnabled(true);// 将小点点设置成灰色
			} else {
				point.setEnabled(false);
			}
			point_group.addView(point);

		}
		// 准备好了views,之后设置ViewPager的适配器
		AdvertiseAdapter adapter = new AdvertiseAdapter(this, views, advertiseArray);
		// 设置适配器,向viewpager容器中添加图片
		viewPager.setAdapter(adapter);
		// 让ViewPager左右滑动的时候无限循环
		viewPager.setCurrentItem(Integer.MAX_VALUE / 2 - (Integer.MAX_VALUE / 2 % views.size()));
		// 设置viewpager滑动时候的监听,设置图片的描述和小点点的切换
		viewPager.setOnPageChangeListener(new OnPageChangeListener() {
			/**
			 * 页面切换后调用
			 */
			@Override
			public void onPageSelected(int position) {
				position = position % views.size();
				// 设置图片的文字描述信息
				String result = advertiseArray.optJSONObject(position).optString("advertise");
				String desc = result.substring(result.lastIndexOf(":") + 1, result.length());
				desc_image.setText(desc);
				// 改变指示点的状态
				// 当前位置的点设为灰色,true
				point_group.getChildAt(position).setEnabled(true);

				// 上一个位置的点设为透明的 false
				point_group.getChildAt(lastPosition).setEnabled(false);
				lastPosition = position;
			}

			/**
			 * 页面正在滑动的时候的回调
			 */
			@Override
			public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

			}

			/**
			 * 页面状态发生改变的时候回调
			 */
			@Override
			public void onPageScrollStateChanged(int state) {

			}
		});
	}
ViewPager的适配器如下:

public class AdvertiseAdapter extends PagerAdapter {
	private Context context;
	private List<View> views;
	JSONArray advertiseArray;

	public AdvertiseAdapter() {
		super();
	}

	public AdvertiseAdapter(Context context, List<View> views, JSONArray advertiseArray) {
		this.context = context;
		this.views = views;
		this.advertiseArray = advertiseArray;
	}

	/**
	 * 获得页面的总数
	 */
	@Override
	public int getCount() {
		return Integer.MAX_VALUE;
	}

	/**
	 * 判断View和object的对应关系
	 */
	@Override
	public boolean isViewFromObject(View view, Object object) {
		return (view == object);
	}

	/**
	 * 销毁对应位置的object
	 */
	@Override
	public void destroyItem(ViewGroup container, int position, Object object) {
		((ViewPager) container).removeView(views.get(position % views.size()));
	}

	/**
	 * 获取相应位置的view 
	 * container view的容器,其实就是ViewPager本身 
	 * position 相应的位置
	 */
	@Override
	public Object instantiateItem(ViewGroup container, int position) {
		((ViewPager) container).addView(views.get(position % views.size()));
		View view = views.get(position % views.size());
		// 读取图片url的地址
		String result = advertiseArray.optJSONObject(position % views.size()).optString("advertise");
		// String desc = result.substring(result.lastIndexOf(":") + 1,
		// result.length());
		String imageUrl = result.substring(0, result.lastIndexOf(":"));
		// 广告图片存放的ImageView
		ImageView ivAdvertise = (ImageView) view.findViewById(R.id.ivAdvertise);
		// ImageUtils工具就已经将网络图片设置到了ivAdvertise这个ImageView上了
		ImageUtils.getImage(context, ivAdvertise, imageUrl, R.drawable.ic_launcher, R.drawable.ic_launcher);

		return view;
	}
}
在适配器中,将页面的总数设置成整数的最大值,其实就是无限大了,目的就是为了实现ViewPager左右的无限滑动,需要注意的就是position的位置需要做一些运算来获得view在集合中的位置,position % views.size(),获取view在views集合中真实的位置。

3、定时切换ViewPager,实现轮播效果

定时轮播我目前想到有三种策略

1、定时器timer 

2、开启子线程,while true循环 

3、使用handler方式发送延时消息,实现循环
这里我使用的是第三种,handler发送延时消息来完成轮播效果。首先定义一个Handler,在它的handleMessage方法中,发送一个延时消息,这就实现了循环发送消息的循环效果

private Handler handler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			// viewPager滑动到下一页
			viewPager.setCurrentItem(viewPager.getCurrentItem() + 1);
			// 发送一个延时消息,延时2秒钟继续执行handler,达到循环的效果
			if (isRunning) {
				handler.sendEmptyMessageDelayed(0, 2000);
			}
		};
	};

OK,写到这里一个Android广告条的轮播效果也就实现了,通过这个例子,我们学习了如何使用volley加载 网络图片以及ViewPager的基本使用方式,同时对于定时器 又有了一个全新的认识。
PS:本人写代码很烂,所有代码中有好多地方有待优化,期盼着您能及时指出我的不足之处,以促进大家的共同进步。

点我下载源码


  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值