Android--带位置提示的轮播控件

github地址:https://github.com/zerohuan/SlideLayout/tree/master

实际效果图:

该自定义控件继承FrameLayout, 包含一个ViewPager和横向排列的LinearLayout。后者用于包含显示表示轮播位置的点集,使用ViewPager的好处在于可以灵活的定义item的内容,而不仅仅是图片。</p><p></p><p>为了便于使用,通过自定义属性的方式定义了所须的运行参数:</p><p><pre name="code" class="html"><declare-styleable name="SlideLayout">
        <attr name="viewpagerId" format="reference"/>
        <attr name="dotsId" format="reference"/>
        <attr name="autoPlay" format="boolean" />
        <attr name="slide_interval" format="integer" />
        <attr name="dotRadius" format="dimension" />
        <attr name="onDotColor" format="color" />
        <attr name="offDotColor" format="color" />
        <attr name="strokeColor" format="color" />
    </declare-styleable>

对应的数据成员如下:

    //轮播容器viewPager
    private ViewPager viewPager;
    //标示位置的点集
    private LinearLayout dots;
    //viewPager的资源ID
    private int viewPagerId;
    //包含点的LinearLayout的资源ID
    private int dotsId;
    //该手机的px-dp比例倍数
    private float scale;
    //ViewPager的Adapter
    private PagerAdapter adapter;
    //是否自动播放
    private boolean isAutoPlay;
    //播放的间隔
    private int interval;
    //当前页位置
    private int currentItem;
    //圆点半径
    private float dotRadius;
    //是否正在轮播运行
    private boolean isRunning;
    //位于当前页,点标志的颜色
    private int onDotColor;
    //未位于当前页,点标志的颜色
    private int offDotColor;
    //点边框颜色
    private int strokeColor;

    private final static int SCROLL_WHAT = 0x7549;

使用时,通过Xml文件来定义:

<com.luckymore.ydd.app.view.selfView.SlideLayout
                xmlns:attrs="http://schemas.android.com/apk/res-auto"
                android:id="@+id/news_slide_bar"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                swipe:dotsId="@+id/test_1"
                attrs:viewpagerId="@+id/test_2"
                attrs:autoPlay="true"
                attrs:dotRadius="3dp"
                attrs:slide_interval="4000"
                attrs:offDotColor="@color/alpha_black"
                attrs:onDotColor="@color/alpha_white"
                attrs:strokeColor="#77626262"
                >

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

                <LinearLayout android:id="@+id/test_1"
                    android:layout_width="wrap_content"
                    android:layout_height="20dp"
                    android:orientation="horizontal"
                    android:layout_gravity="bottom|center_horizontal"
                    android:gravity="center"
                    android:paddingLeft="8dp"
                    android:paddingRight="8dp"
                    />

            </com.luckymore.ydd.app.view.selfView.SlideLayout>

获取自定义参数值:

/**
     * 获取自定义参数,在构造器中调用
     * @param context
     * @param attrs
     */
    private void init(Context context, AttributeSet...attrs) {
        if(attrs.length > 0) {
            TypedArray typedArray = context.obtainStyledAttributes(attrs[0], R.styleable.SlideLayout);
            viewPagerId = typedArray.getResourceId(R.styleable.SlideLayout_viewpagerId, -1);
            dotsId = typedArray.getResourceId(R.styleable.SlideLayout_dotsId, -1);
            scale = getResources().getDisplayMetrics().density;
            isAutoPlay = typedArray.getBoolean(R.styleable.SlideLayout_autoPlay, false);
            interval = typedArray.getInteger(R.styleable.SlideLayout_slide_interval, 4000);
            dotRadius = typedArray.getDimension(R.styleable.SlideLayout_dotRadius, 2f * scale);
            onDotColor = typedArray.getColor(R.styleable.SlideLayout_onDotColor, 0x77FFFFFF);
            offDotColor = typedArray.getColor(R.styleable.SlideLayout_offDotColor, 0x77000000);
            strokeColor = typedArray.getColor(R.styleable.SlideLayout_strokeColor, 0x77626262);
        }
    }

ViewPager的ID和LinearLayout的ID通过自定义属性的方式传入,注入到SlideLayout中,在onFinishInflate中实现:

@Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if(viewPagerId != -1 && dotsId != -1) {
            viewPager = (ViewPager)findViewById(viewPagerId);
            dots = (LinearLayout)findViewById(dotsId);

            viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
                @Override
                public void onPageScrolled(int i, float v, int i2) {

                }

                @Override
                public void onPageSelected(int i) {
                    currentItem = i;
                    for(int j = 0; j < viewPager.getAdapter().getCount(); j++) {
                        DotView dot = (DotView)dots.getChildAt(j);
                        if(j == i)
                            dot.setOn(true);
                        else
                            dot.setOn(false);
                        dot.invalidate();
                    }
                }

                @Override
                public void onPageScrollStateChanged(int i) {
                    switch (i) {
                        case 1:// 手势滑动,空闲中
                            isAutoPlay = false;
                            break;
                        case 2:// 界面切换中
                            isAutoPlay = true;
                            break;
                        case 0:// 滑动结束,即切换完毕或者加载完毕
                            // 当前为最后一张,此时从右向左滑,则切换到第一张
                            if (viewPager.getCurrentItem() == viewPager.getAdapter().getCount() - 1 && !isAutoPlay) {
                                viewPager.setCurrentItem(0);
                            }
                            // 当前为第一张,此时从左向右滑,则切换到最后一张
                            else if (viewPager.getCurrentItem() == 0 && !isAutoPlay) {
                                viewPager.setCurrentItem(viewPager.getAdapter().getCount() - 1);
                        }
                            break;
                    }
                }
            });
        }
    }

