CycleRotationView:自定义控件,主要功能是实现类似与各种商城首页的广告轮播图。其实像这种比较常见的自定义控件早就满大街了,虽然说“不要重复发明轮子”,但是不代表不用关心轮子是怎么造的,本着“知其然知其所以然”的态度,完成了这个控件(总不能需要用到时才来学习吧)。老规矩,先来个效果图(没有效果图的文章都是耍流氓):

对于轮播图的图片切换,大家立马会想到 ViewPager 这个控件,因为它已经拥有左右滑动的功能,用来实现轮播图效果会简单一些,本篇也是基于 ViewPager 这个控件的实现,将其分成下面四个部分:
1、图片轮播
2、指示器
3、图片指示器联动
4、定时轮播
下面开始实现(有点啰嗦,勿喷…):

图片轮播

布局文件

  1. <RelativeLayout
  2. android:layout_width="match_parent"
  3. android:layout_height="120dp">
  4. <android.support.v4.view.ViewPager
  5. android:id="@+id/viewPager"
  6. android:layout_width="match_parent"
  7. android:layout_height="120dp" />
  8. <LinearLayout
  9. android:layout_width="match_parent"
  10. android:layout_height="18dp"
  11. android:layout_alignParentBottom="true"
  12. android:background="#66000000"
  13. android:orientation="horizontal">
  14. <TextView
  15. android:id="@+id/title"
  16. android:layout_width="0dp"
  17. android:layout_height="wrap_content"
  18. android:layout_marginLeft="15dp"
  19. android:layout_weight="1"
  20. android:textColor="#FFFFFF" />
  21. <LinearLayout
  22. android:id="@+id/pointGroup"
  23. android:layout_width="wrap_content"
  24. android:layout_height="wrap_content"
  25. android:layout_gravity="center"
  26. android:layout_marginRight="15dp"
  27. android:orientation="horizontal" />
  28. </LinearLayout>
  29. </RelativeLayout>

先来看看布局文件,最外层的 RelativeLayout 代表整个控件,包含 ViewPager 和一个 LinearLayout;在 LinearLayout 中又包含 TextView 和 LinearLayout。这里说明一下:第一个 LinearLayout 包含标题(id=title)和指示器(id=pointGroup),第二个 LinearLayout 用来包裹指示器(在代码里完成),给张理解图(这里我并没有设置标题):

代码实现

  1. /**
  2. * 初始化布局
  3. * @param mContext
  4. */
  5. private void initView(Context mContext) {
  6. View view = LayoutInflater.from(mContext)
  7. .inflate(R.layout.cycle_rotation_layout, this, true);
  8. mViewPager = (ViewPager) view.findViewById(R.id.viewPager);
  9. mPointGroup = (LinearLayout)view.findViewById(R.id.pointGroup);
  10. }

首先是加载刚才的布局文件,把 ViewPager 和 pointGroup 找出来;

  1. for (int i = 0; i < images.length; i++) {
  2. // 创建 ImageView,并设置图片
  3. ImageView img = new ImageView(mContext);
  4. // 把图片扩大到 ImageView 的大小
  5. img.setScaleType(ImageView.ScaleType.FIT_XY);
  6. img.setImageResource(images[i]);
  7. mList.add(img);
  8. }

根据用户传递过来的图片的数量进行遍历创建对应数量的 ImageView ,然后给创建的 ImageView 设置图片,再用一个 List 集合把 ImageView 装起来待用。在 Fragement + ViewPager + TabLayout 的场景里 ViewPager 里填充的是 Fragement,而这里的 ViewPager 填充的是 ImageView;

  1. /**
  2. * 适配器
  3. */
  4. class CycleAdapter extends PagerAdapter {
  5. @Override
  6. public int getCount() {
  7. return Integer.MAX_VALUE;
  8. }
  9. @Override
  10. public Object instantiateItem(ViewGroup container, int position) {
  11. position = position % mList.size();
  12. // 判断该 item 是否已经有 parent
  13. final View child = mList.get(position);
  14. if (child.getParent() != null) {
  15. container.removeView(child);
  16. }
  17. container.addView(mList.get(position));
  18. return mList.get(position);
  19. }
  20. @Override
  21. public void destroyItem(ViewGroup container, int position, Object object) {}
  22. @Override
  23. public boolean isViewFromObject(View view, Object object) {
  24. return view == object;
  25. }
  26. }

