当前位置: 代码迷 >> Android >> Android-带位置提醒的轮播控件
  详细解决方案

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

热度:95   发布时间:2016-04-28 00:57:56.0
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()));










  相关解决方案