接着实现,LinearLayout中的“点”的填充,首先定义内部类DotView:

/**
     * 点状UI, 正方形View, 包含一个圆形点
     */
    public class DotView extends View {
        private Paint mPaint = new Paint();
        //两种状态, 是否是当前页
        private boolean isOn;
        //正方形边长
        private int mSize;

        public DotView(Context context) {
            super(context);
        }

        public DotView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }

        public DotView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }

        /**
         * 比onDraw先执行
         * @param widthMeasureSpec
         * @param heightMeasureSpec
         */
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            mSize = (int)Math.ceil(dotRadius) * 3;
            setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
        }

        private int measureWidth(int measureSpec) {
            int result = 0;
            int specMode = MeasureSpec.getMode(measureSpec);
            int specSize = MeasureSpec.getSize(measureSpec);

            if (specMode == MeasureSpec.EXACTLY) {
                // We were told how big to be
                result = specSize;
            } else {
                // Measure the text
                result = mSize + getPaddingLeft() + getPaddingRight();
                if (specMode == MeasureSpec.AT_MOST) {
                    // Respect AT_MOST value if that was what is called for by
                    // measureSpec
                    result = Math.min(result, specSize);// 60,480
                }
            }

            return result;
        }

        private int measureHeight(int measureSpec) {
            int result = 0;
            int specMode = MeasureSpec.getMode(measureSpec);
            int specSize = MeasureSpec.getSize(measureSpec);

            if (specMode == MeasureSpec.EXACTLY) {
                // We were told how big to be
                result = specSize;
            } else {
                // Measure the text (beware: ascent is a negative number)
                result = mSize + getPaddingTop() + getPaddingBottom();
                if (specMode == MeasureSpec.AT_MOST) {
                    // Respect AT_MOST value if that was what is called for by
                    // measureSpec
                    result = Math.min(result, specSize);
                }
            }
            return result;
        }

        //绘制点
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            mPaint.setAntiAlias(true);
            if(isOn()) {
                mPaint.setColor(onDotColor);
            } else {
                mPaint.setColor(offDotColor);
            }
            mPaint.setStyle(Paint.Style.FILL);
            canvas.drawCircle(dotRadius, dotRadius, dotRadius, mPaint);

            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setStrokeWidth(0.7f * scale);
            mPaint.setColor(strokeColor);
            canvas.drawCircle(dotRadius, dotRadius, dotRadius, mPaint);
        }

        public boolean isOn() {
            return isOn;
        }

        public void setOn(boolean isOn) {
            this.isOn = isOn;
        }
    }

DotView包含是否是当前页两种状态,填充颜色和边框颜色都从自定义属性中获得。

在SlideLayout的成员函数resetDots填充DotView:

/**
     * 重新生成点集UI
     */
    public void resetDots() {
        int dotCount = viewPager.getAdapter().getCount();
        int oldDotCount = dots.getChildCount();
        for(int i = 0; i < dotCount; i ++) {
            DotView dot;
            if(i >= oldDotCount) {
                dot = new DotView(getContext());
                dots.addView(dot);
            } else {
                dot = (DotView)dots.getChildAt(i);
            }
            if(i == viewPager.getCurrentItem()) {
                dot.setOn(true);
            } else {
                dot.setOn(false);
            }
        }
    }

当LinearLayout点集的dotView数量小于ViewPager中Pager数量时,新建DotView,填充至LinearLayout;

在UI线程中使用SlideLayout主要调用如下两个方法:

/**
     * 在UI线程中调用,注入ViewPager的adapter
     * @param adapter
     */
    public void setViewPagerAdapter(PagerAdapter adapter) {
        this.adapter = adapter;
        viewPager.setAdapter(adapter);
        updateUI();
    }

    /**
     * 在UI线程中调用,修改adapter中数据后, 更新UI
     */
    public synchronized void updateUI() {
        adapter.notifyDataSetChanged();
        resetDots();
        if(isAutoPlay)
            startAutoScroll();
    }

最后,实现定时轮播,通过Handler&Message消息队列机制来实现轮播, 利用sendEmptyMessageDelayed来实现轮播间隔:

