BVB Source Codes

android-viewflow Show ViewFlow.java Source code

Return Download android-viewflow: download ViewFlow.java Source code - Download android-viewflow Source code - Type:.java
  1. /*
  2.  * Copyright (C) 2011 Patrik 脜kerfeldt
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  *              http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  */
  16. package org.taptwo.android.widget;
  17.  
  18. import android.content.Context;
  19. import android.content.res.Configuration;
  20. import android.content.res.TypedArray;
  21. import android.database.DataSetObserver;
  22. import android.util.AttributeSet;
  23. import android.util.Log;
  24. import android.view.*;
  25. import android.view.ViewTreeObserver.OnGlobalLayoutListener;
  26. import android.widget.Adapter;
  27. import android.widget.AdapterView;
  28. import android.widget.Scroller;
  29. import org.taptwo.android.widget.viewflow.R;
  30.  
  31. import java.util.EnumSet;
  32. import java.util.LinkedList;
  33.  
  34. /**
  35.  * A horizontally scrollable {@link ViewGroup} with items populated from an
  36.  * {@link Adapter}. The ViewFlow uses a buffer to store loaded {@link View}s in.
  37.  * The default size of the buffer is 3 elements on both sides of the currently
  38.  * visible {@link View}, making up a total buffer size of 3 * 2 + 1 = 7. The
  39.  * buffer size can be changed using the {@code sidebuffer} xml attribute.
  40.  *
  41.  */
  42. public class ViewFlow extends AdapterView<Adapter> {
  43.  
  44.         private static final int SNAP_VELOCITY = 1000;
  45.         private static final int INVALID_SCREEN = -1;
  46.         private final static int TOUCH_STATE_REST = 0;
  47.         private final static int TOUCH_STATE_SCROLLING = 1;
  48.  
  49.         private LinkedList<View> mLoadedViews;
  50.         private LinkedList<View> mRecycledViews;
  51.         private int mCurrentBufferIndex;
  52.         private int mCurrentAdapterIndex;
  53.         private int mSideBuffer = 2;
  54.         private Scroller mScroller;
  55.         private VelocityTracker mVelocityTracker;
  56.         private int mTouchState = TOUCH_STATE_REST;
  57.         private float mLastMotionX;
  58.         private int mTouchSlop;
  59.         private int mMaximumVelocity;
  60.         private int mCurrentScreen;
  61.         private int mNextScreen = INVALID_SCREEN;
  62.         private boolean mFirstLayout = true;
  63.         private ViewSwitchListener mViewSwitchListener;
  64.         private ViewLazyInitializeListener mViewInitializeListener;
  65.         private EnumSet<LazyInit> mLazyInit = EnumSet.allOf(LazyInit.class);
  66.         private Adapter mAdapter;
  67.         private int mLastScrollDirection;
  68.         private AdapterDataSetObserver mDataSetObserver;
  69.         private FlowIndicator mIndicator;
  70.         private int mLastOrientation = -1;
  71.         /** Extra return value from obtainView: tells you whether the item it returned on the last call was recycled rather than created by the adapter.
  72.          * This is a member because getting a second return value requires an allocation. */
  73.         private boolean mLastObtainedViewWasRecycled = false;
  74.  
  75.         private OnGlobalLayoutListener orientationChangeListener = new OnGlobalLayoutListener() {
  76.  
  77.                 @Override
  78.                 public void onGlobalLayout() {
  79.                         getViewTreeObserver().removeGlobalOnLayoutListener(
  80.                                         orientationChangeListener);
  81.                         setSelection(mCurrentAdapterIndex);
  82.                 }
  83.         };
  84.  
  85.         /**
  86.          * Receives call backs when a new {@link View} has been scrolled to.
  87.          */
  88.         public static interface ViewSwitchListener {
  89.  
  90.                 /**
  91.                  * This method is called when a new View has been scrolled to.
  92.                  *
  93.                  * @param view
  94.                  *                        the {@link View} currently in focus.
  95.                  * @param position
  96.                  *                        The position in the adapter of the {@link View} currently in focus.
  97.                  */
  98.                 void onSwitched(View view, int position);
  99.  
  100.         }
  101.  
  102.         public static interface ViewLazyInitializeListener {
  103.                 void onViewLazyInitialize(View view, int position);
  104.         }
  105.  
  106.         enum LazyInit {
  107.                 LEFT, RIGHT
  108.         }
  109.  
  110.         public ViewFlow(Context context) {
  111.                 super(context);
  112.                 mSideBuffer = 3;
  113.                 init();
  114.         }
  115.  
  116.         public ViewFlow(Context context, int sideBuffer) {
  117.                 super(context);
  118.                 mSideBuffer = sideBuffer;
  119.                 init();
  120.         }
  121.  
  122.         public ViewFlow(Context context, AttributeSet attrs) {
  123.                 super(context, attrs);
  124.                 TypedArray styledAttrs = context.obtainStyledAttributes(attrs,
  125.                                 R.styleable.ViewFlow);
  126.                 mSideBuffer = styledAttrs.getInt(R.styleable.ViewFlow_sidebuffer, 3);
  127.                 init();
  128.         }
  129.  
  130.         private void init() {
  131.                 mLoadedViews = new LinkedList<View>();
  132.                 mRecycledViews = new LinkedList<View>();
  133.                 mScroller = new Scroller(getContext());
  134.                 final ViewConfiguration configuration = ViewConfiguration
  135.                                 .get(getContext());
  136.                 mTouchSlop = configuration.getScaledTouchSlop();
  137.                 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
  138.         }
  139.  
  140.         public void onConfigurationChanged(Configuration newConfig) {
  141.                 if (newConfig.orientation != mLastOrientation) {
  142.                         mLastOrientation = newConfig.orientation;
  143.                         getViewTreeObserver().addOnGlobalLayoutListener(orientationChangeListener);
  144.                 }
  145.         }
  146.  
  147.         public int getViewsCount() {
  148.                 return mAdapter.getCount();
  149.         }
  150.  
  151.         @Override
  152.         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  153.                 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
  154.                 int heightSize = MeasureSpec.getSize(heightMeasureSpec);
  155.                 final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  156.                 final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
  157.  
  158.                 int childWidth = 0;
  159.                 int childHeight = 0;
  160.                 int childState = 0;
  161.  
  162.                 final int widthPadding = getWidthPadding();
  163.                 final int heightPadding = getHeightPadding();
  164.  
  165.                 int count = mAdapter == null ? 0 : mAdapter.getCount();
  166.                 if (count > 0) {
  167.                         final View child = obtainView(0);
  168.                         measureChild(child, widthMeasureSpec, heightMeasureSpec);
  169.                         childWidth = child.getMeasuredWidth();
  170.                         childHeight = child.getMeasuredHeight();
  171.                         childState = child.getMeasuredState();
  172.                         mRecycledViews.add(child);
  173.                 }
  174.  
  175.                 switch (widthMode) {
  176.                         case MeasureSpec.UNSPECIFIED:
  177.                                 widthSize = childWidth + widthPadding;
  178.                                 break;
  179.                         case MeasureSpec.AT_MOST:
  180.                                 widthSize = (childWidth + widthPadding) | childState;
  181.                                 break;
  182.                         case MeasureSpec.EXACTLY:
  183.                                 if (widthSize < childWidth + widthPadding)
  184.                                         widthSize |= MEASURED_STATE_TOO_SMALL;
  185.                                 break;
  186.                 }
  187.                 switch (heightMode) {
  188.                         case MeasureSpec.UNSPECIFIED:
  189.                                 heightSize = childHeight + heightPadding;
  190.                                 break;
  191.                         case MeasureSpec.AT_MOST:
  192.                                 heightSize = (childHeight + heightPadding) | (childState >> MEASURED_HEIGHT_STATE_SHIFT);
  193.                                 break;
  194.                         case MeasureSpec.EXACTLY:
  195.                                 if (heightSize < childHeight + heightPadding)
  196.                                         heightSize |= MEASURED_STATE_TOO_SMALL;
  197.                                 break;
  198.                 }
  199.  
  200.                 if (heightMode == MeasureSpec.UNSPECIFIED) {
  201.                         heightSize = heightPadding + childHeight;
  202.                 } else {
  203.                         heightSize |= (childState&MEASURED_STATE_MASK);
  204.                 }
  205.  
  206.                 setMeasuredDimension(widthSize, heightSize);
  207.         }
  208.  
  209.         private int getWidthPadding() {
  210.                 return getPaddingLeft() + getPaddingRight() + getHorizontalFadingEdgeLength() * 2;
  211.         }
  212.  
  213.         public int getChildWidth() {
  214.                 return getWidth() - getWidthPadding();
  215.         }
  216.  
  217.         private int getHeightPadding() {
  218.                 return getPaddingTop() + getPaddingBottom();
  219.         }
  220.  
  221.         public int getChildHeight() {
  222.                 return getHeight() - getHeightPadding();
  223.         }
  224.  
  225.         @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  226.                 super.onSizeChanged(w, h, oldw, oldh);
  227.  
  228.                 final int count = getChildCount();
  229.                 for (int i = 0; i < count ; ++i) {
  230.                         final View child = getChildAt(i);
  231.                         child.measure(MeasureSpec.makeMeasureSpec(getChildWidth(), MeasureSpec.EXACTLY),
  232.                                         MeasureSpec.makeMeasureSpec(getChildHeight(), MeasureSpec.EXACTLY));
  233.                 }
  234.  
  235.                 if (mFirstLayout) {
  236.                         mScroller.startScroll(0, 0, mCurrentScreen * getChildWidth(), 0, 0);
  237.                         mFirstLayout = false;
  238.                 }
  239.         }
  240.  
  241.         @Override
  242.         protected void onLayout(boolean changed, int l, int t, int r, int b) {
  243.                 int childLeft = getPaddingLeft() + getHorizontalFadingEdgeLength();
  244.  
  245.                 final int count = getChildCount();
  246.                 for (int i = 0; i < count; i++) {
  247.                         final View child = getChildAt(i);
  248.                         if (child.getVisibility() != View.GONE) {
  249.                                 final int childWidth = child.getMeasuredWidth();
  250.                                 child.layout(childLeft, getPaddingTop(), childLeft + childWidth,
  251.                                                 getPaddingTop() + child.getMeasuredHeight());
  252.                                 childLeft += childWidth;
  253.                         }
  254.                 }
  255.         }
  256.  
  257.         @Override protected float getTopFadingEdgeStrength() {
  258.                 return 0.0f;
  259.         }
  260.  
  261.         @Override protected float getBottomFadingEdgeStrength() {
  262.                 return 0.0f;
  263.         }
  264.  
  265.         @Override protected float getLeftFadingEdgeStrength() {
  266.                 // always do the fading edge
  267.                 return 1.0f;
  268.         }
  269.  
  270.         @Override protected float getRightFadingEdgeStrength() {
  271.                 // always do the fading edge
  272.                 return 1.0f;
  273.         }
  274.  
  275.         @Override
  276.         public boolean onInterceptTouchEvent(MotionEvent ev) {
  277.                 if (getChildCount() == 0)
  278.                         return false;
  279.  
  280.                 if (mVelocityTracker == null) {
  281.                         mVelocityTracker = VelocityTracker.obtain();
  282.                 }
  283.                 mVelocityTracker.addMovement(ev);
  284.  
  285.                 final int action = ev.getAction();
  286.                 final float x = ev.getX();
  287.  
  288.                 switch (action) {
  289.                 case MotionEvent.ACTION_DOWN:
  290.                         /*
  291.                          * If being flinged and user touches, stop the fling. isFinished
  292.                          * will be false if being flinged.
  293.                          */
  294.                         if (!mScroller.isFinished()) {
  295.                                 mScroller.abortAnimation();
  296.                         }
  297.  
  298.                         // Remember where the motion event started
  299.                         mLastMotionX = x;
  300.  
  301.                         mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST
  302.                                         : TOUCH_STATE_SCROLLING;
  303.  
  304.                         break;
  305.  
  306.                 case MotionEvent.ACTION_MOVE:
  307.                         final int deltaX = (int) (mLastMotionX - x);
  308.  
  309.                         boolean xMoved = Math.abs(deltaX) > mTouchSlop;
  310.  
  311.                         if (xMoved) {
  312.                                 // Scroll if the user moved far enough along the X axis
  313.                                 mTouchState = TOUCH_STATE_SCROLLING;
  314.  
  315.                                 if (mViewInitializeListener != null)
  316.                                         initializeView(deltaX);
  317.                         }
  318.  
  319.                         if (mTouchState == TOUCH_STATE_SCROLLING) {
  320.                                 // Scroll to follow the motion event
  321.  
  322.                                 mLastMotionX = x;
  323.  
  324.                                 final int scrollX = getScrollX();
  325.                                 if (deltaX < 0) {
  326.                                         if (scrollX > 0) {
  327.                                                 scrollBy(Math.max(-scrollX, deltaX), 0);
  328.                                         }
  329.                                 } else if (deltaX > 0) {
  330.                                         final int availableToScroll = getChildAt(
  331.                                                         getChildCount() - 1).getRight()
  332.                                                         - getPaddingRight() - getHorizontalFadingEdgeLength()
  333.                                                         - scrollX - getWidth();
  334.                                         if (availableToScroll > 0) {
  335.                                                 scrollBy(Math.min(availableToScroll, deltaX), 0);
  336.                                         }
  337.                                 }
  338.                                 return true;
  339.                         }
  340.                         break;
  341.  
  342.                 case MotionEvent.ACTION_UP:
  343.                         if (mTouchState == TOUCH_STATE_SCROLLING) {
  344.                                 final VelocityTracker velocityTracker = mVelocityTracker;
  345.                                 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
  346.                                 int velocityX = (int) velocityTracker.getXVelocity();
  347.  
  348.                                 if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
  349.                                         // Fling hard enough to move left
  350.                                         snapToScreen(mCurrentScreen - 1);
  351.                                 } else if (velocityX < -SNAP_VELOCITY
  352.                                                 && mCurrentScreen < getChildCount() - 1) {
  353.                                         // Fling hard enough to move right
  354.                                         snapToScreen(mCurrentScreen + 1);
  355.                                 } else {
  356.                                         snapToDestination();
  357.                                 }
  358.  
  359.                                 if (mVelocityTracker != null) {
  360.                                         mVelocityTracker.recycle();
  361.                                         mVelocityTracker = null;
  362.                                 }
  363.                         }
  364.  
  365.                         mTouchState = TOUCH_STATE_REST;
  366.  
  367.                         break;
  368.                 case MotionEvent.ACTION_CANCEL:
  369.                         mTouchState = TOUCH_STATE_REST;
  370.                 }
  371.                 return false;
  372.         }
  373.  
  374.         @Override
  375.         public boolean onTouchEvent(MotionEvent ev) {
  376.                 if (getChildCount() == 0)
  377.                         return false;
  378.  
  379.                 if (mVelocityTracker == null) {
  380.                         mVelocityTracker = VelocityTracker.obtain();
  381.                 }
  382.                 mVelocityTracker.addMovement(ev);
  383.  
  384.                 final int action = ev.getAction();
  385.                 final float x = ev.getX();
  386.  
  387.                 switch (action) {
  388.                 case MotionEvent.ACTION_DOWN:
  389.                         /*
  390.                          * If being flinged and user touches, stop the fling. isFinished
  391.                          * will be false if being flinged.
  392.                          */
  393.                         if (!mScroller.isFinished()) {
  394.                                 mScroller.abortAnimation();
  395.                         }
  396.  
  397.                         // Remember where the motion event started
  398.                         mLastMotionX = x;
  399.  
  400.                         mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST
  401.                                         : TOUCH_STATE_SCROLLING;
  402.  
  403.                         break;
  404.  
  405.                 case MotionEvent.ACTION_MOVE:
  406.                         final int deltaX = (int) (mLastMotionX - x);
  407.  
  408.                         boolean xMoved = Math.abs(deltaX) > mTouchSlop;
  409.  
  410.                         if (xMoved) {
  411.                                 // Scroll if the user moved far enough along the X axis
  412.                                 mTouchState = TOUCH_STATE_SCROLLING;
  413.  
  414.                                 if (mViewInitializeListener != null)
  415.                                         initializeView(deltaX);
  416.                         }
  417.  
  418.                         if (mTouchState == TOUCH_STATE_SCROLLING) {
  419.                                 // Scroll to follow the motion event
  420.  
  421.                                 mLastMotionX = x;
  422.  
  423.                                 final int scrollX = getScrollX();
  424.                                 if (deltaX < 0) {
  425.                                         if (scrollX > 0) {
  426.                                                 scrollBy(Math.max(-scrollX, deltaX), 0);
  427.                                         }
  428.                                 } else if (deltaX > 0) {
  429.                                         final int availableToScroll = getChildAt(
  430.                                                         getChildCount() - 1).getRight()
  431.                                                         - getPaddingRight() - getHorizontalFadingEdgeLength()
  432.                                                         - scrollX - getChildWidth();
  433.                                         if (availableToScroll > 0) {
  434.                                                 scrollBy(Math.min(availableToScroll, deltaX), 0);
  435.                                         }
  436.                                 }
  437.                                 return true;
  438.                         }
  439.                         break;
  440.  
  441.                 case MotionEvent.ACTION_UP:
  442.                         if (mTouchState == TOUCH_STATE_SCROLLING) {
  443.                                 final VelocityTracker velocityTracker = mVelocityTracker;
  444.                                 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
  445.                                 int velocityX = (int) velocityTracker.getXVelocity();
  446.  
  447.                                 if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
  448.                                         // Fling hard enough to move left
  449.                                         snapToScreen(mCurrentScreen - 1);
  450.                                 } else if (velocityX < -SNAP_VELOCITY
  451.                                                 && mCurrentScreen < getChildCount() - 1) {
  452.                                         // Fling hard enough to move right
  453.                                         snapToScreen(mCurrentScreen + 1);
  454.                                 } else {
  455.                                         snapToDestination();
  456.                                 }
  457.  
  458.                                 if (mVelocityTracker != null) {
  459.                                         mVelocityTracker.recycle();
  460.                                         mVelocityTracker = null;
  461.                                 }
  462.                         }
  463.  
  464.                         mTouchState = TOUCH_STATE_REST;
  465.  
  466.                         break;
  467.                 case MotionEvent.ACTION_CANCEL:
  468.                         snapToDestination();
  469.                         mTouchState = TOUCH_STATE_REST;
  470.                 }
  471.                 return true;
  472.         }
  473.  
  474.         private void initializeView(final float direction) {
  475.                 if (direction > 0) {
  476.                         if (mLazyInit.contains(LazyInit.RIGHT)) {
  477.                                 mLazyInit.remove(LazyInit.RIGHT);
  478.                                 if (mCurrentBufferIndex+1 < mLoadedViews.size())
  479.                                         mViewInitializeListener.onViewLazyInitialize(mLoadedViews.get(mCurrentBufferIndex + 1), mCurrentAdapterIndex + 1);
  480.                         }
  481.                 } else {
  482.                         if (mLazyInit.contains(LazyInit.LEFT)) {
  483.                                 mLazyInit.remove(LazyInit.LEFT);
  484.                                 if (mCurrentBufferIndex > 0)
  485.                                         mViewInitializeListener.onViewLazyInitialize(mLoadedViews.get(mCurrentBufferIndex - 1), mCurrentAdapterIndex - 1);
  486.                         }
  487.                 }
  488.         }
  489.  
  490.         @Override
  491.         protected void onScrollChanged(int h, int v, int oldh, int oldv) {
  492.                 super.onScrollChanged(h, v, oldh, oldv);
  493.                 if (mIndicator != null) {
  494.                         /*
  495.                          * The actual horizontal scroll origin does typically not match the
  496.                          * perceived one. Therefore, we need to calculate the perceived
  497.                          * horizontal scroll origin here, since we use a view buffer.
  498.                          */
  499.                         int hPerceived = h + (mCurrentAdapterIndex - mCurrentBufferIndex)
  500.                                         * getChildWidth();
  501.                         mIndicator.onScrolled(hPerceived, v, oldh, oldv);
  502.                 }
  503.         }
  504.  
  505.         private void snapToDestination() {
  506.                 final int screenWidth = getChildWidth();
  507.                 final int whichScreen = (getScrollX() + (screenWidth / 2))
  508.                                 / screenWidth;
  509.  
  510.                 snapToScreen(whichScreen);
  511.         }
  512.  
  513.         private void snapToScreen(int whichScreen) {
  514.                 mLastScrollDirection = whichScreen - mCurrentScreen;
  515.                 if (!mScroller.isFinished())
  516.                         return;
  517.  
  518.                 whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
  519.  
  520.                 mNextScreen = whichScreen;
  521.  
  522.                 final int newX = whichScreen * getChildWidth();
  523.                 final int delta = newX - getScrollX();
  524.                 mScroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta) * 2);
  525.                 invalidate();
  526.         }
  527.  
  528.         @Override
  529.         public void computeScroll() {
  530.                 if (mScroller.computeScrollOffset()) {
  531.                         scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
  532.                         postInvalidate();
  533.                 } else if (mNextScreen != INVALID_SCREEN) {
  534.                         mCurrentScreen = Math.max(0,
  535.                                         Math.min(mNextScreen, getChildCount() - 1));
  536.                         mNextScreen = INVALID_SCREEN;
  537.                         post(new Runnable() {
  538.                                 @Override public void run() {
  539.                                         postViewSwitched(mLastScrollDirection);
  540.                                 }
  541.                         });
  542.                 }
  543.         }
  544.  
  545.         /**
  546.          * Scroll to the {@link View} in the view buffer specified by the index.
  547.          *
  548.          * @param indexInBuffer
  549.          *                        Index of the view in the view buffer.
  550.          */
  551.         private void setVisibleView(int indexInBuffer, boolean uiThread) {
  552.                 mCurrentScreen = Math.max(0,
  553.                                 Math.min(indexInBuffer, getChildCount() - 1));
  554.                 int dx = (mCurrentScreen * getChildWidth()) - mScroller.getCurrX();
  555.                 mScroller.startScroll(mScroller.getCurrX(), mScroller.getCurrY(), dx,
  556.                                 0, 0);
  557.                 if(dx == 0)
  558.                         onScrollChanged(mScroller.getCurrX() + dx, mScroller.getCurrY(), mScroller.getCurrX() + dx, mScroller.getCurrY());
  559.                 if (uiThread)
  560.                         invalidate();
  561.                 else
  562.                         postInvalidate();
  563.         }
  564.  
  565.         /**
  566.          * Set the listener that will receive notifications every time the {code
  567.          * ViewFlow} scrolls.
  568.          *
  569.          * @param l
  570.          *                        the scroll listener
  571.          */
  572.         public void setOnViewSwitchListener(ViewSwitchListener l) {
  573.                 mViewSwitchListener = l;
  574.         }
  575.  
  576.         public void setOnViewLazyInitializeListener(ViewLazyInitializeListener l) {
  577.                 mViewInitializeListener = l;
  578.         }
  579.  
  580.         @Override
  581.         public Adapter getAdapter() {
  582.                 return mAdapter;
  583.         }
  584.  
  585.         @Override
  586.         public void setAdapter(Adapter adapter) {
  587.                 setAdapter(adapter, 0);
  588.         }
  589.        
  590.         public void setAdapter(Adapter adapter, int initialPosition) {
  591.                 if (mAdapter != null) {
  592.                         mAdapter.unregisterDataSetObserver(mDataSetObserver);
  593.                 }
  594.  
  595.                 mAdapter = adapter;
  596.  
  597.                 if (mAdapter != null) {
  598.                         mDataSetObserver = new AdapterDataSetObserver();
  599.                         mAdapter.registerDataSetObserver(mDataSetObserver);
  600.  
  601.                 }
  602.                 if (mAdapter == null || mAdapter.getCount() == 0)
  603.                         return;
  604.                
  605.                 setSelection(initialPosition);         
  606.         }
  607.        
  608.         @Override
  609.         public View getSelectedView() {
  610.                 return (mCurrentBufferIndex < mLoadedViews.size() ? mLoadedViews
  611.                                 .get(mCurrentBufferIndex) : null);
  612.         }
  613.  
  614.         @Override
  615.         public int getSelectedItemPosition() {
  616.                 return mCurrentAdapterIndex;
  617.         }
  618.  
  619.         /**
  620.          * Set the FlowIndicator
  621.          *
  622.          * @param flowIndicator
  623.          */
  624.         public void setFlowIndicator(FlowIndicator flowIndicator) {
  625.                 mIndicator = flowIndicator;
  626.                 mIndicator.setViewFlow(this);
  627.         }
  628.  
  629.         protected void recycleViews() {
  630.                 while (!mLoadedViews.isEmpty())
  631.                         recycleView(mLoadedViews.remove());
  632.         }
  633.  
  634.         protected void recycleView(View v) {
  635.                 if (v == null)
  636.                         return;
  637.                 mRecycledViews.addFirst(v);
  638.                 detachViewFromParent(v);
  639.         }
  640.  
  641.         protected View getRecycledView() {
  642.                 return (mRecycledViews.isEmpty() ? null : mRecycledViews.remove());
  643.         }
  644.  
  645.         @Override
  646.         public void setSelection(int position) {
  647.                 mNextScreen = INVALID_SCREEN;
  648.                 mScroller.forceFinished(true);
  649.                 if (mAdapter == null)
  650.                         return;
  651.                
  652.                 position = Math.max(position, 0);
  653.                 position = Math.min(position, mAdapter.getCount()-1);
  654.  
  655.                 recycleViews();
  656.  
  657.                 View currentView = makeAndAddView(position, true);
  658.                 mLoadedViews.addLast(currentView);
  659.  
  660.                 if (mViewInitializeListener != null)
  661.                         mViewInitializeListener.onViewLazyInitialize(currentView, position);
  662.  
  663.                 for(int offset = 1; mSideBuffer - offset >= 0; offset++) {
  664.                         int leftIndex = position - offset;
  665.                         int rightIndex = position + offset;
  666.                         if(leftIndex >= 0)
  667.                                 mLoadedViews.addFirst(makeAndAddView(leftIndex, false));
  668.                         if(rightIndex < mAdapter.getCount())
  669.                                 mLoadedViews.addLast(makeAndAddView(rightIndex, true));
  670.                 }
  671.  
  672.                 mCurrentBufferIndex = mLoadedViews.indexOf(currentView);
  673.                 mCurrentAdapterIndex = position;
  674.  
  675.                 requestLayout();
  676.                 setVisibleView(mCurrentBufferIndex, false);
  677.                 if (mIndicator != null) {
  678.                         mIndicator.onSwitched(currentView, mCurrentAdapterIndex);
  679.                 }
  680.                 if (mViewSwitchListener != null) {
  681.                         mViewSwitchListener.onSwitched(currentView, mCurrentAdapterIndex);
  682.                 }
  683.         }
  684.  
  685.         private void resetFocus() {
  686.                 logBuffer();
  687.                 recycleViews();
  688.                 removeAllViewsInLayout();
  689.                 mLazyInit.addAll(EnumSet.allOf(LazyInit.class));
  690.  
  691.                 for (int i = Math.max(0, mCurrentAdapterIndex - mSideBuffer); i < Math
  692.                                 .min(mAdapter.getCount(), mCurrentAdapterIndex + mSideBuffer
  693.                                                 + 1); i++) {
  694.                         mLoadedViews.addLast(makeAndAddView(i, true));
  695.                         if (i == mCurrentAdapterIndex) {
  696.                                 mCurrentBufferIndex = mLoadedViews.size() - 1;
  697.                                 if (mViewInitializeListener != null)
  698.                                         mViewInitializeListener.onViewLazyInitialize(mLoadedViews.getLast(), mCurrentAdapterIndex);
  699.                         }
  700.                 }
  701.                 logBuffer();
  702.                 requestLayout();
  703.         }
  704.  
  705.         private void postViewSwitched(int direction) {
  706.                 if (direction == 0)
  707.                         return;
  708.  
  709.                 if (direction > 0) { // to the right
  710.                         mCurrentAdapterIndex++;
  711.                         mCurrentBufferIndex++;
  712.                         mLazyInit.remove(LazyInit.LEFT);
  713.                         mLazyInit.add(LazyInit.RIGHT);
  714.  
  715.                         // Recycle view outside buffer range
  716.                         if (mCurrentAdapterIndex > mSideBuffer) {
  717.                                 recycleView(mLoadedViews.removeFirst());
  718.                                 mCurrentBufferIndex--;
  719.                         }
  720.  
  721.                         // Add new view to buffer
  722.                         int newBufferIndex = mCurrentAdapterIndex + mSideBuffer;
  723.                         if (newBufferIndex < mAdapter.getCount())
  724.                                 mLoadedViews.addLast(makeAndAddView(newBufferIndex, true));
  725.  
  726.                 } else { // to the left
  727.                         mCurrentAdapterIndex--;
  728.                         mCurrentBufferIndex--;
  729.                         mLazyInit.add(LazyInit.LEFT);
  730.                         mLazyInit.remove(LazyInit.RIGHT);
  731.  
  732.                         // Recycle view outside buffer range
  733.                         if (mAdapter.getCount() - 1 - mCurrentAdapterIndex > mSideBuffer) {
  734.                                 recycleView(mLoadedViews.removeLast());
  735.                         }
  736.  
  737.                         // Add new view to buffer
  738.                         int newBufferIndex = mCurrentAdapterIndex - mSideBuffer;
  739.                         if (newBufferIndex > -1) {
  740.                                 mLoadedViews.addFirst(makeAndAddView(newBufferIndex, false));
  741.                                 mCurrentBufferIndex++;
  742.                         }
  743.  
  744.                 }
  745.  
  746.                 requestLayout();
  747.                 setVisibleView(mCurrentBufferIndex, true);
  748.                 if (mIndicator != null) {
  749.                         mIndicator.onSwitched(mLoadedViews.get(mCurrentBufferIndex),
  750.                                         mCurrentAdapterIndex);
  751.                 }
  752.                 if (mViewSwitchListener != null) {
  753.                         mViewSwitchListener
  754.                                         .onSwitched(mLoadedViews.get(mCurrentBufferIndex),
  755.                                                         mCurrentAdapterIndex);
  756.                 }
  757.                 logBuffer();
  758.         }
  759.  
  760.         @Override protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) {
  761.                 LayoutParams lp = child.getLayoutParams();
  762.                 final int childWidthSpec = getChildMeasureSpec(parentWidthMeasureSpec, getWidthPadding(), lp.width);
  763.                 final int childHeightSpec = getChildMeasureSpec(parentHeightMeasureSpec, getHeightPadding(), lp.height);
  764.                 child.measure(childWidthSpec, childHeightSpec);
  765.         }
  766.  
  767.         private View setupChild(View child, boolean addToEnd, boolean recycle) {
  768.                 final LayoutParams lp = child.getLayoutParams();
  769.                 child.measure(MeasureSpec.makeMeasureSpec(getChildWidth(), MeasureSpec.EXACTLY),
  770.                                 MeasureSpec.makeMeasureSpec(getChildHeight(), MeasureSpec.EXACTLY));
  771.                 if (recycle)
  772.                         attachViewToParent(child, (addToEnd ? -1 : 0), lp);
  773.                 else
  774.                         addViewInLayout(child, (addToEnd ? -1 : 0), lp, true);
  775.                 return child;
  776.         }
  777.  
  778.         private View makeAndAddView(int position, boolean addToEnd) {
  779.                 View view = obtainView(position);
  780.                 return setupChild(view, addToEnd, mLastObtainedViewWasRecycled);
  781.         }
  782.  
  783.         private View obtainView(int position) {
  784.                 View convertView = getRecycledView();
  785.                 View view = mAdapter.getView(position, convertView, this);
  786.                 if(view != convertView && convertView != null)
  787.                         mRecycledViews.add(convertView);
  788.                 mLastObtainedViewWasRecycled = (view == convertView);
  789.                 ViewGroup.LayoutParams p = view.getLayoutParams();
  790.                 if (p == null) {
  791.                         p = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
  792.                         view.setLayoutParams(p);
  793.                 }
  794.                 return view;
  795.         }
  796.  
  797.         class AdapterDataSetObserver extends DataSetObserver {
  798.  
  799.                 @Override
  800.                 public void onChanged() {
  801.                         View v = getChildAt(mCurrentBufferIndex);
  802.                         if (v != null) {
  803.                                 for (int index = 0; index < mAdapter.getCount(); index++) {
  804.                                         if (v.equals(mAdapter.getItem(index))) {
  805.                                                 mCurrentAdapterIndex = index;
  806.                                                 break;
  807.                                         }
  808.                                 }
  809.                         }
  810.                         resetFocus();
  811.                 }
  812.  
  813.                 @Override
  814.                 public void onInvalidated() {
  815.                         // Not yet implemented!
  816.                 }
  817.  
  818.         }
  819.  
  820.         private void logBuffer() {
  821.  
  822.                 Log.d("viewflow", "Size of mLoadedViews: " + mLoadedViews.size() +
  823.                                 ", Size of mRecycledViews: " + mRecycledViews.size() +
  824.                                 ", X: " + mScroller.getCurrX() + ", Y: " + mScroller.getCurrY());
  825.                 Log.d("viewflow", "IndexInAdapter: " + mCurrentAdapterIndex
  826.                                 + ", IndexInBuffer: " + mCurrentBufferIndex);
  827.         }
  828. }
  829.  
