android 简单的广告条实现

        目前百度上的android广告条实现,大多数都是ViewPager实现的,本文则定义ViewFlow类继承AdapterView类进行实现,并对自动滑动的代码做了优化,支持从左到右依次滑动也支持从右到左依次滑动。要先了解scroller,才能较好的理解本文。不说了上代码。

  首先是ViewFlow类,

  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 p; //上一页页数
    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 = 3000;  //广告条每隔3s调到下一个页面
    private Handler handler;
    private ViewGroup viewGroup;
    private GestureDetector mGestureDetector;
    private Context context;
    private OnGlobalLayoutListener orientationChangeListener = new OnGlobalLayoutListener() {
 
        @Override
        public void onGlobalLayout() {
            getViewTreeObserver().removeGlobalOnLayoutListener(orientationChangeListener);
            setSelection(mCurrentAdapterIndex);
        }
    };
 
        public static interface ViewSwitchListener {
 
       
        void onSwitched(View view, int position);
 
    }
 
    public ViewFlow(Context context) {
        super(context);
        this.context=context;
        mSideBuffer = 6;
        init();
    }
 
    public ViewFlow(Context context, int sideBuffer) {
        super(context);
        this.context=context;
        mSideBuffer = sideBuffer;
        init();
    }
 
    public ViewFlow(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context=context;
        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();
        mGestureDetector = new GestureDetector(getContext(), new YScrollDetector());
    }
 
    public void setViewGroup(ViewGroup viewGroup) {
        this.viewGroup = viewGroup;
    }
    
    /**
     * 方法startAutoFlowTimer(),利用handler的机制,每隔timespan时间调用snapToScreen方法调到相应的页面
     */
    
    public void startAutoFlowTimer() {
        handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
               
            if(getChildCount()>0&&mSideBuffer>1){
            if(mCurrentScreen==0){
           
            snapToScreen((mCurrentScreen + 1) % getChildCount());
           
            }
            if(mCurrentScreen>p&&mCurrentScreen>0&&mCurrentScreen!=getChildCount()-1){
           
           
            snapToScreen((mCurrentScreen + 1) % getChildCount());
            }
            if(mCurrentScreen<p&&p<=getChildCount()-1){
           
            snapToScreen((mCurrentScreen - 1) % getChildCount());
            }
            if(mCurrentScreen==getChildCount()-1){
           
            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;
    }
    /**
     * onMeasure()方法主要是为了测量控件的大小
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
        final int width = MeasureSpec.getSize(widthMeasureSpec);  //获取控件的宽度
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec); //获取宽度的模式 比如为UNSPECIFIED,EXACTLY,AT_MOST
        if (widthMode != MeasureSpec.EXACTLY && !isInEditMode()) {   //EXACTLY代表着精准的尺寸,比如FILL_PARENT。也就是说这个控件必须是精准尺寸的,否则会报错
            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);  //设置mScroller的滑动距离
            mFirstLayout = false;
        }
    }
 
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {   //onLayout()函数主要控制控件的位置
        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 (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:
             
            // 处理事件,左后滑动时,传递给子项处理,上下滑动时,交由ViewGroup处理
            if (viewGroup != null) {
                viewGroup.requestDisallowInterceptTouchEvent(!mGestureDetector.onTouchEvent(ev));
            }
 
            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:
            // 处理事件,左后滑动时,传递给子项处理,上下滑动时,交由ViewGroup处理
            if (viewGroup != null) {
                viewGroup.requestDisallowInterceptTouchEvent(!mGestureDetector.onTouchEvent(ev));
            }
 
            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) {          //根据deltaX判断滑动的左右方向
                    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 (viewGroup != null) {
                viewGroup.requestDisallowInterceptTouchEvent(false);
            }
 
            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:
            if (viewGroup != null) {
                viewGroup.requestDisallowInterceptTouchEvent(false);
            }
 
            mTouchState = TOUCH_STATE_REST;
        }
        return false;
    }
 
    /**
     * 手势监听(用于识别手势滑动)
     * 
     * @author LiangZiChao
     * 
     */
    class YScrollDetector extends SimpleOnGestureListener {
 
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            /**
             * 如果上下滑动超过左右滑动则返回false
             */
            return (Math.abs(distanceY) > Math.abs(distanceX));
        }
    }
 
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        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 (!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 (LYinfo.isTCAgentOpen)
                TCAgent.onEvent(context,context.getResources().getString(R.string.New_Banner_Slide));
                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:
            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);
    }
 
    
    /**
     * 函数snapToScreen主要是指翻到哪一页
     * @param whichScreen
     */
    private void snapToScreen(int whichScreen) {
        mLastScrollDirection = whichScreen - mCurrentScreen;
        if (!mScroller.isFinished())
            return;
         
        whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
        p=mCurrentScreen;
        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++;
 
           
 
            View recycleView = null;
 
         
            if (mCurrentAdapterIndex > mSideBuffer) {
                recycleView = mLoadedViews.removeFirst();
                detachViewFromParent(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) {
        ViewGroup.LayoutParams p = (ViewGroup.LayoutParams) child.getLayoutParams();
        if (p == null) {
            p = new AbsListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.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 setSideBuffer(int sideBuffer) {
        this.mSideBuffer = sideBuffer;
    }
}

下面是FlowIndicator接口类的实现

public interface FlowIndicator extends 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);
}

下面是CircleFlowIndicator类的实现

  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 = 6;
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 = 6;
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) {
// TODO Auto-generated catch block
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) {
}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值