private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            switch (msg.what) {
                case SCROLL_WHAT:
                    synchronized(SlideLayout.this) {
                        viewPager.setCurrentItem(currentItem);
                        sendScrollMessage(interval);
                    }
                    break;
            }
        }
    };

    /**
     * 开始自动轮播
     */
    public void startAutoScroll() {
        if(!isRunning) {
            isRunning = true;
            sendScrollMessage(interval);
        }
    }

    /**
     * 滚动到下一页
     * @param delayTimeInMills
     */
    private synchronized void sendScrollMessage(long delayTimeInMills) {
        /** remove messages before, keeps one message is running at most **/
        handler.removeMessages(SCROLL_WHAT);
        currentItem = (currentItem + 1) %  adapter.getCount();
        //通过该方法实现定时轮播
        handler.sendEmptyMessageDelayed(SCROLL_WHAT, delayTimeInMills);
    }

最后,在UI线程中调用一句代码,可以使用轮播控件:

newsBar.setViewPagerAdapter(new NewsBarAdapter(ret.getNews(), getMactivity()));
时间: 2024-10-13 12:16:25

Android--带位置提示的轮播控件的相关文章

Android之仿京东淘宝的自动无限轮播控件

在App的开发中,很多的时候都需要实现类似京东淘宝一样的自动无限轮播的广告栏,所以就自己写了一个,下面是我自定义控件的思路和过程. 一.自定义控件属性 新建自定义控件SliderLayout继承于RelativeLayout,首先要考虑的就是自定义的控件需要扩展那些属性,把这些属性列出来.在这里是要实现类似于京东淘宝的无限轮播广告栏,那马首先想到的就是轮播的时长.轮播指示器的样式等等.我在这里列举了一些并且结合到了代码中. 1.扩展属性 (1)是否开启自动轮播的功能. (2)指示器的图形样式,一

Qt编写自定义控件24-图片轮播控件

一.前言 上一篇文章写的广告轮播控件,采用的传统widget堆积设置样式表做的,这次必须要用到更高级的QPainter来绘制了,这个才是最高效的办法,本控件参考雨田哥的轮播控件,经过大规模的改造而成,相比于原来的广告轮播控件,本控件可以说完爆他,按在地上使劲摩擦.除了可以设置图片路径集合以外,还可以设置对应的提示信息,这个在众多的web轮播图片效果中最常见,比如新闻的标题等,可以更直观的显示当前图片,而且单击图片还可以支持跳转,指示器的位置也能设置左边+中间+右边,指示器的样式更加增加到椭圆条状

Qt编写自定义控件23-广告轮播控件

一.前言 广告轮播这个控件做的比较早,是很早以前定制一个电信客户端时候用到的,该客户端需要在首页展示轮播预先设定好的图片,图片的路径可以自由设定,然后轮播的间隔速度可以自由控制,同时该控件还需要提供两种指示器的风格,一种是迷你型的样式,一种是数字型的样式. 本控件很早就做好了,由于当时的QPainter功力不足,还不是很熟悉QPainter,采用的是效率比较低的直接用现有控件堆积而成,比如指示器采用的QLabel,用样式表来控制对应的形状,指示器所在的底部放一个widget,采用左右布局,然后右

走马灯图片轮播控件----------WinForm控件开发系列

/// <summary> /// 走马灯图片轮播控件 /// </summary> [ToolboxItem(true)] [DefaultProperty("Images")] [Description("走马灯图片轮播控件")] public partial class ImageCarouselExt : Control { #region private bool barShow = true; /// <summary>

adminLTE 教程 -4 轮播控件

轮播可以放在首页用来展示需要显示的内容,其实内容没有什么可以讲解的,就是在box下面放了carousel控件. 在adminLTE演示页面搜索Carousel <div class="box box-solid"> <div class="box-header with-border"> <h3 class="box-title">Carousel</h3> </div> <!--

图片轮播控件----------WinForm控件开发系列

   public partial class ImageCarouselDevelopExt : Control { #region /// <summary> /// 动画播放定时器 /// </summary> private Timer carouselTimer = new Timer(); /// <summary> /// 轮播的五个PictureBox /// </summary> private List<PictureBox>

WPF自定义轮播控件

 闲得蛋疼做了一个WPF制作轮播动画,勉强可以看,写个随笔留个脚印.  源码:有需要的可留言.  效果图:

swiper轮播控件配置项

var mySwiper = new Swiper ('.swiper-container', { direction: 'horizontal', loop: true, autoplay: 5000, autoplayDisableOnInteraction : false,  //控制使用分页器后,还可以自动轮播 // 如果需要分页器 pagination: '.swiper-pagination', paginationClickable :true,                 /

Android学习笔记之图片轮播...

PS:一个bug又折腾了一个下午....哎... 学习内容: 1.Android利用ViewPager和PagerAdapter实现图片轮播... 2.使用反射机制获取Android的资源信息...     图片轮播是非常常见的一种动画效果,在app中也是很常用的一个效果,这里就简单的来实现一下这个功能,Android中想要实现图片轮播,需要使用到ViewPager这个控件来实现,这个控件的主要功能是实现图片的滑动效果...那么有了滑动,在滑动的基础上附上图片也就实现了图片轮播的效果...这个控