适配器上场了,在 getCount() 方法里返回 Integer.MAX_VALUE 是因为 ViewPager 不会循环滑动,给这么一个数值(据说 Integer.MAX_VALUE 的值是 10亿,我没试过)可以让它一直滑下去。问题来了:这么大的数值,会不会出现性能问题?这个我也好奇,看谁更有兴趣咯。
还要说一下 instantiateItem() 方法,为什么要判断 parent 的状态?原因是如果不这样处理,在向左滑动 ViewPager 的时候会报错: The specified child already has a parent. You must call removeView 。
注意:instantiateItem() 进行了判断,destroyItem() 就不用再 removeView() 了,否则会出现空白页。

指示器

  1. /**
  2. * 创建指示器
  3. */
  4. for(int i = 0; i < images.length; i ++) {
  5. // 创建指示器,实质也是 ImageView
  6. ImageView point = new ImageView(mContext);
  7. point.setImageResource(R.drawable.shape_point_selector);
  8. // 指示器布局参数
  9. LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(pointSize, pointSize);
  10. // 第2个起才设置左边距
  11. if (i > 0) {
  12. params.leftMargin = pointMargin;
  13. point.setSelected(false); // 默认选中第1个
  14. } else {
  15. point.setSelected(true); // 默认选中第1个
  16. }
  17. point.setLayoutParams(params); // 给指示器设置参数
  18. mPointGroup.addView(point); // 把指示器添加到容器
  19. }

创建指示器的时候为了和 ImageView 的数量对应,所以同样要遍历用户设置的图片数量。下面是指示器的相关布局文件:
shape_point_selector:

  1. <selector xmlns:android="http://schemas.android.com/apk/res/android">
  2. <item android:state_selected="true" android:drawable="@drawable/shape_point_selected" />
  3. <item android:state_selected="false" android:drawable="@drawable/shape_point_normal" />
  4. </selector>

shape_point_selected:

  1. <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
  2. <solid android:color="#FFFFFF"/>
  3. </shape>

shape_point_normal:

  1. <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
  2. <solid android:color="#66000000"/>
  3. </shape>

ViewPager 和 pointGroup 都创建好了,接下来是他们俩的联动:

联动

  1. mViewPager.setAdapter(new CycleAdapter());
  2. // ViewPager 的监听事件
  3. mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
  4. @Override
  5. public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
  6. int lastPosition;
  7. @Override
  8. public void onPageSelected(int position) {
  9. position = position % mList.size();
  10. // 设置当前圆点选中
  11. mPointGroup.getChildAt(position).setSelected(true);
  12. // 设置前一个圆点不选中
  13. mPointGroup.getChildAt(lastPosition).setSelected(false);
  14. lastPosition = position;
  15. }
  16. @Override
  17. public void onPageScrollStateChanged(int state) {}
  18. });

给 ViewPager 添加监听事件,在选中某个页面的同时设置相应位置指示器也被选中。position % mList.size():Adapter 的 getCount() 中返回了很大的值,这里求余可以保证不会出现空指针异常。

定时轮播

  1. mHandler.postDelayed(new Runnable() {
  2. @Override
  3. public void run() {
  4. // 当前选中的 item
  5. int currentItem = mViewPager.getCurrentItem();
  6. // 判断是否是最后一个 item
  7. if (currentItem == mViewPager.getAdapter().getCount() - 1) {
  8. mViewPager.setCurrentItem(1);
  9. } else {
  10. mViewPager.setCurrentItem(currentItem + 1);
  11. }
  12. // 不断给自己发消息
  13. mHandler.postDelayed(this, 3000);
  14. }
  15. }, 3000);

定时轮播也很简单,用 Handler 来延时给自己发消息。在 ViewPager 滑动到最后一个时给它设置 mViewPager.setCurrentItem(1) 来实现循环轮播。
基本的轮播功能到这里就完成了,但是还有很多的拓展功能,比如点击事件,用户设置图片链接,这些我都已经实现了,这里就不一一介绍了,有兴趣的可以看看源码:cycleRotation

