diff options
Diffstat (limited to 'platform/android/src/com/artifex/mupdfdemo/ReaderView.java')
-rw-r--r-- | platform/android/src/com/artifex/mupdfdemo/ReaderView.java | 936 |
1 files changed, 0 insertions, 936 deletions
diff --git a/platform/android/src/com/artifex/mupdfdemo/ReaderView.java b/platform/android/src/com/artifex/mupdfdemo/ReaderView.java deleted file mode 100644 index 65d8f665..00000000 --- a/platform/android/src/com/artifex/mupdfdemo/ReaderView.java +++ /dev/null @@ -1,936 +0,0 @@ -package com.artifex.mupdfdemo; - -import java.util.LinkedList; -import java.util.NoSuchElementException; - -import android.app.Activity; -import android.app.ActivityManager; -import android.app.AlertDialog; -import android.content.DialogInterface; - -import android.content.Context; -import android.graphics.Point; -import android.graphics.Rect; -import android.util.AttributeSet; -import android.util.SparseArray; -import android.view.GestureDetector; -import android.view.MotionEvent; -import android.view.ScaleGestureDetector; -import android.view.View; -import android.widget.Adapter; -import android.widget.AdapterView; -import android.widget.Scroller; - -public class ReaderView - extends AdapterView<Adapter> - implements GestureDetector.OnGestureListener, ScaleGestureDetector.OnScaleGestureListener, Runnable { - private static final int MOVING_DIAGONALLY = 0; - private static final int MOVING_LEFT = 1; - private static final int MOVING_RIGHT = 2; - private static final int MOVING_UP = 3; - private static final int MOVING_DOWN = 4; - - private static final int FLING_MARGIN = 100; - private static final int GAP = 20; - - private static final float MIN_SCALE = 1.0f; - private static final float MAX_SCALE = 5.0f; - private static final float REFLOW_SCALE_FACTOR = 0.5f; - - private static final boolean HORIZONTAL_SCROLLING = true; - - private Adapter mAdapter; - private int mCurrent; // Adapter's index for the current view - private boolean mResetLayout; - private final SparseArray<View> - mChildViews = new SparseArray<View>(3); - // Shadows the children of the adapter view - // but with more sensible indexing - private final LinkedList<View> - mViewCache = new LinkedList<View>(); - private boolean mUserInteracting; // Whether the user is interacting - private boolean mScaling; // Whether the user is currently pinch zooming - private float mScale = 1.0f; - private int mXScroll; // Scroll amounts recorded from events. - private int mYScroll; // and then accounted for in onLayout - private boolean mReflow = false; - private boolean mReflowChanged = false; - private final GestureDetector - mGestureDetector; - private final ScaleGestureDetector - mScaleGestureDetector; - private final Scroller mScroller; - private final Stepper mStepper; - private int mScrollerLastX; - private int mScrollerLastY; - private float mLastScaleFocusX; - private float mLastScaleFocusY; - - static abstract class ViewMapper { - abstract void applyToView(View view); - } - - public ReaderView(Context context) { - super(context); - mGestureDetector = new GestureDetector(this); - mScaleGestureDetector = new ScaleGestureDetector(context, this); - mScroller = new Scroller(context); - mStepper = new Stepper(this, this); - } - - public ReaderView(Context context, AttributeSet attrs) { - super(context, attrs); - - // "Edit mode" means when the View is being displayed in the Android GUI editor. (this class - // is instantiated in the IDE, so we need to be a bit careful what we do). - if (isInEditMode()) - { - mGestureDetector = null; - mScaleGestureDetector = null; - mScroller = null; - mStepper = null; - } - else - { - mGestureDetector = new GestureDetector(this); - mScaleGestureDetector = new ScaleGestureDetector(context, this); - mScroller = new Scroller(context); - mStepper = new Stepper(this, this); - } - } - - public ReaderView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - mGestureDetector = new GestureDetector(this); - mScaleGestureDetector = new ScaleGestureDetector(context, this); - mScroller = new Scroller(context); - mStepper = new Stepper(this, this); - } - - public int getDisplayedViewIndex() { - return mCurrent; - } - - public void setDisplayedViewIndex(int i) { - if (0 <= i && i < mAdapter.getCount()) { - onMoveOffChild(mCurrent); - mCurrent = i; - onMoveToChild(i); - mResetLayout = true; - requestLayout(); - } - } - - public void moveToNext() { - View v = mChildViews.get(mCurrent+1); - if (v != null) - slideViewOntoScreen(v); - } - - public void moveToPrevious() { - View v = mChildViews.get(mCurrent-1); - if (v != null) - slideViewOntoScreen(v); - } - - // When advancing down the page, we want to advance by about - // 90% of a screenful. But we'd be happy to advance by between - // 80% and 95% if it means we hit the bottom in a whole number - // of steps. - private int smartAdvanceAmount(int screenHeight, int max) { - int advance = (int)(screenHeight * 0.9 + 0.5); - int leftOver = max % advance; - int steps = max / advance; - if (leftOver == 0) { - // We'll make it exactly. No adjustment - } else if ((float)leftOver / steps <= screenHeight * 0.05) { - // We can adjust up by less than 5% to make it exact. - advance += (int)((float)leftOver/steps + 0.5); - } else { - int overshoot = advance - leftOver; - if ((float)overshoot / steps <= screenHeight * 0.1) { - // We can adjust down by less than 10% to make it exact. - advance -= (int)((float)overshoot/steps + 0.5); - } - } - if (advance > max) - advance = max; - return advance; - } - - public void smartMoveForwards() { - View v = mChildViews.get(mCurrent); - if (v == null) - return; - - // The following code works in terms of where the screen is on the views; - // so for example, if the currentView is at (-100,-100), the visible - // region would be at (100,100). If the previous page was (2000, 3000) in - // size, the visible region of the previous page might be (2100 + GAP, 100) - // (i.e. off the previous page). This is different to the way the rest of - // the code in this file is written, but it's easier for me to think about. - // At some point we may refactor this to fit better with the rest of the - // code. - - // screenWidth/Height are the actual width/height of the screen. e.g. 480/800 - int screenWidth = getWidth(); - int screenHeight = getHeight(); - // We might be mid scroll; we want to calculate where we scroll to based on - // where this scroll would end, not where we are now (to allow for people - // bashing 'forwards' very fast. - int remainingX = mScroller.getFinalX() - mScroller.getCurrX(); - int remainingY = mScroller.getFinalY() - mScroller.getCurrY(); - // right/bottom is in terms of pixels within the scaled document; e.g. 1000 - int top = -(v.getTop() + mYScroll + remainingY); - int right = screenWidth -(v.getLeft() + mXScroll + remainingX); - int bottom = screenHeight+top; - // docWidth/Height are the width/height of the scaled document e.g. 2000x3000 - int docWidth = v.getMeasuredWidth(); - int docHeight = v.getMeasuredHeight(); - - int xOffset, yOffset; - if (bottom >= docHeight) { - // We are flush with the bottom. Advance to next column. - if (right + screenWidth > docWidth) { - // No room for another column - go to next page - View nv = mChildViews.get(mCurrent+1); - if (nv == null) // No page to advance to - return; - int nextTop = -(nv.getTop() + mYScroll + remainingY); - int nextLeft = -(nv.getLeft() + mXScroll + remainingX); - int nextDocWidth = nv.getMeasuredWidth(); - int nextDocHeight = nv.getMeasuredHeight(); - - // Allow for the next page maybe being shorter than the screen is high - yOffset = (nextDocHeight < screenHeight ? ((nextDocHeight - screenHeight)>>1) : 0); - - if (nextDocWidth < screenWidth) { - // Next page is too narrow to fill the screen. Scroll to the top, centred. - xOffset = (nextDocWidth - screenWidth)>>1; - } else { - // Reset X back to the left hand column - xOffset = right % screenWidth; - // Adjust in case the previous page is less wide - if (xOffset + screenWidth > nextDocWidth) - xOffset = nextDocWidth - screenWidth; - } - xOffset -= nextLeft; - yOffset -= nextTop; - } else { - // Move to top of next column - xOffset = screenWidth; - yOffset = screenHeight - bottom; - } - } else { - // Advance by 90% of the screen height downwards (in case lines are partially cut off) - xOffset = 0; - yOffset = smartAdvanceAmount(screenHeight, docHeight - bottom); - } - mScrollerLastX = mScrollerLastY = 0; - mScroller.startScroll(0, 0, remainingX - xOffset, remainingY - yOffset, 400); - mStepper.prod(); - } - - public void smartMoveBackwards() { - View v = mChildViews.get(mCurrent); - if (v == null) - return; - - // The following code works in terms of where the screen is on the views; - // so for example, if the currentView is at (-100,-100), the visible - // region would be at (100,100). If the previous page was (2000, 3000) in - // size, the visible region of the previous page might be (2100 + GAP, 100) - // (i.e. off the previous page). This is different to the way the rest of - // the code in this file is written, but it's easier for me to think about. - // At some point we may refactor this to fit better with the rest of the - // code. - - // screenWidth/Height are the actual width/height of the screen. e.g. 480/800 - int screenWidth = getWidth(); - int screenHeight = getHeight(); - // We might be mid scroll; we want to calculate where we scroll to based on - // where this scroll would end, not where we are now (to allow for people - // bashing 'forwards' very fast. - int remainingX = mScroller.getFinalX() - mScroller.getCurrX(); - int remainingY = mScroller.getFinalY() - mScroller.getCurrY(); - // left/top is in terms of pixels within the scaled document; e.g. 1000 - int left = -(v.getLeft() + mXScroll + remainingX); - int top = -(v.getTop() + mYScroll + remainingY); - // docWidth/Height are the width/height of the scaled document e.g. 2000x3000 - int docHeight = v.getMeasuredHeight(); - - int xOffset, yOffset; - if (top <= 0) { - // We are flush with the top. Step back to previous column. - if (left < screenWidth) { - /* No room for previous column - go to previous page */ - View pv = mChildViews.get(mCurrent-1); - if (pv == null) /* No page to advance to */ - return; - int prevDocWidth = pv.getMeasuredWidth(); - int prevDocHeight = pv.getMeasuredHeight(); - - // Allow for the next page maybe being shorter than the screen is high - yOffset = (prevDocHeight < screenHeight ? ((prevDocHeight - screenHeight)>>1) : 0); - - int prevLeft = -(pv.getLeft() + mXScroll); - int prevTop = -(pv.getTop() + mYScroll); - if (prevDocWidth < screenWidth) { - // Previous page is too narrow to fill the screen. Scroll to the bottom, centred. - xOffset = (prevDocWidth - screenWidth)>>1; - } else { - // Reset X back to the right hand column - xOffset = (left > 0 ? left % screenWidth : 0); - if (xOffset + screenWidth > prevDocWidth) - xOffset = prevDocWidth - screenWidth; - while (xOffset + screenWidth*2 < prevDocWidth) - xOffset += screenWidth; - } - xOffset -= prevLeft; - yOffset -= prevTop-prevDocHeight+screenHeight; - } else { - // Move to bottom of previous column - xOffset = -screenWidth; - yOffset = docHeight - screenHeight + top; - } - } else { - // Retreat by 90% of the screen height downwards (in case lines are partially cut off) - xOffset = 0; - yOffset = -smartAdvanceAmount(screenHeight, top); - } - mScrollerLastX = mScrollerLastY = 0; - mScroller.startScroll(0, 0, remainingX - xOffset, remainingY - yOffset, 400); - mStepper.prod(); - } - - public void resetupChildren() { - for (int i = 0; i < mChildViews.size(); i++) - onChildSetup(mChildViews.keyAt(i), mChildViews.valueAt(i)); - } - - public void applyToChildren(ViewMapper mapper) { - for (int i = 0; i < mChildViews.size(); i++) - mapper.applyToView(mChildViews.valueAt(i)); - } - - public void refresh(boolean reflow) { - mReflow = reflow; - mReflowChanged = true; - mResetLayout = true; - - mScale = 1.0f; - mXScroll = mYScroll = 0; - - requestLayout(); - } - - protected void onChildSetup(int i, View v) {} - - protected void onMoveToChild(int i) {} - - protected void onMoveOffChild(int i) {} - - protected void onSettle(View v) {}; - - protected void onUnsettle(View v) {}; - - protected void onNotInUse(View v) {}; - - protected void onScaleChild(View v, Float scale) {}; - - public View getView(int i) { - return mChildViews.get(i); - } - - public View getDisplayedView() { - return mChildViews.get(mCurrent); - } - - public void run() { - if (!mScroller.isFinished()) { - mScroller.computeScrollOffset(); - int x = mScroller.getCurrX(); - int y = mScroller.getCurrY(); - mXScroll += x - mScrollerLastX; - mYScroll += y - mScrollerLastY; - mScrollerLastX = x; - mScrollerLastY = y; - requestLayout(); - mStepper.prod(); - } - else if (!mUserInteracting) { - // End of an inertial scroll and the user is not interacting. - // The layout is stable - View v = mChildViews.get(mCurrent); - if (v != null) - postSettle(v); - } - } - - public boolean onDown(MotionEvent arg0) { - mScroller.forceFinished(true); - return true; - } - - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, - float velocityY) { - if (mScaling) - return true; - - View v = mChildViews.get(mCurrent); - if (v != null) { - Rect bounds = getScrollBounds(v); - switch(directionOfTravel(velocityX, velocityY)) { - case MOVING_LEFT: - if (HORIZONTAL_SCROLLING && bounds.left >= 0) { - // Fling off to the left bring next view onto screen - View vl = mChildViews.get(mCurrent+1); - - if (vl != null) { - slideViewOntoScreen(vl); - return true; - } - } - break; - case MOVING_UP: - if (!HORIZONTAL_SCROLLING && bounds.top >= 0) { - // Fling off to the top bring next view onto screen - View vl = mChildViews.get(mCurrent+1); - - if (vl != null) { - slideViewOntoScreen(vl); - return true; - } - } - break; - case MOVING_RIGHT: - if (HORIZONTAL_SCROLLING && bounds.right <= 0) { - // Fling off to the right bring previous view onto screen - View vr = mChildViews.get(mCurrent-1); - - if (vr != null) { - slideViewOntoScreen(vr); - return true; - } - } - break; - case MOVING_DOWN: - if (!HORIZONTAL_SCROLLING && bounds.bottom <= 0) { - // Fling off to the bottom bring previous view onto screen - View vr = mChildViews.get(mCurrent-1); - - if (vr != null) { - slideViewOntoScreen(vr); - return true; - } - } - break; - } - mScrollerLastX = mScrollerLastY = 0; - // If the page has been dragged out of bounds then we want to spring back - // nicely. fling jumps back into bounds instantly, so we don't want to use - // fling in that case. On the other hand, we don't want to forgo a fling - // just because of a slightly off-angle drag taking us out of bounds other - // than in the direction of the drag, so we test for out of bounds only - // in the direction of travel. - // - // Also don't fling if out of bounds in any direction by more than fling - // margin - Rect expandedBounds = new Rect(bounds); - expandedBounds.inset(-FLING_MARGIN, -FLING_MARGIN); - - if(withinBoundsInDirectionOfTravel(bounds, velocityX, velocityY) - && expandedBounds.contains(0, 0)) { - mScroller.fling(0, 0, (int)velocityX, (int)velocityY, bounds.left, bounds.right, bounds.top, bounds.bottom); - mStepper.prod(); - } - } - - return true; - } - - public void onLongPress(MotionEvent e) { - } - - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, - float distanceY) { - if (!mScaling) { - mXScroll -= distanceX; - mYScroll -= distanceY; - requestLayout(); - } - return true; - } - - public void onShowPress(MotionEvent e) { - } - - public boolean onSingleTapUp(MotionEvent e) { - return false; - } - - public boolean onScale(ScaleGestureDetector detector) { - float previousScale = mScale; - float scale_factor = mReflow ? REFLOW_SCALE_FACTOR : 1.0f; - float min_scale = MIN_SCALE * scale_factor; - float max_scale = MAX_SCALE * scale_factor; - mScale = Math.min(Math.max(mScale * detector.getScaleFactor(), min_scale), max_scale); - - if (mReflow) { - View v = mChildViews.get(mCurrent); - if (v != null) - onScaleChild(v, mScale); - } else { - float factor = mScale/previousScale; - - View v = mChildViews.get(mCurrent); - if (v != null) { - float currentFocusX = detector.getFocusX(); - float currentFocusY = detector.getFocusY(); - // Work out the focus point relative to the view top left - int viewFocusX = (int)currentFocusX - (v.getLeft() + mXScroll); - int viewFocusY = (int)currentFocusY - (v.getTop() + mYScroll); - // Scroll to maintain the focus point - mXScroll += viewFocusX - viewFocusX * factor; - mYScroll += viewFocusY - viewFocusY * factor; - - if (mLastScaleFocusX>=0) - mXScroll+=currentFocusX-mLastScaleFocusX; - if (mLastScaleFocusY>=0) - mYScroll+=currentFocusY-mLastScaleFocusY; - - mLastScaleFocusX=currentFocusX; - mLastScaleFocusY=currentFocusY; - requestLayout(); - } - } - return true; - } - - public boolean onScaleBegin(ScaleGestureDetector detector) { - mScaling = true; - // Ignore any scroll amounts yet to be accounted for: the - // screen is not showing the effect of them, so they can - // only confuse the user - mXScroll = mYScroll = 0; - mLastScaleFocusX = mLastScaleFocusY = -1; - return true; - } - - public void onScaleEnd(ScaleGestureDetector detector) { - if (mReflow) { - applyToChildren(new ViewMapper() { - @Override - void applyToView(View view) { - onScaleChild(view, mScale); - } - }); - } - mScaling = false; - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - mScaleGestureDetector.onTouchEvent(event); - mGestureDetector.onTouchEvent(event); - - if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) { - mUserInteracting = true; - } - if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) { - mUserInteracting = false; - - View v = mChildViews.get(mCurrent); - if (v != null) { - if (mScroller.isFinished()) { - // If, at the end of user interaction, there is no - // current inertial scroll in operation then animate - // the view onto screen if necessary - slideViewOntoScreen(v); - } - - if (mScroller.isFinished()) { - // If still there is no inertial scroll in operation - // then the layout is stable - postSettle(v); - } - } - } - - requestLayout(); - return true; - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - int n = getChildCount(); - for (int i = 0; i < n; i++) - measureView(getChildAt(i)); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - - try { - onLayout2(changed, left, top, right, bottom); - } - catch (java.lang.OutOfMemoryError e) { - System.out.println("Out of memory during layout"); - - // we might get an out of memory error. - // so let's display an alert. - // TODO: a better message, in resources. - - if (!memAlert) { - memAlert = true; - AlertDialog alertDialog = MuPDFActivity.getAlertBuilder().create(); - alertDialog.setMessage("Out of memory during layout"); - alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK", - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - memAlert = false; - } - }); - alertDialog.show(); - } - } - } - - private boolean memAlert = false; - - private void onLayout2(boolean changed, int left, int top, int right, - int bottom) { - - // "Edit mode" means when the View is being displayed in the Android GUI editor. (this class - // is instantiated in the IDE, so we need to be a bit careful what we do). - if (isInEditMode()) - return; - - View cv = mChildViews.get(mCurrent); - Point cvOffset; - - if (!mResetLayout) { - // Move to next or previous if current is sufficiently off center - if (cv != null) { - boolean move; - cvOffset = subScreenSizeOffset(cv); - // cv.getRight() may be out of date with the current scale - // so add left to the measured width for the correct position - if (HORIZONTAL_SCROLLING) - move = cv.getLeft() + cv.getMeasuredWidth() + cvOffset.x + GAP/2 + mXScroll < getWidth()/2; - else - move = cv.getTop() + cv.getMeasuredHeight() + cvOffset.y + GAP/2 + mYScroll < getHeight()/2; - if (move && mCurrent + 1 < mAdapter.getCount()) { - postUnsettle(cv); - // post to invoke test for end of animation - // where we must set hq area for the new current view - mStepper.prod(); - - onMoveOffChild(mCurrent); - mCurrent++; - onMoveToChild(mCurrent); - } - - if (HORIZONTAL_SCROLLING) - move = cv.getLeft() - cvOffset.x - GAP/2 + mXScroll >= getWidth()/2; - else - move = cv.getTop() - cvOffset.y - GAP/2 + mYScroll >= getHeight()/2; - if (move && mCurrent > 0) { - postUnsettle(cv); - // post to invoke test for end of animation - // where we must set hq area for the new current view - mStepper.prod(); - - onMoveOffChild(mCurrent); - mCurrent--; - onMoveToChild(mCurrent); - } - } - - // Remove not needed children and hold them for reuse - int numChildren = mChildViews.size(); - int childIndices[] = new int[numChildren]; - for (int i = 0; i < numChildren; i++) - childIndices[i] = mChildViews.keyAt(i); - - for (int i = 0; i < numChildren; i++) { - int ai = childIndices[i]; - if (ai < mCurrent - 1 || ai > mCurrent + 1) { - View v = mChildViews.get(ai); - onNotInUse(v); - mViewCache.add(v); - removeViewInLayout(v); - mChildViews.remove(ai); - } - } - } else { - mResetLayout = false; - mXScroll = mYScroll = 0; - - // Remove all children and hold them for reuse - int numChildren = mChildViews.size(); - for (int i = 0; i < numChildren; i++) { - View v = mChildViews.valueAt(i); - onNotInUse(v); - mViewCache.add(v); - removeViewInLayout(v); - } - mChildViews.clear(); - - // Don't reuse cached views if the adapter has changed - if (mReflowChanged) { - mReflowChanged = false; - mViewCache.clear(); - } - - // post to ensure generation of hq area - mStepper.prod(); - } - - // Ensure current view is present - int cvLeft, cvRight, cvTop, cvBottom; - boolean notPresent = (mChildViews.get(mCurrent) == null); - cv = getOrCreateChild(mCurrent); - // When the view is sub-screen-size in either dimension we - // offset it to center within the screen area, and to keep - // the views spaced out - cvOffset = subScreenSizeOffset(cv); - if (notPresent) { - //Main item not already present. Just place it top left - cvLeft = cvOffset.x; - cvTop = cvOffset.y; - } else { - // Main item already present. Adjust by scroll offsets - cvLeft = cv.getLeft() + mXScroll; - cvTop = cv.getTop() + mYScroll; - } - // Scroll values have been accounted for - mXScroll = mYScroll = 0; - cvRight = cvLeft + cv.getMeasuredWidth(); - cvBottom = cvTop + cv.getMeasuredHeight(); - - if (!mUserInteracting && mScroller.isFinished()) { - Point corr = getCorrection(getScrollBounds(cvLeft, cvTop, cvRight, cvBottom)); - cvRight += corr.x; - cvLeft += corr.x; - cvTop += corr.y; - cvBottom += corr.y; - } else if (HORIZONTAL_SCROLLING && cv.getMeasuredHeight() <= getHeight()) { - // When the current view is as small as the screen in height, clamp - // it vertically - Point corr = getCorrection(getScrollBounds(cvLeft, cvTop, cvRight, cvBottom)); - cvTop += corr.y; - cvBottom += corr.y; - } else if (!HORIZONTAL_SCROLLING && cv.getMeasuredWidth() <= getWidth()) { - // When the current view is as small as the screen in width, clamp - // it horizontally - Point corr = getCorrection(getScrollBounds(cvLeft, cvTop, cvRight, cvBottom)); - cvRight += corr.x; - cvLeft += corr.x; - } - - cv.layout(cvLeft, cvTop, cvRight, cvBottom); - - if (mCurrent > 0) { - View lv = getOrCreateChild(mCurrent - 1); - Point leftOffset = subScreenSizeOffset(lv); - if (HORIZONTAL_SCROLLING) - { - int gap = leftOffset.x + GAP + cvOffset.x; - lv.layout(cvLeft - lv.getMeasuredWidth() - gap, - (cvBottom + cvTop - lv.getMeasuredHeight())/2, - cvLeft - gap, - (cvBottom + cvTop + lv.getMeasuredHeight())/2); - } else { - int gap = leftOffset.y + GAP + cvOffset.y; - lv.layout((cvLeft + cvRight - lv.getMeasuredWidth())/2, - cvTop - lv.getMeasuredHeight() - gap, - (cvLeft + cvRight + lv.getMeasuredWidth())/2, - cvTop - gap); - } - } - - if (mCurrent + 1 < mAdapter.getCount()) { - View rv = getOrCreateChild(mCurrent + 1); - Point rightOffset = subScreenSizeOffset(rv); - if (HORIZONTAL_SCROLLING) - { - int gap = cvOffset.x + GAP + rightOffset.x; - rv.layout(cvRight + gap, - (cvBottom + cvTop - rv.getMeasuredHeight())/2, - cvRight + rv.getMeasuredWidth() + gap, - (cvBottom + cvTop + rv.getMeasuredHeight())/2); - } else { - int gap = cvOffset.y + GAP + rightOffset.y; - rv.layout((cvLeft + cvRight - rv.getMeasuredWidth())/2, - cvBottom + gap, - (cvLeft + cvRight + rv.getMeasuredWidth())/2, - cvBottom + gap + rv.getMeasuredHeight()); - } - } - - invalidate(); - } - - @Override - public Adapter getAdapter() { - return mAdapter; - } - - @Override - public View getSelectedView() { - return null; - } - - @Override - public void setAdapter(Adapter adapter) { - - // release previous adapter's bitmaps - if (null!=mAdapter && adapter!=mAdapter) { - if (adapter instanceof MuPDFPageAdapter){ - ((MuPDFPageAdapter) adapter).releaseBitmaps(); - } - } - - mAdapter = adapter; - - requestLayout(); - } - - @Override - public void setSelection(int arg0) { - throw new UnsupportedOperationException(getContext().getString(R.string.not_supported)); - } - - private View getCached() { - if (mViewCache.size() == 0) - return null; - else - return mViewCache.removeFirst(); - } - - private View getOrCreateChild(int i) { - View v = mChildViews.get(i); - if (v == null) { - v = mAdapter.getView(i, getCached(), this); - addAndMeasureChild(i, v); - onChildSetup(i, v); - onScaleChild(v, mScale); - } - - return v; - } - - private void addAndMeasureChild(int i, View v) { - LayoutParams params = v.getLayoutParams(); - if (params == null) { - params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); - } - addViewInLayout(v, 0, params, true); - mChildViews.append(i, v); // Record the view against it's adapter index - measureView(v); - } - - private void measureView(View v) { - // See what size the view wants to be - v.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); - - if (!mReflow) { - // Work out a scale that will fit it to this view - float scale = Math.min((float)getWidth()/(float)v.getMeasuredWidth(), - (float)getHeight()/(float)v.getMeasuredHeight()); - // Use the fitting values scaled by our current scale factor - v.measure(View.MeasureSpec.EXACTLY | (int)(v.getMeasuredWidth()*scale*mScale), - View.MeasureSpec.EXACTLY | (int)(v.getMeasuredHeight()*scale*mScale)); - } else { - v.measure(View.MeasureSpec.EXACTLY | (int)(v.getMeasuredWidth()), - View.MeasureSpec.EXACTLY | (int)(v.getMeasuredHeight())); - } - } - - private Rect getScrollBounds(int left, int top, int right, int bottom) { - int xmin = getWidth() - right; - int xmax = -left; - int ymin = getHeight() - bottom; - int ymax = -top; - - // In either dimension, if view smaller than screen then - // constrain it to be central - if (xmin > xmax) xmin = xmax = (xmin + xmax)/2; - if (ymin > ymax) ymin = ymax = (ymin + ymax)/2; - - return new Rect(xmin, ymin, xmax, ymax); - } - - private Rect getScrollBounds(View v) { - // There can be scroll amounts not yet accounted for in - // onLayout, so add mXScroll and mYScroll to the current - // positions when calculating the bounds. - return getScrollBounds(v.getLeft() + mXScroll, - v.getTop() + mYScroll, - v.getLeft() + v.getMeasuredWidth() + mXScroll, - v.getTop() + v.getMeasuredHeight() + mYScroll); - } - - private Point getCorrection(Rect bounds) { - return new Point(Math.min(Math.max(0,bounds.left),bounds.right), - Math.min(Math.max(0,bounds.top),bounds.bottom)); - } - - private void postSettle(final View v) { - // onSettle and onUnsettle are posted so that the calls - // wont be executed until after the system has performed - // layout. - post (new Runnable() { - public void run () { - onSettle(v); - } - }); - } - - private void postUnsettle(final View v) { - post (new Runnable() { - public void run () { - onUnsettle(v); - } - }); - } - - private void slideViewOntoScreen(View v) { - Point corr = getCorrection(getScrollBounds(v)); - if (corr.x != 0 || corr.y != 0) { - mScrollerLastX = mScrollerLastY = 0; - mScroller.startScroll(0, 0, corr.x, corr.y, 400); - mStepper.prod(); - } - } - - private Point subScreenSizeOffset(View v) { - return new Point(Math.max((getWidth() - v.getMeasuredWidth())/2, 0), - Math.max((getHeight() - v.getMeasuredHeight())/2, 0)); - } - - private static int directionOfTravel(float vx, float vy) { - if (Math.abs(vx) > 2 * Math.abs(vy)) - return (vx > 0) ? MOVING_RIGHT : MOVING_LEFT; - else if (Math.abs(vy) > 2 * Math.abs(vx)) - return (vy > 0) ? MOVING_DOWN : MOVING_UP; - else - return MOVING_DIAGONALLY; - } - - private static boolean withinBoundsInDirectionOfTravel(Rect bounds, float vx, float vy) { - switch (directionOfTravel(vx, vy)) { - case MOVING_DIAGONALLY: return bounds.contains(0, 0); - case MOVING_LEFT: return bounds.left <= 0; - case MOVING_RIGHT: return bounds.right >= 0; - case MOVING_UP: return bounds.top <= 0; - case MOVING_DOWN: return bounds.bottom >= 0; - default: throw new NoSuchElementException(); - } - } -} |