Android开发之app基础---自动轮播图

来源:互联网 发布:淘宝客在哪里设置 编辑:程序博客网 时间:2024/04/28 17:09

前面已经介绍了app基本框架,框架搭好之后自然要进入到页面中,最常用的自然是我们首页的轮播图了,下面就来分享下我看到的比较好的一个banner轮播图的代码:

  1. 三个自定义view FlowIndictor CircleFlowIndictor,ViewFlow,封装了banner图所需的逻辑
  2. FlowView的设置轮播图片
  3. 创建BannerAdapter 适配器加载图片
    这里写图片描述
    ok说到这就基本上完了,是不是觉得很简单呢,他不需要自己再写一个viewpager,而且避免了一个问题,就是当图片跳到最后一张的时候不会很突兀的跳到第一张,我想刚开始很多人遇到过这个问题吧,废话讲了这么多,上代码吧!
    CircleFlowIndicator
package com.example.bannerdemo;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Paint;import android.graphics.Paint.Style;import android.os.AsyncTask;import android.util.AttributeSet;import android.view.View;import android.view.animation.Animation;import android.view.animation.Animation.AnimationListener;import android.view.animation.AnimationUtils;/** * A FlowIndicator which draws circles (one for each view).  * <br/> * Availables attributes are:<br/> * <ul> * activeColor: Define the color used to draw the active circle (default to white) * </ul> * <ul> * inactiveColor: Define the color used to draw the inactive circles (default to 0x44FFFFFF) * </ul> * <ul> * inactiveType: Define how to draw the inactive circles, either stroke or fill (default to stroke) * </ul> * <ul> * activeType: Define how to draw the active circle, either stroke or fill (default to fill) * </ul> * <ul> * fadeOut: Define the time (in ms) until the indicator will fade out (default to 0 = never fade out) * </ul> * <ul> * radius: Define the circle radius (default to 4.0) * </ul> */public class CircleFlowIndicator extends View implements FlowIndicator,        AnimationListener {    private static final int STYLE_STROKE = 0;    private static final int STYLE_FILL = 1;    private float radius = 4;    private float circleSeparation = 2*radius+radius;    private float activeRadius = 0.5f;    private int fadeOutTime = 0;    private final Paint mPaintInactive = new Paint(Paint.ANTI_ALIAS_FLAG);    private final Paint mPaintActive = new Paint(Paint.ANTI_ALIAS_FLAG);    private ViewFlow viewFlow;    private int currentScroll = 0;    private int flowWidth = 0;    private FadeTimer timer;    public AnimationListener animationListener = this;    private Animation animation;    private boolean mCentered = false;    /**     * Default constructor     *      * @param context     */    public CircleFlowIndicator(Context context) {        super(context);        initColors(0xFFFFFFFF, 0xFFFFFFFF, STYLE_FILL, STYLE_STROKE);    }    /**     * The contructor used with an inflater     *      * @param context     * @param attrs     */    public CircleFlowIndicator(Context context, AttributeSet attrs) {        super(context, attrs);        // Retrieve styles attributs        TypedArray a = context.obtainStyledAttributes(attrs,                R.styleable.CircleFlowIndicator);        // Gets the inactive circle type, defaulting to "fill"        int activeType = a.getInt(R.styleable.CircleFlowIndicator_activeType,                STYLE_FILL);        int activeDefaultColor = 0xFFFFFFFF;        // Get a custom inactive color if there is one        int activeColor = a                .getColor(R.styleable.CircleFlowIndicator_activeColor,                        activeDefaultColor);        // Gets the inactive circle type, defaulting to "stroke"        int inactiveType = a.getInt(                R.styleable.CircleFlowIndicator_inactiveType, STYLE_STROKE);        int inactiveDefaultColor = 0x44FFFFFF;        // Get a custom inactive color if there is one        int inactiveColor = a.getColor(                R.styleable.CircleFlowIndicator_inactiveColor,                inactiveDefaultColor);        // Retrieve the radius        radius = a.getDimension(R.styleable.CircleFlowIndicator_radius, 4.0f);        circleSeparation = a.getDimension(R.styleable.CircleFlowIndicator_circleSeparation, 2*radius+radius);        activeRadius = a.getDimension(R.styleable.CircleFlowIndicator_activeRadius, 0.5f);        // Retrieve the fade out time        fadeOutTime = a.getInt(R.styleable.CircleFlowIndicator_fadeOut, 0);        mCentered = a.getBoolean(R.styleable.CircleFlowIndicator_centered, false);        initColors(activeColor, inactiveColor, activeType, inactiveType);    }    private void initColors(int activeColor, int inactiveColor, int activeType,            int inactiveType) {        // Select the paint type given the type attr        switch (inactiveType) {        case STYLE_FILL:            mPaintInactive.setStyle(Style.FILL);            break;        default:            mPaintInactive.setStyle(Style.STROKE);        }        mPaintInactive.setColor(inactiveColor);        // Select the paint type given the type attr        switch (activeType) {        case STYLE_STROKE:            mPaintActive.setStyle(Style.STROKE);            break;        default:            mPaintActive.setStyle(Style.FILL);        }        mPaintActive.setColor(activeColor);    }    /*     * (non-Javadoc)     *      * @see android.view.View#onDraw(android.graphics.Canvas)     */    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        int count = 3;        if (viewFlow != null) {            count = viewFlow.getViewsCount();        }        //this is the amount the first circle should be offset to make the entire thing centered        float centeringOffset = 0;        int leftPadding = getPaddingLeft();        // Draw stroked circles        for (int iLoop = 0; iLoop < count; iLoop++) {            canvas.drawCircle(leftPadding + radius                    + (iLoop * circleSeparation) + centeringOffset,                    getPaddingTop() + radius, radius, mPaintInactive);        }        float cx = 0;        if (flowWidth != 0) {            // Draw the filled circle according to the current scroll            cx = (currentScroll * circleSeparation) / flowWidth;        }        // The flow width has been upadated yet. Draw the default position        canvas.drawCircle(leftPadding + radius + cx+centeringOffset, getPaddingTop()                + radius, radius + activeRadius, mPaintActive);    }    /*     * (non-Javadoc)     *      * @see     * org.taptwo.android.widget.ViewFlow.ViewSwitchListener#onSwitched(android     * .view.View, int)     */    @Override    public void onSwitched(View view, int position) {    }    /*     * (non-Javadoc)     *      * @see     * org.taptwo.android.widget.FlowIndicator#setViewFlow(org.taptwo.android     * .widget.ViewFlow)     */    @Override    public void setViewFlow(ViewFlow view) {        resetTimer();        viewFlow = view;        flowWidth = viewFlow.getWidth();        invalidate();    }    /*     * (non-Javadoc)     *      * @see org.taptwo.android.widget.FlowIndicator#onScrolled(int, int, int,     * int)     */    @Override    public void onScrolled(int h, int v, int oldh, int oldv) {        setVisibility(View.VISIBLE);        resetTimer();        flowWidth = viewFlow.getWidth();        if(viewFlow.getViewsCount()*flowWidth!=0){            currentScroll = h%(viewFlow.getViewsCount()*flowWidth);        }else {            currentScroll = h;        }        invalidate();    }    /*     * (non-Javadoc)     *      * @see android.view.View#onMeasure(int, int)     */    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        setMeasuredDimension(measureWidth(widthMeasureSpec),                measureHeight(heightMeasureSpec));    }    /**     * Determines the width of this view     *      * @param measureSpec     *            A measureSpec packed into an int     * @return The width of the view, honoring constraints from measureSpec     */    private int measureWidth(int measureSpec) {        int result = 0;        int specMode = MeasureSpec.getMode(measureSpec);        int specSize = MeasureSpec.getSize(measureSpec);        // We were told how big to be        if (specMode == MeasureSpec.EXACTLY) {            result = specSize;        }        // Calculate the width according the views count        else {            int count = 3;            if (viewFlow != null) {                count = viewFlow.getViewsCount();            }            float temp = circleSeparation - 2*radius;            result = (int) (getPaddingLeft() + getPaddingRight()                    + (count * 2 * radius) + (count - 1) * temp + 1);            // Respect AT_MOST value if that was what is called for by            // measureSpec            if (specMode == MeasureSpec.AT_MOST) {                result = Math.min(result, specSize);            }        }        return result;    }    /**     * Determines the height of this view     *      * @param measureSpec     *            A measureSpec packed into an int     * @return The height of the view, honoring constraints from measureSpec     */    private int measureHeight(int measureSpec) {        int result = 0;        int specMode = MeasureSpec.getMode(measureSpec);        int specSize = MeasureSpec.getSize(measureSpec);        // We were told how big to be        if (specMode == MeasureSpec.EXACTLY) {            result = specSize;        }        // Measure the height        else {            result = (int) (2 * radius + getPaddingTop() + getPaddingBottom() + 1);            // Respect AT_MOST value if that was what is called for by            // measureSpec            if (specMode == MeasureSpec.AT_MOST) {                result = Math.min(result, specSize);            }        }        return result;    }    /**     * Sets the fill color     *      * @param color     *            ARGB value for the text     */    public void setFillColor(int color) {        mPaintActive.setColor(color);        invalidate();    }    /**     * Sets the stroke color     *      * @param color     *            ARGB value for the text     */    public void setStrokeColor(int color) {        mPaintInactive.setColor(color);        invalidate();    }    /**     * Resets the fade out timer to 0. Creating a new one if needed     */    private void resetTimer() {        // Only set the timer if we have a timeout of at least 1 millisecond        if (fadeOutTime > 0) {            // Check if we need to create a new timer            if (timer == null || timer._run == false) {                // Create and start a new timer                timer = new FadeTimer();                timer.execute();            } else {                // Reset the current tiemr to 0                timer.resetTimer();            }        }    }    /**     * Counts from 0 to the fade out time and animates the view away when     * reached     */    private class FadeTimer extends AsyncTask<Void, Void, Void> {        // The current count        private int timer = 0;        // If we are inside the timing loop        private boolean _run = true;        public void resetTimer() {            timer = 0;        }        @Override        protected Void doInBackground(Void... arg0) {            while (_run) {                try {                    // Wait for a millisecond                    Thread.sleep(1);                    // Increment the timer                    timer++;                    // Check if we've reached the fade out time                    if (timer == fadeOutTime) {                        // Stop running                        _run = false;                    }                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            return null;        }        @Override        protected void onPostExecute(Void result) {            animation = AnimationUtils.loadAnimation(getContext(),                    android.R.anim.fade_out);            animation.setAnimationListener(animationListener);            startAnimation(animation);        }    }    @Override    public void onAnimationEnd(Animation animation) {        setVisibility(View.GONE);    }    @Override    public void onAnimationRepeat(Animation animation) {    }    @Override    public void onAnimationStart(Animation animation) {    }}

FlowIndicator

package com.example.bannerdemo;/** * An interface which defines the contract between a ViewFlow and a * FlowIndicator.<br/> * A FlowIndicator is responsible to show an visual indicator on the total views * number and the current visible view.<br/> *  */public interface FlowIndicator extends ViewFlow.ViewSwitchListener {    /**     * Set the current ViewFlow. This method is called by the ViewFlow when the     * FlowIndicator is attached to it.     *      * @param view     */    public void setViewFlow(ViewFlow view);    /**     * The scroll position has been changed. A FlowIndicator may implement this     * method to reflect the current position     *      * @param h     * @param v     * @param oldh     * @param oldv     */    public void onScrolled(int h, int v, int oldh, int oldv);}

ViewFlow

package com.example.bannerdemo;import java.util.ArrayList;import java.util.LinkedList;import android.content.Context;import android.content.res.Configuration;import android.content.res.TypedArray;import android.database.DataSetObserver;import android.os.Handler;import android.os.Message;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.VelocityTracker;import android.view.View;import android.view.ViewConfiguration;import android.view.ViewGroup;import android.view.ViewTreeObserver.OnGlobalLayoutListener;import android.widget.AbsListView;import android.widget.Adapter;import android.widget.AdapterView;import android.widget.Scroller;/** * A horizontally scrollable {@link ViewGroup} with items populated from an * {@link Adapter}. The ViewFlow uses a buffer to store loaded {@link View}s in. * The default size of the buffer is 3 elements on both sides of the currently * visible {@link View}, making up a total buffer size of 3 * 2 + 1 = 7. The * buffer size can be changed using the {@code sidebuffer} xml attribute. * */public class ViewFlow extends AdapterView<Adapter> {    private static final int SNAP_VELOCITY = 1000;    private static final int INVALID_SCREEN = -1;    private final static int TOUCH_STATE_REST = 0;    private final static int TOUCH_STATE_SCROLLING = 1;    private LinkedList<View> mLoadedViews;    private int mCurrentBufferIndex;    private int mCurrentAdapterIndex;    private int mSideBuffer = 2;    private Scroller mScroller;    private VelocityTracker mVelocityTracker;    private int mTouchState = TOUCH_STATE_REST;    private float mLastMotionX;    private int mTouchSlop;    private int mMaximumVelocity;    private int mCurrentScreen;    private int mNextScreen = INVALID_SCREEN;    private boolean mFirstLayout = true;    private ViewSwitchListener mViewSwitchListener;    private Adapter mAdapter;    private int mLastScrollDirection;    private AdapterDataSetObserver mDataSetObserver;    private FlowIndicator mIndicator;    private int mLastOrientation = -1;    private long timeSpan = 4500;    private Handler handler;    private OnGlobalLayoutListener orientationChangeListener = new OnGlobalLayoutListener() {        @Override        public void onGlobalLayout() {            getViewTreeObserver().removeGlobalOnLayoutListener(                    orientationChangeListener);            setSelection(mCurrentAdapterIndex);        }    };    /**     * Receives call backs when a new {@link View} has been scrolled to.     */    public static interface ViewSwitchListener {        /**         * This method is called when a new View has been scrolled to.         *         * @param view         *            the {@link View} currently in focus.         * @param position         *            The position in the adapter of the {@link View} currently in focus.         */        void onSwitched(View view, int position);    }    public ViewFlow(Context context) {        super(context);        mSideBuffer = 3;        init();    }    public ViewFlow(Context context, int sideBuffer) {        super(context);        mSideBuffer = sideBuffer;        init();    }    public ViewFlow(Context context, AttributeSet attrs) {        super(context, attrs);        TypedArray styledAttrs = context.obtainStyledAttributes(attrs,R.styleable.ViewFlow);        mSideBuffer = styledAttrs.getInt(R.styleable.ViewFlow_sidebuffer, 3);        init();    }    private void init() {        mLoadedViews = new LinkedList<View>();        mScroller = new Scroller(getContext());        final ViewConfiguration configuration = ViewConfiguration                .get(getContext());        mTouchSlop = configuration.getScaledTouchSlop();        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();    }    public void startAutoFlowTimer(){        handler = new Handler(){            @Override            public void handleMessage(Message msg) {                snapToScreen((mCurrentScreen+1)%getChildCount());                Message message = handler.obtainMessage(0);                sendMessageDelayed(message, timeSpan);            }        };        Message message = handler.obtainMessage(0);        handler.sendMessageDelayed(message, timeSpan);    }    public void stopAutoFlowTimer(){        if(handler!=null)            handler.removeMessages(0);        handler = null;    }    public void onConfigurationChanged(Configuration newConfig) {        if (newConfig.orientation != mLastOrientation) {            mLastOrientation = newConfig.orientation;            getViewTreeObserver().addOnGlobalLayoutListener(orientationChangeListener);        }    }    public int getViewsCount() {        return mSideBuffer;    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        final int width = MeasureSpec.getSize(widthMeasureSpec);        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);        if (widthMode != MeasureSpec.EXACTLY && !isInEditMode()) {            throw new IllegalStateException(                    "ViewFlow can only be used in EXACTLY mode.");        }        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);        if (heightMode != MeasureSpec.EXACTLY && !isInEditMode()) {            throw new IllegalStateException(                    "ViewFlow can only be used in EXACTLY mode.");        }        // The children are given the same width and height as the workspace        final int count = getChildCount();        for (int i = 0; i < count; i++) {            getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);        }        if (mFirstLayout) {            mScroller.startScroll(0, 0, mCurrentScreen * width, 0, 0);            mFirstLayout = false;        }    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        int childLeft = 0;        final int count = getChildCount();        for (int i = 0; i < count; i++) {            final View child = getChildAt(i);            if (child.getVisibility() != View.GONE) {                final int childWidth = child.getMeasuredWidth();                child.layout(childLeft, 0, childLeft + childWidth,                        child.getMeasuredHeight());                childLeft += childWidth;            }        }    }    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        if(mSideBuffer<2){                return false;            }        if (getChildCount() == 0)            return false;        if (mVelocityTracker == null) {            mVelocityTracker = VelocityTracker.obtain();        }        mVelocityTracker.addMovement(ev);        final int action = ev.getAction();        final float x = ev.getX();        switch (action) {        case MotionEvent.ACTION_DOWN:            /*             * If being flinged and user touches, stop the fling. isFinished             * will be false if being flinged.             */            if (!mScroller.isFinished()) {                mScroller.abortAnimation();            }            // Remember where the motion event started            mLastMotionX = x;            mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST                    : TOUCH_STATE_SCROLLING;            if(handler!=null)                handler.removeMessages(0);            break;        case MotionEvent.ACTION_MOVE:            final int xDiff = (int) Math.abs(x - mLastMotionX);            boolean xMoved = xDiff > mTouchSlop;            if (xMoved) {                // Scroll if the user moved far enough along the X axis                mTouchState = TOUCH_STATE_SCROLLING;            }            if (mTouchState == TOUCH_STATE_SCROLLING) {                // Scroll to follow the motion event                final int deltaX = (int) (mLastMotionX - x);                mLastMotionX = x;                final int scrollX = getScrollX();                if (deltaX < 0) {                    if (scrollX > 0) {                        scrollBy(Math.max(-scrollX, deltaX), 0);                    }                } else if (deltaX > 0) {                    final int availableToScroll = getChildAt(                            getChildCount() - 1).getRight()                            - scrollX - getWidth();                    if (availableToScroll > 0) {                        scrollBy(Math.min(availableToScroll, deltaX), 0);                    }                }                return true;            }            break;        case MotionEvent.ACTION_UP:            if (mTouchState == TOUCH_STATE_SCROLLING) {                final VelocityTracker velocityTracker = mVelocityTracker;                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);                int velocityX = (int) velocityTracker.getXVelocity();                if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {                    // Fling hard enough to move left                    snapToScreen(mCurrentScreen - 1);                } else if (velocityX < -SNAP_VELOCITY                        && mCurrentScreen < getChildCount() - 1) {                    // Fling hard enough to move right                    snapToScreen(mCurrentScreen + 1);                } else {                    snapToDestination();                }                if (mVelocityTracker != null) {                    mVelocityTracker.recycle();                    mVelocityTracker = null;                }            }            mTouchState = TOUCH_STATE_REST;            if(handler!=null){                Message message = handler.obtainMessage(0);                handler.sendMessageDelayed(message, timeSpan);            }            break;        case MotionEvent.ACTION_CANCEL:            mTouchState = TOUCH_STATE_REST;        }        return false;    }    @Override    public boolean onTouchEvent(MotionEvent ev) {        if(mSideBuffer<2){            return false;        }        if (getChildCount() == 0)            return false;        if (mVelocityTracker == null) {            mVelocityTracker = VelocityTracker.obtain();        }        mVelocityTracker.addMovement(ev);        final int action = ev.getAction();        final float x = ev.getX();        switch (action) {        case MotionEvent.ACTION_DOWN:            /*             * If being flinged and user touches, stop the fling. isFinished             * will be false if being flinged.             */            if (!mScroller.isFinished()) {                mScroller.abortAnimation();            }            // Remember where the motion event started            mLastMotionX = x;            mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST                    : TOUCH_STATE_SCROLLING;            if(handler!=null)                handler.removeMessages(0);            break;        case MotionEvent.ACTION_MOVE:            final int xDiff = (int) Math.abs(x - mLastMotionX);            boolean xMoved = xDiff > mTouchSlop;            if (xMoved) {                // Scroll if the user moved far enough along the X axis                mTouchState = TOUCH_STATE_SCROLLING;            }            if (mTouchState == TOUCH_STATE_SCROLLING) {                // Scroll to follow the motion event                final int deltaX = (int) (mLastMotionX - x);                mLastMotionX = x;                final int scrollX = getScrollX();                if (deltaX < 0) {                    if (scrollX > 0) {                        scrollBy(Math.max(-scrollX, deltaX), 0);                    }                } else if (deltaX > 0) {                    final int availableToScroll = getChildAt(                            getChildCount() - 1).getRight()                            - scrollX - getWidth();                    if (availableToScroll > 0) {                        scrollBy(Math.min(availableToScroll, deltaX), 0);                    }                }                return true;            }            break;        case MotionEvent.ACTION_UP:            if (mTouchState == TOUCH_STATE_SCROLLING) {                final VelocityTracker velocityTracker = mVelocityTracker;                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);                int velocityX = (int) velocityTracker.getXVelocity();                if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {                    // Fling hard enough to move left                    snapToScreen(mCurrentScreen - 1);                } else if (velocityX < -SNAP_VELOCITY                        && mCurrentScreen < getChildCount() - 1) {                    // Fling hard enough to move right                    snapToScreen(mCurrentScreen + 1);                }//              else if (velocityX < -SNAP_VELOCITY//                          && mCurrentScreen == getChildCount() - 1) {//                      snapToScreen(0);//              }//              else if (velocityX > SNAP_VELOCITY//                          && mCurrentScreen == 0) {//                      snapToScreen(getChildCount() - 1);//              }                else {                    snapToDestination();                }                if (mVelocityTracker != null) {                    mVelocityTracker.recycle();                    mVelocityTracker = null;                }            }            mTouchState = TOUCH_STATE_REST;            if(handler!=null){                Message message = handler.obtainMessage(0);                handler.sendMessageDelayed(message, timeSpan);            }            break;        case MotionEvent.ACTION_CANCEL:            snapToDestination();            mTouchState = TOUCH_STATE_REST;        }        return true;    }    @Override    protected void onScrollChanged(int h, int v, int oldh, int oldv) {        super.onScrollChanged(h, v, oldh, oldv);        if (mIndicator != null) {            /*             * The actual horizontal scroll origin does typically not match the             * perceived one. Therefore, we need to calculate the perceived             * horizontal scroll origin here, since we use a view buffer.             */            int hPerceived = h + (mCurrentAdapterIndex - mCurrentBufferIndex)                    * getWidth();            mIndicator.onScrolled(hPerceived, v, oldh, oldv);        }    }    private void snapToDestination() {        final int screenWidth = getWidth();        final int whichScreen = (getScrollX() + (screenWidth / 2))                / screenWidth;        snapToScreen(whichScreen);    }    private void snapToScreen(int whichScreen) {        mLastScrollDirection = whichScreen - mCurrentScreen;        if (!mScroller.isFinished())            return;        whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));        mNextScreen = whichScreen;        final int newX = whichScreen * getWidth();        final int delta = newX - getScrollX();        mScroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta) * 2);        invalidate();    }    @Override    public void computeScroll() {        if (mScroller.computeScrollOffset()) {            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());            postInvalidate();        } else if (mNextScreen != INVALID_SCREEN) {            mCurrentScreen = Math.max(0,                    Math.min(mNextScreen, getChildCount() - 1));            mNextScreen = INVALID_SCREEN;            postViewSwitched(mLastScrollDirection);        }    }    /**     * Scroll to the {@link View} in the view buffer specified by the index.     *     * @param indexInBuffer     *            Index of the view in the view buffer.     */    private void setVisibleView(int indexInBuffer, boolean uiThread) {        mCurrentScreen = Math.max(0,                Math.min(indexInBuffer, getChildCount() - 1));        int dx = (mCurrentScreen * getWidth()) - mScroller.getCurrX();        mScroller.startScroll(mScroller.getCurrX(), mScroller.getCurrY(), dx,                0, 0);        if(dx == 0)            onScrollChanged(mScroller.getCurrX() + dx, mScroller.getCurrY(), mScroller.getCurrX() + dx, mScroller.getCurrY());        if (uiThread)            invalidate();        else            postInvalidate();    }    /**     * Set the listener that will receive notifications every time the {code     * ViewFlow} scrolls.     *     * @param l     *            the scroll listener     */    public void setOnViewSwitchListener(ViewSwitchListener l) {        mViewSwitchListener = l;    }    @Override    public Adapter getAdapter() {        return mAdapter;    }    @Override    public void setAdapter(Adapter adapter) {        setAdapter(adapter, 0);    }    public void setAdapter(Adapter adapter, int initialPosition) {        if (mAdapter != null) {            mAdapter.unregisterDataSetObserver(mDataSetObserver);        }        mAdapter = adapter;        if (mAdapter != null) {            mDataSetObserver = new AdapterDataSetObserver();            mAdapter.registerDataSetObserver(mDataSetObserver);        }        if (mAdapter == null || mAdapter.getCount() == 0)            return;        setSelection(initialPosition);    }    @Override    public View getSelectedView() {        return (mCurrentBufferIndex < mLoadedViews.size() ? mLoadedViews                .get(mCurrentBufferIndex) : null);    }    @Override    public int getSelectedItemPosition() {        return mCurrentAdapterIndex;    }    /**     * Set the FlowIndicator     *     * @param flowIndicator     */    public void setFlowIndicator(FlowIndicator flowIndicator) {        mIndicator = flowIndicator;        mIndicator.setViewFlow(this);    }    @Override    public void setSelection(int position) {        mNextScreen = INVALID_SCREEN;        mScroller.forceFinished(true);        if (mAdapter == null)            return;        position = Math.max(position, 0);        position =  Math.min(position, mAdapter.getCount() - 1);        ArrayList<View> recycleViews = new ArrayList<View>();        View recycleView;        while (!mLoadedViews.isEmpty()) {            recycleViews.add(recycleView = mLoadedViews.remove());            detachViewFromParent(recycleView);        }        View currentView = makeAndAddView(position, true,                (recycleViews.isEmpty() ? null : recycleViews.remove(0)));        mLoadedViews.addLast(currentView);        for(int offset = 1; mSideBuffer - offset >= 0; offset++) {            int leftIndex = position - offset;            int rightIndex = position + offset;            if(leftIndex >= 0)                mLoadedViews.addFirst(makeAndAddView(leftIndex, false,                        (recycleViews.isEmpty() ? null : recycleViews.remove(0))));            if(rightIndex < mAdapter.getCount())                mLoadedViews.addLast(makeAndAddView(rightIndex, true,                        (recycleViews.isEmpty() ? null : recycleViews.remove(0))));        }        mCurrentBufferIndex = mLoadedViews.indexOf(currentView);        mCurrentAdapterIndex = position;        for (View view : recycleViews) {            removeDetachedView(view, false);        }        requestLayout();        setVisibleView(mCurrentBufferIndex, false);        if (mIndicator != null) {            mIndicator.onSwitched(mLoadedViews.get(mCurrentBufferIndex),                    mCurrentAdapterIndex);        }        if (mViewSwitchListener != null) {            mViewSwitchListener                    .onSwitched(mLoadedViews.get(mCurrentBufferIndex),                            mCurrentAdapterIndex);        }    }    private void resetFocus() {        mLoadedViews.clear();        removeAllViewsInLayout();        for (int i = Math.max(0, mCurrentAdapterIndex - mSideBuffer); i < Math                .min(mAdapter.getCount(), mCurrentAdapterIndex + mSideBuffer                        + 1); i++) {            mLoadedViews.addLast(makeAndAddView(i, true, null));            if (i == mCurrentAdapterIndex)                mCurrentBufferIndex = mLoadedViews.size() - 1;        }        requestLayout();    }    private void postViewSwitched(int direction) {        if (direction == 0)            return;        if (direction > 0) { // to the right            mCurrentAdapterIndex++;            mCurrentBufferIndex++;//          if(direction > 1) {//              mCurrentAdapterIndex += mAdapter.getCount() - 2;//              mCurrentBufferIndex += mAdapter.getCount() - 2;//          }            View recycleView = null;            // Remove view outside buffer range            if (mCurrentAdapterIndex > mSideBuffer) {                recycleView = mLoadedViews.removeFirst();                detachViewFromParent(recycleView);                // removeView(recycleView);                mCurrentBufferIndex--;            }            // Add new view to buffer            int newBufferIndex = mCurrentAdapterIndex + mSideBuffer;            if (newBufferIndex < mAdapter.getCount())                mLoadedViews.addLast(makeAndAddView(newBufferIndex, true,                        recycleView));        } else { // to the left            mCurrentAdapterIndex--;            mCurrentBufferIndex--;//          if(direction < -1) {//              mCurrentAdapterIndex -= mAdapter.getCount() - 2;//              mCurrentBufferIndex -= mAdapter.getCount() - 2;//          }            View recycleView = null;            // Remove view outside buffer range            if (mAdapter.getCount() - 1 - mCurrentAdapterIndex > mSideBuffer) {                recycleView = mLoadedViews.removeLast();                detachViewFromParent(recycleView);            }            // Add new view to buffer            int newBufferIndex = mCurrentAdapterIndex - mSideBuffer;            if (newBufferIndex > -1) {                mLoadedViews.addFirst(makeAndAddView(newBufferIndex, false,                        recycleView));                mCurrentBufferIndex++;            }        }        requestLayout();        setVisibleView(mCurrentBufferIndex, true);        if (mIndicator != null) {            mIndicator.onSwitched(mLoadedViews.get(mCurrentBufferIndex),                    mCurrentAdapterIndex);        }        if (mViewSwitchListener != null) {            mViewSwitchListener                    .onSwitched(mLoadedViews.get(mCurrentBufferIndex),                            mCurrentAdapterIndex);        }    }    private View setupChild(View child, boolean addToEnd, boolean recycle) {        LayoutParams p = (LayoutParams) child                .getLayoutParams();        if (p == null) {            p = new AbsListView.LayoutParams(                    LayoutParams.FILL_PARENT,                    LayoutParams.WRAP_CONTENT, 0);        }        if (recycle)            attachViewToParent(child, (addToEnd ? -1 : 0), p);        else            addViewInLayout(child, (addToEnd ? -1 : 0), p, true);        return child;    }    private View makeAndAddView(int position, boolean addToEnd, View convertView) {        View view = mAdapter.getView(position, convertView, this);        return setupChild(view, addToEnd, convertView != null);    }    class AdapterDataSetObserver extends DataSetObserver {        @Override        public void onChanged() {            View v = getChildAt(mCurrentBufferIndex);            if (v != null) {                for (int index = 0; index < mAdapter.getCount(); index++) {                    if (v.equals(mAdapter.getItem(index))) {                        mCurrentAdapterIndex = index;                        break;                    }                }            }            resetFocus();        }        @Override        public void onInvalidated() {            // Not yet implemented!        }    }    public void setTimeSpan(long timeSpan) {        this.timeSpan = timeSpan;    }    public void setmSideBuffer(int mSideBuffer) {        this.mSideBuffer = mSideBuffer;    }}

上面这三个view就是对轮播图片的逻辑的封装,如果只想用的小伙伴就可以不看了,复制粘贴,改下包名ok
MainActivity

package com.example.bannerdemo;import java.util.ArrayList;import java.util.List;import com.nostra13.universalimageloader.core.ImageLoader;import android.app.Activity;import android.os.Bundle;import android.view.Menu;import android.view.MenuItem;import android.view.View;public class MainActivity extends Activity {private ViewFlow viewFlow;private CircleFlowIndicator viewflowindic;private List<Banner > list = new ArrayList<Banner>();String img0 = "http://121.42.220.158:8081/pinke/fileUpload/brannerImages/1451550226031.png";String img1 = "http://121.42.220.158:8081/pinke/fileUpload/brannerImages/1451550217854.png";    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        init();        list.add(new Banner(img0));        list.add(new Banner(img1));        setupViewflow(list);    }    private void init() {        viewFlow = (ViewFlow) findViewById(R.id.viewFlow);        viewflowindic = (CircleFlowIndicator) findViewById(R.id.viewflowindic);    }     /**     * 设置ViewFlow  轮播图片     */    private void setupViewflow(List<Banner> bannerList) {        BannerAdapter bannerAdapter = new BannerAdapter(this, bannerList);        viewFlow.setmSideBuffer(bannerList == null ? 0 : bannerList.size());        viewFlow.setSelection(0);//初始位置设置        viewflowindic.setVisibility(View.GONE);        if (bannerList != null && bannerList.size() > 1) {            viewflowindic.setVisibility(View.VISIBLE);            viewFlow.setFlowIndicator(viewflowindic);            viewFlow.startAutoFlowTimer(); // 启动自动播放        } else {            viewFlow.stopAutoFlowTimer();        }        viewFlow.setAdapter(bannerAdapter);    }}

BannerAdapter

package com.example.bannerdemo;import android.content.Context;import android.content.Intent;import android.util.Log;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import java.util.List;import com.nostra13.universalimageloader.core.DisplayImageOptions;import com.nostra13.universalimageloader.core.ImageLoader;import com.nostra13.universalimageloader.core.display.RoundedBitmapDisplayer;/** *  * 图片轮播适配器 */public class BannerAdapter extends BaseAdapter{    private Context mContext;    private List<Banner> bannerList;    private LayoutInflater mInflater;    DisplayImageOptions options;        // DisplayImageOptions是用于设置图片显示的类    public BannerAdapter(Context mContext,List<Banner> bannerList){        this.mContext=mContext;        this.bannerList=bannerList;        mInflater=LayoutInflater.from(mContext);        // 使用DisplayImageOptions.Builder()创建DisplayImageOptions          options = new DisplayImageOptions.Builder()              .showStubImage(R.drawable.ic_launcher)          // 设置图片下载期间显示的图片              .showImageForEmptyUri(R.drawable.ic_launcher)  // 设置图片Uri为空或是错误的时候显示的图片              .showImageOnFail(R.drawable.ic_launcher)       // 设置图片加载或解码过程中发生错误显示的图片                  .cacheInMemory(true)                        // 设置下载的图片是否缓存在内存中              .cacheOnDisc(true)                          // 设置下载的图片是否缓存在SD卡中             // .displayer(new RoundedBitmapDisplayer(20))  // 设置成圆角图片              .build();                                   // 创建配置过得DisplayImageOption对象      }    @Override    public int getCount() {        return Integer.MAX_VALUE;//返回很大的值使得getview中的position不断增大来实现循环    }    @Override    public Object getItem(int position) {        return position;    }    @Override    public long getItemId(int position) {        return position;    }    @Override    public View getView(final int position, View convertView, ViewGroup parent){        ViewHolder mHolder=null;        if(convertView==null){            mHolder=new ViewHolder();            convertView=mInflater.inflate(R.layout.item_banner,null);            mHolder.imageView=(ImageView)convertView.findViewById(R.id.iv_banner);            convertView.setTag(mHolder);        }else{            mHolder=(ViewHolder)convertView.getTag();        }        //因为返回的是最大值所以要取余得当当前循环后的item        final Banner banner=bannerList.get(position % bannerList.size());                ImageLoader.getInstance().displayImage(banner.pic_url, mHolder.imageView,options);        mHolder.imageView.setOnClickListener(new View.OnClickListener(){            @Override            public void onClick(View v) {              // 跳转            }        });        return convertView;    }    class ViewHolder{        ImageView imageView;    }}

Banner实体类

package com.example.bannerdemo;/** * banner图片 */public class Banner {    public String pic_url;//uil    public Banner(String pic_url) {        super();        this.pic_url = pic_url;    }    public Banner() {        super();    }}

加载图片的时候用的ImagerLoader框架,如果还需要导入ImagerLoader.jar
然后记得在application中初始化并在清单文件中添加权限:

package com.example.bannerdemo;import com.nostra13.universalimageloader.cache.disc.naming.Md5FileNameGenerator;import com.nostra13.universalimageloader.cache.memory.impl.UsingFreqLimitedMemoryCache;import com.nostra13.universalimageloader.core.DisplayImageOptions;import com.nostra13.universalimageloader.core.ImageLoader;import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;import com.nostra13.universalimageloader.core.assist.QueueProcessingType;import com.nostra13.universalimageloader.core.download.BaseImageDownloader;import android.app.Application;import android.graphics.Bitmap.CompressFormat;public class MyApplication extends Application {      @SuppressWarnings("deprecation")    @Override      public void onCreate() {          super.onCreate();          ImageLoaderConfiguration config = new ImageLoaderConfiguration                  .Builder(this)                  .memoryCacheExtraOptions(480, 800) // max width, max height,即保存的每个缓存文件的最大长宽                  .threadPoolSize(3)//线程池内加载的数量                  .threadPriority(Thread.NORM_PRIORITY - 2)                  .denyCacheImageMultipleSizesInMemory()                  .memoryCache(new UsingFreqLimitedMemoryCache(2 * 1024 * 1024)) // You can pass your own memory cache implementation/你可以通过自己的内存缓存实现                  .memoryCacheSize(2 * 1024 * 1024)                    .discCacheSize(50 * 1024 * 1024)                    .discCacheFileNameGenerator(new Md5FileNameGenerator())//将保存的时候的URI名称用MD5 加密                  .tasksProcessingOrder(QueueProcessingType.LIFO)                  .defaultDisplayImageOptions(DisplayImageOptions.createSimple())                  .imageDownloader(new BaseImageDownloader(this, 5 * 1000, 30 * 1000)) // connectTimeout (5 s), readTimeout (30 s)超时时间                  .writeDebugLogs() // Remove for release app                  .build();//开始构建          //Initialize ImageLoader with configuration.          ImageLoader.getInstance().init(config);      }  }  
<uses-permission android:name="android.permission.INTERNET" />  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 

下面在附加这个项目中用到的一些资源:
atrrs.xml

<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name="ViewFlow">        <attr name="sidebuffer" format="integer"/>    </declare-styleable>    <declare-styleable name="CircleFlowIndicator">        <attr name="activeColor" format="color" />        <attr name="inactiveColor" format="color" />        <attr name="radius" format="dimension" />        <attr name="centered" format="boolean" />        <attr name="fadeOut" format="integer" />        <attr name="inactiveType">            <flag name="stroke" value="0" />            <flag name="fill" value="1" />        </attr>        <attr name="activeType">            <flag name="stroke" value="0" />            <flag name="fill" value="1" />        </attr>        <attr name="circleSeparation" format="dimension" />        <attr name="activeRadius" format="dimension" />    </declare-styleable></resources>

item_banner.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">    <ImageView        android:id="@+id/iv_banner"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:scaleType="fitXY"        /></LinearLayout>

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    xmlns:banner="http://schemas.android.com/apk/res-auto"    android:layout_width="match_parent"    android:layout_height="match_parent"  >   <RelativeLayout        android:id="@+id/rl_banner"        android:layout_width="match_parent"        android:layout_height="150dp"        android:layout_marginTop="20dp">        <com.example.bannerdemo.ViewFlow            android:id="@+id/viewFlow"            android:layout_width="match_parent"            android:layout_height="match_parent" />        <com.example.bannerdemo.CircleFlowIndicator            android:id="@+id/viewflowindic"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_alignParentBottom="true"            android:layout_centerHorizontal="true"            android:layout_marginBottom="8dp"            android:visibility="gone"            banner:activeColor="#8000b5ff"            banner:activeType="fill"            banner:circleSeparation="15dip"            banner:inactiveColor="#80ffffff"            banner:inactiveType="fill"            banner:radius="3dip" />    </RelativeLayout></RelativeLayout>

代码看起来比较多,如果直接继承viewper的话不需要写这么多,但是实际上用到的代码就是MainActivity中的setupViewflow(),需要注意的是adapter中的getCount()的处理!ok,这个基本上就说完了!
附上项目下载地址eclipse版的
http://download.csdn.net/detail/wei8023hzp/9408230

1 0
原创粉丝点击