CycleRotationView:自定义控件之轮播图的更多相关文章

  1. Android自定义控件之轮播图控件

    背景 最近要做一个轮播图的效果,网上看了几篇文章,基本上都能找到实现,效果还挺不错,但是在写的时候感觉每次都要单独去重新在Activity里写一堆代码.于是自己封装了一下.本篇轮播图实现原理原文出处: ...

  2. Android 轮播图Banner切换图片的效果

    Android XBanner使用详解 2018年03月14日 08:19:59 AND_Devil 阅读数:910   版权声明:本文为博主原创文章,未经博主允许不得转载. https://www. ...

  3. js 基础篇(点击事件轮播图的实现)

    轮播图在以后的应用中还是比较常见的,不需要多少行代码就能实现.但是在只掌握了js基础知识的情况下,怎么来用较少的而且逻辑又简单的方法来实现呢?下面来分析下几种不同的做法: 1.利用位移的方法来实现 首 ...

  4. 实现下来ScrollView放大轮播图

    创建工程,创建一个UIScrollView属性,并遵循其协议: #define kWidth self.view.frame.size.width//屏幕宽 #define kHeight self. ...

  5. ViewPager轮播图

    LoopViewPagerLayout无限轮播 项目地址:https://github.com/why168/LoopViewPagerLayout 支持三种动画: 支持修改轮播的速度: 支持修改滑动 ...

  6. CSS-用伪类制作小箭头(轮播图的左右切换btn)

    先上学习地址:http://www.htmleaf.com/Demo/201610234136.html 作者对轮播图左右按钮的处理方法一改往常,不是简单地用btn.prev+btn.next的图片代 ...

  7. phpcms首页实现轮播图

    1.在你想要加轮播图的位置加入以下 <div id="flowDiagram" > <div id="button"> <span ...

  8. React视角下的轮播图

    天猫购物网站最显眼的就是轮播图了.我在学习一样新js库,一个新框架或新的编程思想的时候,总是感叹"入门必做选项卡,进阶须撸轮播图."作为一个React组件,它是状态操控行为的典型, ...

  9. Jquery 轮播图简易框架

    =====================基本结构===================== <div class="carousel" style="width: ...

随机推荐

  1. Atitit.复合文档的格式 标准化格式

    Atitit.复合文档的格式 标准化格式 1. Docfile1 2. Iso   Cdf  cd file1 3. Zip1 4. Ooxml1 5. Odf  :OpenDocument Form ...

  2. Android Handler机制(三)----Looper源码解析

    一.Looper Looper对象,顾名思义,直译过来就是循环的意思,从MessageQueue中不断取出message. Class used to run a message loop for a ...

  3. 转 用C API 操作MySQL数据库

    用C API 操作MySQL数据库 参考MYSQL的帮助文档整理 这里归纳了C API可使用的函数,并在下一节详细介绍了它们.请参见25.2.3节,“C API函数描述”. 函数 描述 mysql_a ...

  4. JQuery隐藏显示详情功能

    放置两个DIV:初始DIV :在Repetr绑定设置文字隐藏(三元运算符):'<%# Eval("字段2").ToString().Length>11?Eval(&qu ...

  5. Android APK的安装

    打开packages\apps\PackageInstaller下的清单文件 <?xml version="1.0" encoding="utf-8"?& ...

  6. 【代码笔记】iOS-屏幕旋转

    代码: - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. se ...

  7. C 运算符优先级列表

  8. google不能访问的根本解决方法

    最近中国的防火墙又调皮了.直接出现404了.以前通过IP访问的方式也失效了.难道这种问题能难倒我们这些做技术的IT工程师么?! 经过我的查询,我发现还是有大神在研究这块的.大神针对开发经常访问的网站做 ...

  9. 源码编译安装screen

    OS:Amazon Linux AMI 2015.09.2 (HVM) #sudo su #wget http://ftp.gnu.org/gnu/screen/screen-4.3.1.tar.gz ...

  10. WPF学习之路(九)导航和页面(续)

    生命周期 如果Page1成功导航到Page2,首先会触发NavigationService的Navigating事件,标识导航开始.随后创建Page2对象,并且触发NavigationProgress ...