android无限自动轮播图控件

            开发中经常要做轮播图,每次都去写的话确实很烦,网上也有类似这样的控件,但感觉都不太如意,要么繁琐要么简单到不能达到想要的效果,也很不方便,于是自己就把项目中写的轮播图抽了出来,基于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的工具类,上代码:

**
 * @类名: 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);
    }
}


          初始化ImageLoader放在了BaseApplication中,这就是程序的入口,在里面可以做一些常量的初始化,看看代码:
/**
 * @类名:        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;
	}
}


至于MainActivity的布局文件activity_main.xml,也是相当简单:

<?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>


以上的代码已经很详尽了,读者可以把它整合到你的项目当中,就可以完美实现轮播图的效果了。





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值