当前位置:  编程技术>移动开发

实现轮转广告带底部指示的自定义ViewPager控件

    来源: 互联网  发布时间:2014-10-21

    本文导语:  有许多博客和开源项目都致力于这项工作,但是他们的工作大都是为了制作类似于启动页的效果,ViewPager全屏显示,或者自己可操作的属性难以满足要求,因此我想把ViewPager和底部的指示物封装在一个自定义的View中,作为一个...

有许多博客和开源项目都致力于这项工作,但是他们的工作大都是为了制作类似于启动页的效果,ViewPager全屏显示,或者自己可操作的属性难以满足要求,因此我想把ViewPager和底部的指示物封装在一个自定义的View中,作为一个新的控件在xml中使用,所以自己来实现了一个。
而且,在用自定义视图封装ViewPager时,出现了一个问题,就是ViewPager的所有页不能全部显示的问题,不知道是因为这个问题太简单还是什么其它原因,在网上并没有搜到这个问题的解决方法(事实上连提问的人都没有……),困扰了我半个多星期,终于解决,这一点在正文里会介绍,先来贴一下效果图:

下面来介绍我的实现过程:

首先在res/values/目录下创建attrs.xml文件,用来定义新View自定义的属性:

代码如下:



   
       
       
       
       
       
       
       
       
       
   

其中:

dotsViewHeight定义底部指示物所在视图(我定义为一个LinearLayout)的高度,也就是示例图中圆圈所在灰色透明部分的高度,默认为40像素;

dotsSpacing定义底部指示物之间的间距,默认为0;

dotsFocusImage定义代表当前页的指示物的样子;

dotsBlurImage定义代表非当前页的指示物的样子;

android:scaleType定义ViewPager中ImageView的scale类型,如果ViewPager中的View不是ImageView,则此属性没有效果,默认为ScaleType.FIT_XY;

android:gravity定义底部指示物在父View(即示例灰色透明部分)的gravity属性;

dotsBackground定义底部指示物的背景颜色或背景图;

dotsBgAlpha定义底部指示物的背景颜色或背景图的透明度,取值为0-1,0代表透明;

changeInteval定义ViewPager自动切换的时间间隔,单位为ms,默认为1000ms(这个地方实际的间隔比设置的要大,不知道是什么原因,望高手解答);

下一步,定义PageAdapter,为ViewPager提供内容:

代码如下:

public class ViewPagerAdapter extends PagerAdapter {

    private List views = null;
    private ScaleType scaleType;

    public ViewPagerAdapter(List views) {
        this(views, ScaleType.CENTER);
    }

    public ViewPagerAdapter(List views, ScaleType scaleType) {
        super();
        this.views = views;
        this.scaleType = scaleType;
    }

定义一个views来存储要显示的View,然后定义一个ScaleType来规定如果ViewPager是用来显示ImageView的,ImageView应该怎样呈现在ViewPager当中,如果调用的构造函数不传ScaleType信息,则默认使用ScaleType.CENTER。
根据官方API描述,需要重写PageAdapter的getCount,isViewFromObject,instantiateItem和destroyItem这四个方法,在instantiateItem中设置ScaleType,其它几个方法,都是用官方描述的写法,没有做什么新的改动:

代码如下:

@Override
public int getCount() {
    // TODO Auto-generated method stub
    return views.size();
}

@Override
public boolean isViewFromObject(View arg0, Object arg1) {
    // TODO Auto-generated method stub
    return arg0 == arg1;
}

@Override
public Object instantiateItem(View container, int position) {
    // TODO Auto-generated method stub
    View view = views.get(position);
    ViewPager viewPager = (ViewPager) container;
    if (view instanceof ImageView){
        ((ImageView) view).setScaleType(scaleType);
    }
    viewPager.addView(view, 0);
    return view;
}

@Override
public void destroyItem(View container, int position, Object object) {
    // TODO Auto-generated method stub
    ((ViewPager) container).removeView((View) object);
}

下面就是重头戏了,核心类,被封装的底部带指示物的ViewPager,基本思路是自定义一个类继承LinearLayout,在里面加入两个子视图ViewPager和LinearLayout(放置指示物),并且,因为要定期轮转,还实现了Runnable接口,定义了以下的变量:

代码如下:

public class MyViewPager extends LinearLayout implements Runnable {

    private ViewPager viewPager;
    private LinearLayout viewDots;
    private List dots;
    private List views;

    private int position = 0;
    private boolean isContinue = true;