downloadViewFlow.java Source code - Download android-viewflow Source code
Related Source Codes/Software:
onionshare - Securely and anonymously share a file of any size ... 2017-04-16
Android-ItemTouchHelper-Demo - Basic example of using ItemTouchHelper to add drag... 2017-04-16
later - A javascript library for defining recurring schedu... 2017-04-16
x64dbg - An open-source x64/x32 debugger for windows. ... 2017-04-16
pencil - Multiplatform GUI Prototyping/Wireframing 2017-04-16
css-in-js - React: CSS in JS techniques comparison. 2017-04-16
st2 - StackStorm (aka IFTTT for Ops) is event-driven aut... 2017-04-16
WNXHuntForCity - City find By Objective - C 2017-04-16
tmate - Instant Terminal Sharing ht... 2017-04-16
dat.gui - dat.gui is a lightweight controller library for Ja... 2017-04-16
EventStore - The open-source, functional database with Complex ... 2017-04-23
screencat - 2017-04-23
viz.js - A hack to put Graphviz on the web. ... 2017-04-23
JKeyboardPanelSwitch - For resolve the layout conflict when keybord & amp... 2017-04-23
mama2 - Mother plan - all firewood high flame 2017-04-23
BlurEffectForAndroidDesign - Sample to show how to implement blur graphical tri... 2017-04-23
sphinx_rtd_theme - Sphinx theme for readthedocs.org 2017-04-23
rouge - A pure-ruby code highlighter that is compatible wi... 2017-04-23
spring-security-oauth - Support for adding OAuth1(a) and OAuth2 features (... 2017-04-23
Toucan - Fabulous Image Processing in Swift 2017-04-23

 Back to top