zoukankan      html  css  js  c++  java
  • 48、ViewFlow ---- 滑动广告页

     1 <!-- main.xml -->
     2 <?xml version="1.0" encoding="utf-8"?>
     3 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     4     xmlns:app="http://schemas.android.com/apk/res/com.viewflowtest.cjy"
     5     android:layout_width="fill_parent"
     6     android:layout_height="fill_parent"
     7     android:orientation="vertical" >
     8 
     9     <FrameLayout
    10         android:id="@+id/framelayout"
    11         android:layout_width="fill_parent"
    12         android:layout_height="150dip"
    13         android:background="#ffffff" >
    14 
    15         <org.taptwo.android.widget.ViewFlow
    16             android:id="@+id/viewflow"
    17             android:layout_width="fill_parent"
    18             android:layout_height="fill_parent" />
    19 
    20         <LinearLayout
    21             android:layout_width="fill_parent"
    22             android:layout_height="wrap_content"
    23             android:layout_gravity="bottom"
    24             android:background="#88252525"
    25             android:gravity="center"
    26             android:padding="3dip" >
    27 
    28             <org.taptwo.android.widget.CircleFlowIndicator
    29                 android:id="@+id/viewflowindic"
    30                 android:layout_width="wrap_content"
    31                 android:layout_height="wrap_content"
    32                 android:layout_gravity="center_horizontal|bottom"
    33                 android:padding="2dip"
    34                 app:activeColor="#ff0000"
    35                 app:activeType="fill"
    36                 app:circleSeparation="20dip"
    37                 app:inactiveColor="#ffffff"
    38                 app:inactiveType="fill"
    39                 app:radius="4dip" />
    40         </LinearLayout>
    41     </FrameLayout>
    42 
    43     <TextView
    44         android:layout_width="fill_parent"
    45         android:layout_height="wrap_content"
    46         android:text="@string/hello" />
    47 
    48 </LinearLayout>
     1 import org.taptwo.android.widget.CircleFlowIndicator;
     2 import org.taptwo.android.widget.ViewFlow;
     3 import android.app.Activity;
     4 import android.os.Bundle;
     5 
     6 public class ViewFlowTestActivity extends Activity {
     7     
     8     private ViewFlow viewFlow;
     9     
    10     @Override
    11     public void onCreate(Bundle savedInstanceState) {
    12         super.onCreate(savedInstanceState);
    13         setContentView(R.layout.main);
    14         
    15         viewFlow = (ViewFlow)findViewById(R.id.viewflow);
    16         viewFlow.setAdapter(new ImageAdapter(this));
    17         // 实际图片张数, 我的ImageAdapter实际图片张数为3
    18         viewFlow.setmSideBuffer(3); 
    19         
    20         CircleFlowIndicator indic = (CircleFlowIndicator) findViewById(R.id.viewflowindic);
    21         viewFlow.setFlowIndicator(indic);
    22         viewFlow.setTimeSpan(4500);
    23         viewFlow.setSelection(3*1000);    //设置初始位置
    24         viewFlow.startAutoFlowTimer();  //启动自动播放
    25     }
    26 }
    package org.taptwo.android.widget;
    
    import java.util.ArrayList;
    import java.util.LinkedList;
    
    import com.viewflowtest.cjy.R;
    
    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 = 3000;
        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 (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 (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) {
            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 setmSideBuffer(int mSideBuffer) {
            this.mSideBuffer = mSideBuffer;
        }
    }
    package org.taptwo.android.widget;
    
    
    import com.viewflowtest.cjy.R;
    
    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.AnimationUtils;
    import android.view.animation.Animation.AnimationListener;
    
    
    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) {
                        // 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) {
        }
    }
    View Code
    package org.taptwo.android.widget;
    
    import org.taptwo.android.widget.ViewFlow.ViewSwitchListener;
    
    /**
     * 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 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);
    }

    values/attrs.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>
  • 相关阅读:
    LabVIEW(数据库连接)
    单片机之静态局部变量static
    LabVIEW(数据库自动编号)
    为什么大电容滤低频,小电容滤高频 ?(转载)
    电容、频率的一点小关系(项目有关)
    深入理解计算机系统 第三部分程序间的交互和通信
    深入理解计算机系统chapter9
    深入理解计算机系统chapter8
    深入理解计算机系统chapter7
    深入理解计算机系统chapter6
  • 原文地址:https://www.cnblogs.com/androidsj/p/4710989.html
Copyright © 2011-2022 走看看