    private float dotsViewHeight;
    private float dotsSpacing;
    private Drawable dotsFocusImage;
    private Drawable dotsBlurImage;
    private ScaleType scaleType;
    private int gravity;
    private Drawable dotsBackground;
    private float dotsBgAlpha;
    private int changeInterval;

viewPager是要显示的ViewPager对象,viewDots是放置指示物的子视图,dots是viewDots上的指示物项,views是ViewPager项,position指示当前正在显示第几张图,isContinue表示可不可以自动轮转(当手指触摸时不轮转),在下面的就是雨attrs.xml中定义的属性相对应的值。作为一个能够在xml布局文件中直接使用的View,必须重写拥有Context和AttributeSet参数的构造函数:

代码如下:

public MyViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
    // TODO Auto-generated constructor stub
    TypedArray a = context.obtainStyledAttributes(attrs,
            R.styleable.MyViewPager, 0, 0);

try {
        dotsViewHeight = a.getDimension(
                    R.styleable.MyViewPager_dotsViewHeight, 40);
            //这里依次获取所有的属性值,此处省略,可参看最后附上的全部代码
        } finally {
            a.recycle();
        }

    initView();
}

最后调用的函数initView,用来初始化ViewPager和LinearLayout这两个子视图,同时,如果xml中给指示物设置了背景,在这里进行设置:

代码如下:

@SuppressLint("NewApi")
private void initView() {
    // TODO Auto-generated method stub
    viewPager = new ViewPager(getContext());
    viewDots = new LinearLayout(getContext());

    LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT,
            LayoutParams.MATCH_PARENT);
    addView(viewPager, lp);
    if (dotsBackground != null) {
        dotsBackground.setAlpha((int) (dotsBgAlpha * 255));
        viewDots.setBackground(dotsBackground);
    }
    viewDots.setGravity(gravity);
    addView(viewDots, lp);
}


使用这个类时,关键就是创建一个List,并作为参数传进来供ViewPager(PagerAdapter)使用,对外的接口就是这个setViewPagerViews:

代码如下:

public void setViewPagerViews(List views) {
    this.views = views;
    addDots(views.size());

    viewPager.setAdapter(new ViewPagerAdapter(views, scaleType));

    viewPager.setOnPageChangeListener(new OnPageChangeListener() {
        @Override
        public void onPageSelected(int index) {
            // TODO Auto-generated method stub
            position = index;
            switchToDot(index);
        }
        //override的两个空方法,此处省略
    });

    viewPager.setOnTouchListener(new OnTouchListener() {

        @Override
        public boolean onTouch(View view, MotionEvent motionevent) {
            // TODO Auto-generated method stub
            switch (motionevent.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
                isContinue = false;
                break;
            case MotionEvent.ACTION_UP:
                isContinue = true;
                break;
            default:
                isContinue = true;
                break;
            }
            return false;
        }
    });
    new Thread(this).start();
}

addDots就是在底部添加多少个小点,默认第一个处于被选中状态,关键是OnPageChangeListener的onPageSelected方法,这个方法在viewPager进行切换时调用,做的工作就是把底部的指示物切换到对应的标识上,在这个方法的最后,启动了轮转的线程。

代码如下:

@Override
public void run() {
    // TODO Auto-generated method stub
    while (true) {
        if (isContinue) {
            pageHandler.sendEmptyMessage(position);
            position = (position + 1) % views.size();
            try {
                Thread.sleep(changeInterval);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

Handler pageHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        // TODO Auto-generated method stub
        viewPager.setCurrentItem(msg.what);
        super.handleMessage(msg);
    }
};

在这个线程中,每隔固定秒数,就向Handler队列中发送一个消息,内容就是要显示的view项的index,然后再handler中调用viewPager的setCurrentItem方法进行跳转。至此,最核心的类就完成了,但还剩很关键的一个方法,作为一个自定义的View,要重写父类的onLayout方法来对子元素进行布局,就是这一个方法中不当的代码,导致每次只能显示前两张图,因为ViewPager在显示时,会默认初始化当前页和前后页,对于第一张来说,没有前一页,所以初始化了两张,在ViewPager滑动时,每次都会调用onLayout方法,而且,changed参数为false,我已开始只判断changed为true时才进行布局,就造成了上述问题,完整的onLayout代码如下:

代码如下:

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    // TODO Auto-generated method stub
    View child = this.getChildAt(0);
    child.layout(0, 0, getWidth(), getHeight());

    if (changed) {
        child = this.getChildAt(1);
        child.measure(r - l, (int) dotsViewHeight);
        child.layout(0, getHeight() - (int) dotsViewHeight, getWidth(),
                getHeight());
    }
}

最后,就是如何使用这个类了,首先,在activity的布局文件中声明这个组件:

代码如下:

   


然后,在MainActivity中,创建List数组并设置数据:

代码如下:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    initViewPager();
}

