开发中经常要做轮播图,每次都去写的话确实很烦,网上也有类似这样的控件,但感觉都不太如意,要么繁琐要么简单到不能达到想要的效果,也很不方便,于是自己就把项目中写的轮播图抽了出来,基于MVC的原理把他改造成了直接可以拿来引用的控件,只需要四行代码就能实现无限轮播了,图片显示是通过GIthub上的开源框架Universal_Image_Loader来加载网络图片显示到轮播图中,轮播图是通过Viewpager实现的,很实用。。。在这里我把界面效果做出博客分享出来,需要源码的朋友到我的资源页下载
下载地址:http://download.csdn.net/detail/u013122144/9395916
要使用ImageLoader可以要到这里下载jar包:
https://github.com/nostra13/Android-Universal-Image-Loader
接下来就看我们的轮播图的holder类代码,轮播图的实现就在这里:
/**
* @类名: HomeAutoSwitchPicHolder
* @创建者: 史翔宇
* @创建时间:2015年12月18日
* @描述: 轮播图控件
*/
public class HomeAutoSwitchPicHolder extends BaseHolder<List<String>>
implements ViewPager.OnPageChangeListener {
private ViewPager mPager;
private HomeAutoSwitchPicAdapter adapter;
private LinearLayout mPointContainer;
private List<String> mPictures;
private AutoSwitchTask mSwitchTask;
public HomeAutoSwitchPicHolder(Context context) {
super(context);
}
protected View initView() {
View view = View.inflate(mContext, R.layout.home_autoswitchpic, null);
mPager=(ViewPager) view.findViewById(R.id.item_home_viewPager);
mPointContainer=(LinearLayout) view.findViewById(R.id.item_home_point_container);
return view;
}
protected void refreshUI(List<String> data) {
this.mPictures = data;
adapter=new HomeAutoSwitchPicAdapter();
this.mPager.setAdapter(adapter);
addPointToContainer(data);
this.mPager.setOnPageChangeListener(this);
int middle = Integer.MAX_VALUE/1000;
int extra = middle % mPictures.size();
this.mPager.setCurrentItem(middle - extra);
if (mSwitchTask == null)
mSwitchTask = new AutoSwitchTask();
//开始轮播
this.mSwitchTask.start();
// 给ViewPager设置touch的监听
mPager.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 希望轮播停止
mSwitchTask.stop();
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// 希望播放
mSwitchTask.start();
break;
default:
break;
}
return false;
}
});
}
/**
* 给容器添加点
* @param data
*/
protected void addPointToContainer(List<String> data) {
mPointContainer.removeAllViews();
for (int i = 0; i < data.size(); i++) {
View view = new View(UIUtils.getContext());
view.setBackgroundResource(R.drawable.home_point_normal);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(UIUtils.dip2px(6), UIUtils.dip2px(6));
if (i != 0) {
params.leftMargin = UIUtils.dip2px(8);
params.bottomMargin = UIUtils.dip2px(8);
} else {
view.setBackgroundResource(R.drawable.home_point_select);
}
mPointContainer.addView(view,params);
}
}
/**
* 轮播任务
*/
class AutoSwitchTask implements Runnable {
public void run() {
int item = mPager.getCurrentItem();
mPager.setCurrentItem(++item);
UIUtils.postDelayed(this, 3000);
}
public void start() {
stop();
UIUtils.postDelayed(this, 3000);
}
public void stop() {
UIUtils.removeCallbacks(this);
}
}
class HomeAutoSwitchPicAdapter extends PagerAdapter {
@Override
public int getCount() {
return mPictures != null ?Integer.MAX_VALUE : 0;
//return mPictures != null ? mPictures.size() : 0;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
position = position % mPictures.size();
ImageView iv = new ImageView(UIUtils.getContext());
iv.setScaleType(ImageView.ScaleType.FIT_XY);
// 设置网络图片
// BitmapHelper.display(iv, mPictures.get(position));
ImageLoader.getInstance().displayImage(mPictures.get(position),iv);
container.addView(iv);
return iv;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
// 页面选中时
position = position % mPictures.size();
int count = mPointContainer.getChildCount();
for (int i = 0; i < count; i++)
{
View view = mPointContainer.getChildAt(i);
view.setBackgroundResource(i == position ? R.drawable.home_point_select
: R.drawable.home_point_normal);
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
}
轮播图的布局文件home_autoswitchpic.xml采用了相对布局,包含一个ViewPager和一个指针容器,指针容器是用来存放下面动态变化的圆点的,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<!-- 轮播图视图 -->
<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/item_home_viewPager"
android:layout_width="match_parent"
android:layout_height="160dp" >
</android.support.v4.view.ViewPager>
<!-- 轮播图下方的圆点指针容器 -->
<LinearLayout
android:id="@+id/item_home_point_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/item_home_viewPager"
android:layout_centerHorizontal="true"
android:layout_marginBottom="10dp"
android:orientation="horizontal" >
</LinearLayout>
</RelativeLayout>
另外代码里用到了一个基类BaseHolder和一个工具类UIUtils,先看看BaseHolder,它就相当于一个controller,拥有一个根视图rootView和数据mData, 通过setData后来刷新视图,了解mvc原理的更容易懂,看下它的源码:
/**
* ClassName : BaseHolder
* Author : 史翔宇
* Time : 2015/11/19
* Desc :item view 对应的 View的持有者的基类
*/
public abstract class BaseHolder<T> {
//提供不具体的view,让子类去实现具体的
protected Context mContext;
protected View mRootView;//根视图
protected T mData;//数据
public BaseHolder(Context context){
mContext=context;
mRootView=initView();
//设置标记
mRootView.setTag(this);
}
/**
* 实现view的布局
* @return
*/
protected abstract View initView();
/**
* 让子类根据数据来刷新自己的视图
*
* @param data
*/
protected abstract void refreshUI(T data);
/**
* 获取根布局
*
* @return
*/
public View getRootView()
{
return mRootView;
}
public void setData(T data)
{
// 保存数据
this.mData = data;
// 通过数据来改变UI显示
refreshUI(data);
}
}
**
* @类名: UIUtils
* @创建者: 史翔宇
* @创建时间:2015年11月18日
* @描述: UI工具类
*/
public class UIUtils {
public static Context getContext() {
return BaseApplication.getContext();
}
public static Resources getResources() {
return getContext().getResources();
}
public static String getPackageName() {
return getContext().getPackageName();
}
public static String getString(int resId) {
return getResources().getString(resId);
}
public static String[] getStringArray(int resId) {
return getResources().getStringArray(resId);
}
public static int getColor(int resId) {
return getResources().getColor(resId);
}
public static Handler getMainHandler() {
return BaseApplication.getMainHander();
}
public static long getMainThreadId() {
return BaseApplication.getMainThreadId();
}
public static void post(Runnable task) {
//判断是否是在主线程中指执行的
//获得执行该方法的线程的id
int currentThreadId = android.os.Process.myTid();
if (getMainThreadId() == currentThreadId) {
//在主线程中执行的
task.run();
} else {
//在子线程中执行的
getMainHandler().post(task);
}
}
/**
* dip 转 px
*
* @param dip
* @return
*/
public static int dip2px(int dip) {
//
// 公式: dp = px / (dpi / 160) px = dp * (dpi / 160)
// dp = px / denisity
// px = dp * denisity;
DisplayMetrics metrics = getResources().getDisplayMetrics();
float density = metrics.density;
return (int) (dip * density + 0.5f);
}
/**
* px 转 dip
*
* @param px
* @return
*/
public static int px2dip(int px) {
// dp = px / denisity
DisplayMetrics metrics = getResources().getDisplayMetrics();
float density = metrics.density;
return (int) (px / density + 0.5f);
}
/**
* 执行需要延时的任务
*/
public static void postDelayed(Runnable task, int delayed) {
getMainHandler().postDelayed(task, delayed);
}
/**
* 移除任务
*
* @param task
*/
public static void removeCallbacks(Runnable task) {
getMainHandler().removeCallbacks(task);
}
}
/**
* @类名: BaseApplication
* @创建者: 史翔宇
* @创建时间:2015年10月18日 下午12:46:56
*
* @描述: 程序入口类
*/
public class BaseApplication extends Application
{
private static Context mContext;
private static Thread mMainThread;
private static long mMainThreadId;
private static Looper mMainLooper;
private static Handler mMainHander;
//应用程序的入口
@Override
public void onCreate()
{
super.onCreate();
//应用程序的上下文
mContext=getApplicationContext();
//主线程
mMainThread=Thread.currentThread();
//主线程Id
//mMainThreadId=mMainThread.getId();
mMainThreadId=android.os.Process.myTid();
mMainLooper=getMainLooper();
//创建主线程的Handler
mMainHander=new Handler();
initImageLoader();
}
public static Context getContext()
{
return mContext;
}
public static Thread getMainThread()
{
return mMainThread;
}
public static long getMainThreadId()
{
return mMainThreadId;
}
public static Looper getMainThreadLooper()
{
return mMainLooper;
}
public static Handler getMainHander()
{
return mMainHander;
}
private final static void initImageLoader() {
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(mContext)
.defaultDisplayImageOptions(getDefaultDisplayOption())//显示图片的参数,传入自己配置过得DisplayImageOption对象
.memoryCache(new LruMemoryCache(50 * 1024 * 1024)) //缓存策略
.memoryCacheExtraOptions(320, 480) //即保存的每个缓存文件的最大长宽
.threadPoolSize(8) //线程池内线程的数量,默认是3
.threadPriority(Thread.NORM_PRIORITY - 2) //当同一个Uri获取不同大小的图片,缓存到内存时,只缓存一个。默认会缓存多个不同的大小的相同图片
.denyCacheImageMultipleSizesInMemory() //拒绝同一个url缓存多个图片
.diskCacheSize(50 * 1024 * 1024) //设置磁盘缓存大小 50M
.diskCacheFileNameGenerator(new Md5FileNameGenerator()) //将保存的时候的URI名称用MD5 加密
.tasksProcessingOrder(QueueProcessingType.LIFO)//设置图片下载和显示的工作队列排序
.build();
ImageLoader.getInstance().init(config);
}
private final static DisplayImageOptions getDefaultDisplayOption() {
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageForEmptyUri(R.drawable.isloading) // 设置图片Uri为空或是错误的时候显示的图片
.showImageOnFail(R.drawable.isloading) // 设置图片加载或解码过程中发生错误显示的图片
.cacheInMemory(true) // 设置下载的图片是否缓存在内存中
.cacheOnDisk(true) // 设置下载的图片是否缓存在SD卡中
.showImageOnLoading(R.drawable.isloading)
.build();
return options;
}
}
定义完BaseApplication后一定要到清单文件里加上下面这 句话,加到标签<application>里,不然会出错。
android:name="BaseApplication"
剩下的任务就简单了,只需要到MainActivity中引用一下就行了,只需简单四步就能实现轮播图了:
**
* @类名: MainActivity
* @创建者: 史翔宇
* @创建时间:2015年12月18日
* @描述: TODO
*/
public class MainActivity extends Activity {
private HomeAutoSwitchPicHolder mAutoSwitchPicHolder;
private FrameLayout auto_play_pic_container;
private List<String> mData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
auto_play_pic_container = (FrameLayout) findViewById(R.id.auto_play_pic_container);
// 1.创建轮播的holder
mAutoSwitchPicHolder = new HomeAutoSwitchPicHolder(this);
// 2.得到轮播图的视图view
View autoPlayPicView = mAutoSwitchPicHolder.getRootView();
// 把轮播图的视图添加到主界面中
auto_play_pic_container.addView(autoPlayPicView);
//4. 为轮播图设置数据
mAutoSwitchPicHolder.setData(getData());
}
public List<String> getData() {
mData = new ArrayList<String>();
mData.add("http://pic.qqmail.com/imagecache/20101016/1287208885.png");
mData.add("http://v1.qzone.cc/pic/201308/01/16/44/51fa1fd3d9f0d545.jpg!600x600.jpg");
mData.add("http://img.blog.cctv.com/attachments/2009/02/810583_200902231053501.jpg");
return mData;
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<!-- 轮播图的容器 -->
<FrameLayout
android:id="@+id/auto_play_pic_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</FrameLayout>
</LinearLayout>
以上的代码已经很详尽了,读者可以把它整合到你的项目当中,就可以完美实现轮播图的效果了。