private void initViewPager() {
    views = new ArrayList();

    ImageView image = new ImageView(this);
    image.setImageResource(R.drawable.demo_scroll_image);
    views.add(image);
    image = new ImageView(this);
    image.setImageResource(R.drawable.demo_scroll_image2);
    views.add(image);
    image = new ImageView(this);
    image.setImageResource(R.drawable.demo_coupon_image);
    views.add(image);
    image = new ImageView(this);
    image.setImageResource(R.drawable.demo_scroll_image2);
    views.add(image);

    MyViewPager pager = (MyViewPager) findViewById(R.id.my_view_pager);
    pager.setViewPagerViews(views);
}

至此,本示例就全部讲解完了,两个问题,一个就是为什么使用Thread的方法来控制时间间隔,实际值会比设置的值长,是因为Message在排队吗,第二个问题,就是为什么ViewPager滑动时不重新对ViewPager布局,就会不显示任何图,这两个问题还有待大家解答。


    
 
 

您可能感兴趣的文章:

  • 在QLable控件上如何绘图???我想实现在label控件上绘图,大家帮忙~~~
  • C#实现根据指定容器和控件名字获得控件的方法
  • 请问VC生成的控件怎么在JAVA里面调用,就是显示在网页上,里面的方法可以实现?
  • 开发板运行交互式网页,控件包含linux命令,用什么方法实现?
  • 象CSDN这样的树形结构的控件是怎么实现的,有源代码吗
  • java 实现 vb中timer控件的功能?
  • applet 能否实现控件的功能
  • python调用短信猫控件实现发短信功能实例
  • java通过复选框控件数组实现添加多个复选框控件示例分享
  • 实现DataGridView控件中CheckBox列的使用实例
  • 提问:JBuilder4中,JTable控件如何实现定时刷新?
  • 请问怎样实现光标定位于某个控件上?
  • WinForm实现移除控件某个事件的方法
  • 解决C#中取消方向键对控件焦点控制的实现方法
  • c#实现DataGridView控件隔行变色(winform)的代码
  • WinForm自定义函数FindControl实现按名称查找控件
  • WinForm实现按名称递归查找控件的方法
  • C#自定义控件添加右键菜单的实现代码
  • WinForm子窗体访问父窗体控件的实现方法
  • 求救:如何制作象VB工具栏一样,实现按钮或其他控件的动态拖放添加到当前容器,而且还能拖动和改变大小!
  •  
    本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
    本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • destoon实现底部添加你是第几位访问者的方法
  • jquery实现的固定底部的菜单代码
  • jquery 定时滑出可最小化的底部提示层的实现代码
  • Android顶部工具栏和底部工具栏的简单实现代码
  • Android仿UC底部菜单栏实现原理与代码
  • android底部菜单栏实现原理与代码
  • 通过javascript实现DIV居中,兼容各浏览器版本
  • socket实现多文件并发传输,求助多线程实现问题?
  • Python GUI编程:tkinter实现一个窗口并居中代码
  • interface 到底有什么用???实现接口,怎么实现??
  • 通过javascript库JQuery实现页面跳转功能代码
  • 怎么用Jsp实现在页面实现树型结构?
  • sharepoint 2010 使用STSNavigate函数实现文件下载举例
  • windows 下的PortTunnel 在linux下怎么实现?或者相应的已经实现的软件?端口映射
  • php实现socket实现客户端和服务端数据通信源代码
  • 网站重定向用C语言实现iptables,ACL实现
  • flash AS3反射实现(describeType和getDefinitionByName)
  • 在linux下如何编程实现nslookup命令实现的IP地址和域名互相转换的功能?
  • boost unordered_map和std::list相结合的实现LRU算法
  • 求在freebsd+Squid下实现pc上网的透明代理的实现方法!给出具体配置方法的高分谢!
  • c#通过委托delegate与Dictionary实现action选择器代码举例
  • linux下如实现与window下的驱动器实现文件共享??
  • 技术文章 iis7站长之家
  • qt如何实现:操作键盘实现数据的滚动?
  • iphone cocos2d 精灵的动画效果(图片,纹理,帧)CCAnimation实现
  • 我想用APPLET实现读取客户端的图片文件,该如何实现?
  • c语言判断某一年是否为闰年的各种实现程序代码
  • PING是用TCP,还是用UDP来实现的?或是采用其它协议实现的?


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3