From 652433d9434b83fdf01ca2819e94f01c50c443be Mon Sep 17 00:00:00 2001 From: Alex Urzhumtcev Date: Fri, 7 Oct 2016 14:53:33 +0300 Subject: [PATCH] replace toolbar by pdf specific --- .../andpdf/pdfviewer/PdfViewerFragment.java | 56 +- .../andpdf/pdfviewer/gui/FullScrollView.java | 1732 ----------------- 2 files changed, 35 insertions(+), 1753 deletions(-) delete mode 100644 PdfView/src/main/java/net/sf/andpdf/pdfviewer/gui/FullScrollView.java diff --git a/PdfView/src/main/java/net/sf/andpdf/pdfviewer/PdfViewerFragment.java b/PdfView/src/main/java/net/sf/andpdf/pdfviewer/PdfViewerFragment.java index 5fcaaff..f07dbdb 100644 --- a/PdfView/src/main/java/net/sf/andpdf/pdfviewer/PdfViewerFragment.java +++ b/PdfView/src/main/java/net/sf/andpdf/pdfviewer/PdfViewerFragment.java @@ -13,8 +13,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; import android.util.Log; import android.view.Gravity; import android.view.KeyEvent; @@ -85,11 +84,8 @@ public class PdfViewerFragment extends Fragment { @Nullable @Override public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) { - Log.i(TAG, "onCreate"); uiHandler = new Handler(); - hideActionBar(); - progress = ProgressDialog.show(getActivity(), "Loading", "Loading PDF Page", true, true); mGraphView = new GraphView(getActivity()); @@ -100,26 +96,17 @@ public class PdfViewerFragment extends Fragment { mPage = STARTPAGE; - final LinearLayout linearLayout = new LinearLayout(getActivity()); - linearLayout.setOrientation(LinearLayout.VERTICAL); - final View view = LayoutInflater.from(getActivity()).inflate(R.layout.pfd_toolbar, null); - view.findViewById(R.id.pdf_toolbar_close_image).setOnClickListener(new OnClickListener() { + final Toolbar toolbar = (Toolbar) LayoutInflater.from(getActivity()).inflate(R.layout.pfd_toolbar, null); + toolbar.findViewById(R.id.pdf_toolbar_close_image).setOnClickListener(new OnClickListener() { @Override public void onClick(final View v) { getFragmentManager().popBackStack(); } }); - pageNumbersView = (TextView) view.findViewById(R.id.pdf_toolbar_page_numbers_text_view); - linearLayout.addView(view); - linearLayout.addView(setContent(password)); - return linearLayout; - } - private void hideActionBar() { - final ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); - if (actionBar != null) { - actionBar.hide(); - } + replaceView(((ActionBarHidden) getActivity()).getCurrentActivityToolbarReplacedBy(toolbar), toolbar); + pageNumbersView = (TextView) toolbar.findViewById(R.id.pdf_toolbar_page_numbers_text_view); + return setContent(password); } @Override @@ -129,6 +116,7 @@ public class PdfViewerFragment extends Fragment { getFragmentManager().popBackStack(); return; } + if (!passwordNeeded) { startRenderThread(mPage); } else { @@ -351,8 +339,7 @@ public class PdfViewerFragment extends Fragment { setPageBitmap(null); //updateImage(); - - final LinearLayout.LayoutParams linearLayout = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, 0, 1); + final LinearLayout.LayoutParams linearLayout = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); setLayoutParams(linearLayout); addView(pdfZoomedImageView); } @@ -479,6 +466,27 @@ public class PdfViewerFragment extends Fragment { } } + public static ViewGroup getParent(View view) { + return (ViewGroup) view.getParent(); + } + + public static void removeView(View view) { + ViewGroup parent = getParent(view); + if (parent != null) { + parent.removeView(view); + } + } + + public static void replaceView(View currentView, View newView) { + ViewGroup parent = getParent(currentView); + if (parent == null) { + return; + } + final int index = parent.indexOfChild(currentView); + removeView(currentView); + parent.addView(newView, index); + } + private int getPdfPasswordEditField() { return R.id.etPassword; } @@ -491,4 +499,10 @@ public class PdfViewerFragment extends Fragment { return R.id.btExit; } + public interface ActionBarHidden { + + Toolbar getCurrentActivityToolbarReplacedBy(@NonNull final Toolbar toolbar); + + } + } \ No newline at end of file diff --git a/PdfView/src/main/java/net/sf/andpdf/pdfviewer/gui/FullScrollView.java b/PdfView/src/main/java/net/sf/andpdf/pdfviewer/gui/FullScrollView.java deleted file mode 100644 index d0c69d2..0000000 --- a/PdfView/src/main/java/net/sf/andpdf/pdfviewer/gui/FullScrollView.java +++ /dev/null @@ -1,1732 +0,0 @@ -/* - * Copyright (C) 2008-2009 pjv (and others, see About dialog) - * - * This file is part of Android's Fortune. - * - * Android's Fortune is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Android's Fortune is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Android's Fortune. If not, see . - * - * - * - * Based on android.widget.ScrollView (original license below). - */ - -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.sf.andpdf.pdfviewer.gui; -// package net.lp.androidsfortune.utils; - -import java.util.List; - -import android.R; -import android.content.Context; -import android.graphics.Rect; -import android.util.AttributeSet; -import android.util.Config; -import android.util.Log; -import android.view.FocusFinder; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.VelocityTracker; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.view.ViewParent; -import android.view.animation.AnimationUtils; -import android.widget.FrameLayout; -import android.widget.LinearLayout; -import android.widget.ListView; -import android.widget.Scroller; -import android.widget.TextView; - -/** - * Layout container for a view hierarchy that can be scrolled by the user, - * allowing it to be larger than the physical display. A ScrollView - * is a {@link FrameLayout}, meaning you should place one child in it - * containing the entire contents to scroll; this child may itself be a layout - * manager with a complex hierarchy of objects. A child that is often used - * is a {@link LinearLayout} in a vertical orientation, presenting a vertical - * array of top-level items that the user can scroll through. - * - *

You should never use a ScrollView with a {@link ListView}, since - * ListView takes care of its own scrolling. Most importantly, doing this - * defeats all of the important optimizations in ListView for dealing with - * large lists, since it effectively forces the ListView to display its entire - * list of items to fill up the infinite container supplied by ScrollView. - * - *

The {@link TextView} class also - * takes care of its own scrolling, so does not require a ScrollView, but - * using the two together is possible to achieve the effect of a text view - * within a larger container. - * - *

FullScrollView supports vertical and horizontal scrolling. FullScrollView is based on ScrollView and extended according to symmetry so horizontal scrolling would be possible. Contains many bugs and documentation has not been updated, but kinda works when containing one TextView as a child. pjv 2009-01-24 - */ -public class FullScrollView extends FrameLayout { - static final String TAG = "FullScrollView"; - static final boolean localLOGV = false || Config.LOGV; - - private static final int ANIMATED_SCROLL_GAP = 250; - - /** - * When arrow scrolling, ListView will never scroll more than this factor - * times the height of the list. - */ - private static final float MAX_SCROLL_FACTOR = 0.5f; - - - - - /** - * The offset, in pixels, by which the content of this view is scrolled - * horizontally. - * {@hide} - */ - protected int mScrollX; - /** - * The offset, in pixels, by which the content of this view is scrolled - * vertically. - * {@hide} - */ - protected int mScrollY; - - - - - private long mLastScroll; - - private final Rect mTempRect = new Rect(); - private Scroller mScroller; - - /** - * Flag to indicate that we are moving focus ourselves. This is so the - * code that watches for focus changes initiated outside this ScrollView - * knows that it does not have to do anything. - */ - private boolean mScrollViewMovedFocus; - - /** - * Position of the last motion event. - */ - private float mLastMotionX; - private float mLastMotionY; - - /** - * True when the layout has changed but the traversal has not come through yet. - * Ideally the view hierarchy would keep track of this for us. - */ - private boolean mIsLayoutDirty = true; - - /** - * The child to give focus to in the event that a child has requested focus while the - * layout is dirty. This prevents the scroll from being wrong if the child has not been - * laid out before requesting focus. - */ - private View mChildToScrollTo = null; - - /** - * True if the user is currently dragging this ScrollView around. This is - * not the same as 'is being flinged', which can be checked by - * mScroller.isFinished() (flinging begins when the user lifts his finger). - */ - private boolean mIsBeingDraggedX = false; - private boolean mIsBeingDraggedY = false; - - /** - * Determines speed during touch scrolling - */ - private VelocityTracker mVelocityTracker; - - /** - * When set to true, the scroll view measure its child to make it fill the currently - * visible area. - */ - private boolean mFillViewportX; - private boolean mFillViewportY; - - /** - * Whether arrow scrolling is animated. - */ - private boolean mSmoothScrollingEnabled = true; - - public FullScrollView(Context context) { - this(context, null); - } - - public FullScrollView(Context context, AttributeSet attrs) { - this(context, attrs, R.attr.scrollViewStyle); - } - - public FullScrollView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - initScrollView(); - //TODO - //TypedArray a = - // context.obtainStyledAttributes(attrs, net.lp.androidsfortune.R.attr.ScrollView, defStyle, 0); - - //setFillViewport(a.getBoolean(R.styleable.ScrollView_fillViewport, false)); - - //a.recycle(); - } - - @Override - protected float getLeftFadingEdgeStrength() { - if (getChildCount() == 0) { - return 0.0f; - } - - final int width = getHorizontalFadingEdgeLength(); - if (mScrollX < width) { - return mScrollX / (float) width; - } - - return 1.0f; - } - - @Override - protected float getRightFadingEdgeStrength() { - if (getChildCount() == 0) { - return 0.0f; - } - - final int width = getHorizontalFadingEdgeLength(); - final int right = getChildAt(0).getRight(); - final int span = right - mScrollX - getWidth(); - if (span < width) { - return span / (float) width; - } - - return 1.0f; - } - @Override - protected float getTopFadingEdgeStrength() { - if (getChildCount() == 0) { - return 0.0f; - } - - final int length = getVerticalFadingEdgeLength(); - if (mScrollY < length) { - return mScrollY / (float) length; - } - - return 1.0f; - } - - @Override - protected float getBottomFadingEdgeStrength() { - if (getChildCount() == 0) { - return 0.0f; - } - - final int length = getVerticalFadingEdgeLength(); - final int bottom = getChildAt(0).getBottom(); - final int span = bottom - mScrollY - getHeight(); - if (span < length) { - return span / (float) length; - } - - return 1.0f; - } - - /** - * @return The maximum amount this scroll view will scroll in response to - * an arrow event. - */ - public int getMaxScrollAmountY() { - return (int) (MAX_SCROLL_FACTOR * (getBottom() - getTop())); - } - /** - * @return The maximum amount this scroll view will scroll in response to - * an arrow event. - */ - public int getMaxScrollAmountX() { - return (int) (MAX_SCROLL_FACTOR * (getRight() - getLeft())); - } - - - private void initScrollView() { - mScroller = new Scroller(getContext()); - setFocusable(true); - setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); - setWillNotDraw(false); - } - @Override - public void addView(View child) { - if (getChildCount() > 0) { - throw new IllegalStateException("ScrollView can host only one direct child"); - } - - super.addView(child); - } - - @Override - public void addView(View child, int index) { - if (getChildCount() > 0) { - throw new IllegalStateException("ScrollView can host only one direct child"); - } - - super.addView(child, index); - } - - @Override - public void addView(View child, ViewGroup.LayoutParams params) { - if (getChildCount() > 0) { - throw new IllegalStateException("ScrollView can host only one direct child"); - } - - super.addView(child, params); - } - - @Override - public void addView(View child, int index, ViewGroup.LayoutParams params) { - if (getChildCount() > 0) { - throw new IllegalStateException("ScrollView can host only one direct child"); - } - - super.addView(child, index, params); - } - - /** - * @return Returns true this ScrollView can be scrolled - */ - private boolean canScrollX() { - View child = getChildAt(0); - if (child != null) { - int childWidth = child.getWidth(); - return getWidth() < childWidth + getPaddingLeft() + getPaddingRight(); - } - return false; - } - - /** - * @return Returns true this ScrollView can be scrolled - */ - private boolean canScrollY() { - View child = getChildAt(0); - if (child != null) { - int childHeight = child.getHeight(); - return getHeight() < childHeight + getPaddingTop() + getPaddingBottom(); - } - return false; - } - - /** - * Indicates whether this ScrollView's content is stretched to fill the viewport. - * - * @return True if the content fills the viewport, false otherwise. - */ - public boolean isFillViewportX() { - return mFillViewportX; - } - - /** - * Indicates whether this ScrollView's content is stretched to fill the viewport. - * - * @return True if the content fills the viewport, false otherwise. - */ - public boolean isFillViewportY() { - return mFillViewportY; - } - - /** - * Indicates this ScrollView whether it should stretch its content height to fill - * the viewport or not. - * - * @param fillViewport True to stretch the content's height to the viewport's - * boundaries, false otherwise. - */ - public void setFillViewportX(boolean fillViewportX) { - if (fillViewportX != mFillViewportX) { - mFillViewportX = fillViewportX; - requestLayout(); - } - } - - /** - * Indicates this ScrollView whether it should stretch its content height to fill - * the viewport or not. - * - * @param fillViewport True to stretch the content's height to the viewport's - * boundaries, false otherwise. - */ - public void setFillViewportY(boolean fillViewportY) { - if (fillViewportY != mFillViewportY) { - mFillViewportY = fillViewportY; - requestLayout(); - } - } - - /** - * @return Whether arrow scrolling will animate its transition. - */ - public boolean isSmoothScrollingEnabled() { - return mSmoothScrollingEnabled; - } - - /** - * Set whether arrow scrolling will animate its transition. - * @param smoothScrollingEnabled whether arrow scrolling will animate its transition - */ - public void setSmoothScrollingEnabled(boolean smoothScrollingEnabled) { - mSmoothScrollingEnabled = smoothScrollingEnabled; - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - if (!mFillViewportX&&!mFillViewportY) { - return; - } - - final int widthMode = MeasureSpec.getMode(widthMeasureSpec); - final int heightMode = MeasureSpec.getMode(heightMeasureSpec); - if (heightMode == MeasureSpec.UNSPECIFIED && widthMode == MeasureSpec.UNSPECIFIED) { - return; - } - - final View child = getChildAt(0); - int width = getMeasuredWidth(); - int height = getMeasuredHeight(); - if (child.getMeasuredWidth() < width && child.getMeasuredHeight() < height && heightMode != MeasureSpec.UNSPECIFIED && widthMode != MeasureSpec.UNSPECIFIED && mFillViewportX&&mFillViewportY) { - //final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams(); - - width -= getPaddingLeft(); - width -= getPaddingRight(); - int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); - height -= getPaddingTop(); - height -= getPaddingBottom(); - int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); - - child.measure(childWidthMeasureSpec, childHeightMeasureSpec); - }else if (child.getMeasuredHeight() < height && heightMode != MeasureSpec.UNSPECIFIED && mFillViewportY) { - final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams(); - - int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, getPaddingLeft() - + getPaddingRight(), lp.width); - height -= getPaddingTop(); - height -= getPaddingBottom(); - int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); - - child.measure(childWidthMeasureSpec, childHeightMeasureSpec); - }else if (child.getMeasuredWidth() < width && widthMode != MeasureSpec.UNSPECIFIED && mFillViewportX) { - final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams(); - - int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTop() - + getPaddingBottom(), lp.height); - width -= getPaddingLeft(); - width -= getPaddingRight(); - int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); - - child.measure(childWidthMeasureSpec, childHeightMeasureSpec); - } - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - // Let the focused view and/or our descendants get the key first - boolean handled = super.dispatchKeyEvent(event); - if (handled) { - return true; - } - return executeKeyEvent(event); - } - - /** - * You can call this function yourself to have the scroll view perform - * scrolling from a key event, just as if the event had been dispatched to - * it by the view hierarchy. - * - * @param event The key event to execute. - * @return Return true if the event was handled, else false. - */ - public boolean executeKeyEvent(KeyEvent event) { - mTempRect.setEmpty(); - - if (!canScrollX()||!canScrollY()) { - if (isFocused()) { - View currentFocused = findFocus(); - if (currentFocused == this) currentFocused = null; - View nextFocused = FocusFinder.getInstance().findNextFocus(this, - currentFocused, View.FOCUS_DOWN); - return nextFocused != null - && nextFocused != this - && nextFocused.requestFocus(View.FOCUS_DOWN); - } - return false; - } - - boolean handled = false; - if(canScrollY()){ - if (event.getAction() == KeyEvent.ACTION_DOWN) { - switch (event.getKeyCode()) { - case KeyEvent.KEYCODE_DPAD_UP: - if (!event.isAltPressed()) { - handled = arrowScroll(View.FOCUS_UP); - } else { - handled = fullScroll(View.FOCUS_UP); - } - break; - case KeyEvent.KEYCODE_DPAD_DOWN: - if (!event.isAltPressed()) { - handled = arrowScroll(View.FOCUS_DOWN); - } else { - handled = fullScroll(View.FOCUS_DOWN); - } - break; - case KeyEvent.KEYCODE_SPACE: - pageScroll(event.isShiftPressed() ? View.FOCUS_UP : View.FOCUS_DOWN); - break; - } - } - } - if(canScrollX()){ - if (event.getAction() == KeyEvent.ACTION_DOWN) { - switch (event.getKeyCode()) { - case KeyEvent.KEYCODE_DPAD_LEFT: - if (!event.isAltPressed()) { - handled = arrowScroll(View.FOCUS_LEFT); - } else { - handled = fullScroll(View.FOCUS_LEFT); - } - break; - case KeyEvent.KEYCODE_DPAD_RIGHT: - if (!event.isAltPressed()) { - handled = arrowScroll(View.FOCUS_RIGHT); - } else { - handled = fullScroll(View.FOCUS_RIGHT); - } - break; - } - } - } - - return handled; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - /* - * This method JUST determines whether we want to intercept the motion. - * If we return true, onMotionEvent will be called and we do the actual - * scrolling there. - */ - - /* - * Shortcut the most recurring case: the user is in the dragging - * state and he is moving his finger. We want to intercept this - * motion. - */ - final int action = ev.getAction(); - if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDraggedX||mIsBeingDraggedY)) { - return true; - } - - if (!canScrollX()) { - mIsBeingDraggedX = false; - } - if (!canScrollY()) { - mIsBeingDraggedY = false; - } - - if (!canScrollY()&&!canScrollX()) { - return false; - } - - final float x = ev.getX(); - final float y = ev.getY(); - - switch (action) { - case MotionEvent.ACTION_MOVE: - /* - * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check - * whether the user has moved far enough from his original down touch. - */ - - /* - * Locally do absolute value. mLastMotionY is set to the x value - * of the down event. - */ - final int xDiff = (int) Math.abs(x - mLastMotionX); - if (xDiff > ViewConfiguration.getTouchSlop()) { - mIsBeingDraggedX = true; - } - - /* - * Locally do absolute value. mLastMotionY is set to the y value - * of the down event. - */ - final int yDiff = (int) Math.abs(y - mLastMotionY); - if (yDiff > ViewConfiguration.getTouchSlop()) { - mIsBeingDraggedY = true; - } - break; - - case MotionEvent.ACTION_DOWN: - /* Remember location of down touch */ - mLastMotionX = x; - mLastMotionY = y; - - /* - * If being flinged and user touches the screen, initiate drag; - * otherwise don't. mScroller.isFinished should be false when - * being flinged. - */ - mIsBeingDraggedX = !mScroller.isFinished(); - mIsBeingDraggedY = !mScroller.isFinished(); - break; - - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: - /* Release the drag */ - mIsBeingDraggedX = false; - mIsBeingDraggedY = false; - break; - } - - /* - * The only time we want to intercept motion events is if we are in the - * drag mode. - */ - return (mIsBeingDraggedX||mIsBeingDraggedY); - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - - if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) { - // Don't handle edge touches immediately -- they may actually belong to one of our - // descendants. - return false; - } - - if (!canScrollX()&&!canScrollY()) { - return false; - } - - if (mVelocityTracker == null) { - mVelocityTracker = VelocityTracker.obtain(); - } - mVelocityTracker.addMovement(ev); - - final int action = ev.getAction(); - final float x = ev.getX(); - final float y = ev.getY(); - - 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; - mLastMotionY = y; - break; - case MotionEvent.ACTION_MOVE: - // Scroll to follow the motion event - final int deltaX = (int) (mLastMotionX - x); - mLastMotionX = x; - final int deltaY = (int) (mLastMotionY - y); - mLastMotionY = y; - - if(localLOGV) Log.v(TAG, "onTouchEvent ActionMove deltaX="+deltaX+" deltaY="+deltaY); - - if (deltaX < 0) { - if (mScrollX >= 0) {//FIX Changed, don't know why, pjv. - scrollBy(deltaX, 0); - } - } else if (deltaX > 0) { - final int rightEdge = getWidth() - getPaddingRight(); - final int availableToScroll = getChildAt(0).getRight() - mScrollX - rightEdge; - if (availableToScroll > 0) { - scrollBy(Math.min(availableToScroll, deltaX), 0); - } - } - - if (deltaY < 0) { - if (mScrollY >= 0) {//FIX Changed, don't know why, pjv. - scrollBy(0, deltaY); - } - } else if (deltaY > 0) { - final int bottomEdge = getHeight() - getPaddingBottom(); - final int availableToScroll = getChildAt(0).getBottom() - mScrollY - bottomEdge; - if (availableToScroll > 0) { - scrollBy(0, Math.min(availableToScroll, deltaY)); - } - } - break; - case MotionEvent.ACTION_UP: - final VelocityTracker velocityTracker = mVelocityTracker; - velocityTracker.computeCurrentVelocity(1000); - int initialVelocityX = (int) velocityTracker.getXVelocity(); - int initialVelocityY = (int) velocityTracker.getYVelocity(); - - if ((Math.abs(initialVelocityX) > ViewConfiguration.getMinimumFlingVelocity()) && - (getChildCount() > 0)) { - flingX(-initialVelocityX); - } - - if ((Math.abs(initialVelocityY) > ViewConfiguration.getMinimumFlingVelocity()) && - (getChildCount() > 0)) { - flingY(-initialVelocityY); - } - - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; - } - } - return true; - } - - /** - *

- * Finds the next focusable component that fits in this View's bounds - * (excluding fading edges) pretending that this View's left is located at - * the parameter left. - *

- * - * @param leftFocus look for a candidate is the one at the left of the bounds - * if leftFocus is true, or at the bottom of the bounds if leftFocus is - * false - * @param left the left offset of the bounds in which a focusable must be - * found (the fading edge is assumed to start at this position) - * @param preferredFocusable the View that has highest priority and will be - * returned if it is within my bounds (null is valid) - * @return the next focusable component in the bounds or null if none can be - * found - */ - private View findFocusableViewInMyBoundsX(final boolean leftFocus, - final int left, View preferredFocusable) { - /* - * The fading edge's transparent side should be considered for focus - * since it's mostly visible, so we divide the actual fading edge length - * by 2. - */ - final int fadingEdgeLength = getHorizontalFadingEdgeLength() / 2; - final int leftWithoutFadingEdge = left + fadingEdgeLength; - final int rightWithoutFadingEdge = left + getWidth() - fadingEdgeLength; - - if ((preferredFocusable != null) - && (preferredFocusable.getLeft() < rightWithoutFadingEdge) - && (preferredFocusable.getRight() > leftWithoutFadingEdge)) { - return preferredFocusable; - } - - return findFocusableViewInBoundsX(leftFocus, leftWithoutFadingEdge, - rightWithoutFadingEdge); - } - /** - *

- * Finds the next focusable component that fits in this View's bounds - * (excluding fading edges) pretending that this View's top is located at - * the parameter top. - *

- * - * @param topFocus look for a candidate is the one at the top of the bounds - * if topFocus is true, or at the bottom of the bounds if topFocus is - * false - * @param top the top offset of the bounds in which a focusable must be - * found (the fading edge is assumed to start at this position) - * @param preferredFocusable the View that has highest priority and will be - * returned if it is within my bounds (null is valid) - * @return the next focusable component in the bounds or null if none can be - * found - */ - private View findFocusableViewInMyBoundsY(final boolean topFocus, - final int top, View preferredFocusable) { - /* - * The fading edge's transparent side should be considered for focus - * since it's mostly visible, so we divide the actual fading edge length - * by 2. - */ - final int fadingEdgeLength = getVerticalFadingEdgeLength() / 2; - final int topWithoutFadingEdge = top + fadingEdgeLength; - final int bottomWithoutFadingEdge = top + getHeight() - fadingEdgeLength; - - if ((preferredFocusable != null) - && (preferredFocusable.getTop() < bottomWithoutFadingEdge) - && (preferredFocusable.getBottom() > topWithoutFadingEdge)) { - return preferredFocusable; - } - - return findFocusableViewInBoundsY(topFocus, topWithoutFadingEdge, - bottomWithoutFadingEdge); - } - - /** - *

- * Finds the next focusable component that fits in the specified bounds. - *

- * - * @param leftFocus look for a candidate is the one at the left of the bounds - * if leftFocus is true, or at the right of the bounds if leftFocus is - * false - * @param left the left offset of the bounds in which a focusable must be - * found - * @param right the right offset of the bounds in which a focusable must - * be found - * @return the next focusable component in the bounds or null if none can - * be found - */ - private View findFocusableViewInBoundsX(boolean leftFocus, int left, int right) { - - List focusables = getFocusables(View.FOCUS_FORWARD); - View focusCandidate = null; - - /* - * A fully contained focusable is one where its left is right of the bound's - * left, and its right is left of the bound's right. A partially - * contained focusable is one where some part of it is within the - * bounds, but it also has some part that is not within bounds. A fully contained - * focusable is preferred to a partially contained focusable. - */ - boolean foundFullyContainedFocusable = false; - - int count = focusables.size(); - for (int i = 0; i < count; i++) { - View view = focusables.get(i); - int viewLeft = view.getLeft(); - int viewRight = view.getRight(); - - if (left < viewRight && viewLeft < right) { - /* - * the focusable is in the target area, it is a candidate for - * focusing - */ - - final boolean viewIsFullyContained = (left < viewLeft) && - (viewRight < right); - - if (focusCandidate == null) { - /* No candidate, take this one */ - focusCandidate = view; - foundFullyContainedFocusable = viewIsFullyContained; - } else { - final boolean viewIsCloserToBoundary = - (leftFocus && viewLeft < focusCandidate.getLeft()) || - (!leftFocus && viewRight > focusCandidate - .getRight()); - - if (foundFullyContainedFocusable) { - if (viewIsFullyContained && viewIsCloserToBoundary) { - /* - * We're dealing with only fully contained views, so - * it has to be closer to the boundary to beat our - * candidate - */ - focusCandidate = view; - } - } else { - if (viewIsFullyContained) { - /* Any fully contained view beats a partially contained view */ - focusCandidate = view; - foundFullyContainedFocusable = true; - } else if (viewIsCloserToBoundary) { - /* - * Partially contained view beats another partially - * contained view if it's closer - */ - focusCandidate = view; - } - } - } - } - } - - return focusCandidate; - } - - /** - *

- * Finds the next focusable component that fits in the specified bounds. - *

- * - * @param topFocus look for a candidate is the one at the top of the bounds - * if topFocus is true, or at the bottom of the bounds if topFocus is - * false - * @param top the top offset of the bounds in which a focusable must be - * found - * @param bottom the bottom offset of the bounds in which a focusable must - * be found - * @return the next focusable component in the bounds or null if none can - * be found - */ - private View findFocusableViewInBoundsY(boolean topFocus, int top, int bottom) { - - List focusables = getFocusables(View.FOCUS_FORWARD); - View focusCandidate = null; - - /* - * A fully contained focusable is one where its top is below the bound's - * top, and its bottom is above the bound's bottom. A partially - * contained focusable is one where some part of it is within the - * bounds, but it also has some part that is not within bounds. A fully contained - * focusable is preferred to a partially contained focusable. - */ - boolean foundFullyContainedFocusable = false; - - int count = focusables.size(); - for (int i = 0; i < count; i++) { - View view = focusables.get(i); - int viewTop = view.getTop(); - int viewBottom = view.getBottom(); - - if (top < viewBottom && viewTop < bottom) { - /* - * the focusable is in the target area, it is a candidate for - * focusing - */ - - final boolean viewIsFullyContained = (top < viewTop) && - (viewBottom < bottom); - - if (focusCandidate == null) { - /* No candidate, take this one */ - focusCandidate = view; - foundFullyContainedFocusable = viewIsFullyContained; - } else { - final boolean viewIsCloserToBoundary = - (topFocus && viewTop < focusCandidate.getTop()) || - (!topFocus && viewBottom > focusCandidate - .getBottom()); - - if (foundFullyContainedFocusable) { - if (viewIsFullyContained && viewIsCloserToBoundary) { - /* - * We're dealing with only fully contained views, so - * it has to be closer to the boundary to beat our - * candidate - */ - focusCandidate = view; - } - } else { - if (viewIsFullyContained) { - /* Any fully contained view beats a partially contained view */ - focusCandidate = view; - foundFullyContainedFocusable = true; - } else if (viewIsCloserToBoundary) { - /* - * Partially contained view beats another partially - * contained view if it's closer - */ - focusCandidate = view; - } - } - } - } - } - - return focusCandidate; - } - - /** - *

Handles scrolling in response to a "page up/down" shortcut press. This - * method will scroll the view by one page up or down and give the focus - * to the topmost/bottommost component in the new visible area. If no - * component is a good candidate for focus, this scrollview reclaims the - * focus.

- * - * @param direction the scroll direction: {@link android.view.View#FOCUS_UP} - * to go one page up or - * {@link android.view.View#FOCUS_DOWN} to go one page down - * @return true if the key event is consumed by this method, false otherwise - */ - public boolean pageScroll(int direction) { - boolean down = direction == View.FOCUS_DOWN; - int height = getHeight(); - - if (down) { - mTempRect.top = getScrollY() + height; - int count = getChildCount(); - if (count > 0) { - View view = getChildAt(count - 1); - if (mTempRect.top + height > view.getBottom()) { - mTempRect.top = view.getBottom() - height; - } - } - } else { - mTempRect.top = getScrollY() - height; - if (mTempRect.top < 0) { - mTempRect.top = 0; - } - } - mTempRect.bottom = mTempRect.top + height; - - return scrollAndFocus(direction, mTempRect.top, mTempRect.bottom); - } - - /** - *

Handles scrolling in response to a "home/end" shortcut press. This - * method will scroll the view to the top or bottom and give the focus - * to the topmost/bottommost component in the new visible area. If no - * component is a good candidate for focus, this scrollview reclaims the - * focus.

- * - * @param direction the scroll direction: {@link android.view.View#FOCUS_UP} - * to go the top of the view or - * {@link android.view.View#FOCUS_DOWN} to go the bottom - * @return true if the key event is consumed by this method, false otherwise - */ - public boolean fullScroll(int direction) { - boolean down = direction == View.FOCUS_DOWN; - int height = getHeight(); - - mTempRect.top = 0; - mTempRect.bottom = height; - - if (down) { - int count = getChildCount(); - if (count > 0) { - View view = getChildAt(count - 1); - mTempRect.bottom = view.getBottom(); - mTempRect.top = mTempRect.bottom - height; - } - } - - return scrollAndFocus(direction, mTempRect.top, mTempRect.bottom); - } - - /** - *

Scrolls the view to make the area defined by top and - * bottom visible. This method attempts to give the focus - * to a component visible in this area. If no component can be focused in - * the new visible area, the focus is reclaimed by this scrollview.

- * - * @param direction the scroll direction: {@link android.view.View#FOCUS_UP} - * to go upward - * {@link android.view.View#FOCUS_DOWN} to downward - * @param top the top offset of the new area to be made visible - * @param bottom the bottom offset of the new area to be made visible - * @return true if the key event is consumed by this method, false otherwise - */ - private boolean scrollAndFocus(int direction, int top, int bottom) { - boolean handled = true; - - int height = getHeight(); - int containerTop = getScrollY(); - int containerBottom = containerTop + height; - boolean up = direction == View.FOCUS_UP; - - View newFocused = findFocusableViewInBoundsY(up, top, bottom);//TODO - if (newFocused == null) { - newFocused = this; - } - - if (top >= containerTop && bottom <= containerBottom) { - handled = false; - } else { - int delta = up ? (top - containerTop) : (bottom - containerBottom); - doScrollY(delta); - } - - if (newFocused != findFocus() && newFocused.requestFocus(direction)) { - mScrollViewMovedFocus = true; - mScrollViewMovedFocus = false; - } - - return handled; - } - - /** - * Handle scrolling in response to an up or down or left or right arrow click. - * - * @param direction The direction corresponding to the arrow key that was - * pressed - * @return True if we consumed the event, false otherwise - */ - public boolean arrowScroll(int direction) { - - View currentFocused = findFocus(); - if (currentFocused == this) currentFocused = null; - - View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction); - - final int maxJumpX = getMaxScrollAmountX(); - final int maxJumpY = getMaxScrollAmountY(); - - if (nextFocused != null && isWithinDeltaOfScreenX(nextFocused, maxJumpX) && isWithinDeltaOfScreenY(nextFocused, maxJumpY)) {//TODO - nextFocused.getDrawingRect(mTempRect); - offsetDescendantRectToMyCoords(nextFocused, mTempRect); - int scrollDeltaX = computeScrollDeltaToGetChildRectOnScreenX(mTempRect); - doScrollX(scrollDeltaX); - int scrollDeltaY = computeScrollDeltaToGetChildRectOnScreenY(mTempRect); - doScrollY(scrollDeltaY); - nextFocused.requestFocus(direction); - } else { - // no new focus - int scrollDeltaX = maxJumpX; - int scrollDeltaY = maxJumpY; - - if (direction == View.FOCUS_UP && getScrollX() < scrollDeltaX) { - scrollDeltaX = getScrollX(); - } else if (direction == View.FOCUS_DOWN) { - - int daRight = getChildAt(getChildCount() - 1).getRight(); - - int screenRight = getScrollX() + getWidth(); - - if (daRight - screenRight < maxJumpX) { - scrollDeltaX = daRight - screenRight; - } - } - if (direction == View.FOCUS_UP && getScrollY() < scrollDeltaY) { - scrollDeltaY = getScrollY(); - } else if (direction == View.FOCUS_DOWN) { - - int daBottom = getChildAt(getChildCount() - 1).getBottom(); - - int screenBottom = getScrollY() + getHeight(); - - if (daBottom - screenBottom < maxJumpY) { - scrollDeltaY = daBottom - screenBottom; - } - } - if (scrollDeltaX == 0 && scrollDeltaY == 0) { - return false; - }else if (scrollDeltaX == 0) { - doScrollY(direction == View.FOCUS_DOWN ? scrollDeltaY : -scrollDeltaY); - }else if (scrollDeltaY == 0) { - doScrollX(direction == View.FOCUS_DOWN ? scrollDeltaX : -scrollDeltaX); - } - } - - if (currentFocused != null && currentFocused.isFocused() - && isOffScreen(currentFocused)) { - // previously focused item still has focus and is off screen, give - // it up (take it back to ourselves) - // (also, need to temporarily force FOCUS_BEFORE_DESCENDANTS so we are - // sure to - // get it) - final int descendantFocusability = getDescendantFocusability(); // save - setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS); - requestFocus(); - setDescendantFocusability(descendantFocusability); // restore - } - return true; - } - - /** - * @return whether the descendant of this scroll view is scrolled off - * screen. - */ - private boolean isOffScreen(View descendant) { - return !isWithinDeltaOfScreenX(descendant, 0)&&!isWithinDeltaOfScreenY(descendant, 0); - } - - /** - * @return whether the descendant of this scroll view is within delta - * pixels of being on the screen. - */ - private boolean isWithinDeltaOfScreenX(View descendant, int delta) { - descendant.getDrawingRect(mTempRect); - offsetDescendantRectToMyCoords(descendant, mTempRect); - - return (mTempRect.right + delta) >= getScrollX() - && (mTempRect.left - delta) <= (getScrollX() + getWidth()); - } - - /** - * @return whether the descendant of this scroll view is within delta - * pixels of being on the screen. - */ - private boolean isWithinDeltaOfScreenY(View descendant, int delta) { - descendant.getDrawingRect(mTempRect); - offsetDescendantRectToMyCoords(descendant, mTempRect); - - return (mTempRect.bottom + delta) >= getScrollY() - && (mTempRect.top - delta) <= (getScrollY() + getHeight()); - } - - - /** - * Smooth scroll by a X delta - * - * @param delta the number of pixels to scroll by on the Y axis - */ - private void doScrollX(int delta) { - if (delta != 0) { - if (mSmoothScrollingEnabled) { - smoothScrollBy(delta, 0); - } else { - scrollBy(delta, 0); - } - } - } - - /** - * Smooth scroll by a Y delta - * - * @param delta the number of pixels to scroll by on the X axis - */ - private void doScrollY(int delta) { - if (delta != 0) { - if (mSmoothScrollingEnabled) { - smoothScrollBy(0, delta); - } else { - scrollBy(0, delta); - } - } - } - - /** - * Like {@link View#scrollBy}, but scroll smoothly instead of immediately. - * - * @param dx the number of pixels to scroll by on the X axis - * @param dy the number of pixels to scroll by on the Y axis - */ - public final void smoothScrollBy(int dx, int dy) { - long duration = AnimationUtils.currentAnimationTimeMillis() - mLastScroll; - if (duration > ANIMATED_SCROLL_GAP) { - if (localLOGV) Log.v(TAG, "Smooth scroll: mScrollX=" + mScrollX - + " dx=" + dx + "mScrollY=" + mScrollY - + " dy=" + dy); - mScroller.startScroll(mScrollX, mScrollY, dx, dy); - invalidate(); - } else { - if (!mScroller.isFinished()) { - mScroller.abortAnimation(); - } - if (localLOGV) Log.v(TAG, "Immediate scroll: mScrollX=" + mScrollX - + " dx=" + dx + "mScrollY=" + mScrollY - + " dy=" + dy); - scrollBy(dx, dy); - } - mLastScroll = AnimationUtils.currentAnimationTimeMillis(); - } - - /** - * Like {@link #scrollTo}, but scroll smoothly instead of immediately. - * - * @param x the position where to scroll on the X axis - * @param y the position where to scroll on the Y axis - */ - public final void smoothScrollTo(int x, int y) { - smoothScrollBy(x - mScrollX, y - mScrollY); - } - - /** - *

The scroll range of a scroll view is the overall width of all of its - * children.

- */ - @Override - protected int computeHorizontalScrollRange() { - int count = getChildCount(); - return count == 0 ? getWidth() : (getChildAt(0)).getRight(); - } - - /** - *

The scroll range of a scroll view is the overall height of all of its - * children.

- */ - @Override - protected int computeVerticalScrollRange() { - int count = getChildCount(); - return count == 0 ? getHeight() : (getChildAt(0)).getBottom(); - } - - - @Override - protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { - int childWidthMeasureSpec; - int childHeightMeasureSpec; - - childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); - - childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); - - child.measure(childWidthMeasureSpec, childHeightMeasureSpec); - } - - @Override - protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, - int parentHeightMeasureSpec, int heightUsed) { - final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); - - final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( - lp.leftMargin + lp.rightMargin, MeasureSpec.UNSPECIFIED); - final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( - lp.topMargin + lp.bottomMargin, MeasureSpec.UNSPECIFIED); - - child.measure(childWidthMeasureSpec, childHeightMeasureSpec); - } - - @Override - public void computeScroll() { - if (mScroller.computeScrollOffset()) { - // This is called at drawing time by ViewGroup. We don't want to - // re-show the scrollbars at this point, which scrollTo will do, - // so we replicate most of scrollTo here. - // - // It's a little odd to call onScrollChanged from inside the drawing. - // - // It is, except when you remember that computeScroll() is used to - // animate scrolling. So unless we want to defer the onScrollChanged() - // until the end of the animated scrolling, we don't really have a - // choice here. - // - // I agree. The alternative, which I think would be worse, is to post - // something and tell the subclasses later. This is bad because there - // will be a window where mScrollX/Y is different from what the app - // thinks it is. - // - int oldX = mScrollX; - int oldY = mScrollY; - int x = mScroller.getCurrX(); - int y = mScroller.getCurrY(); - if (getChildCount() > 0) { - View child = getChildAt(0); - mScrollX = clamp(x, this.getWidth(), child.getWidth()); - mScrollY = clamp(y, this.getHeight(), child.getHeight()); - if (localLOGV) Log.v(TAG, "mScrollX=" + mScrollX + " x=" + x - + " width=" + this.getWidth() - + " child width=" + child.getWidth() + " mScrollY=" + mScrollY + " y=" + y - + " height=" + this.getHeight() - + " child height=" + child.getHeight()); - } else { - mScrollX = x; - mScrollY = y; - } - if (oldX != mScrollX || oldY != mScrollY) { - onScrollChanged(mScrollX, mScrollY, oldX, oldY); - } - - // Keep on drawing until the animation has finished. - postInvalidate(); - } - } - - /** - * Scrolls the view to the given child. - * - * @param child the View to scroll to - */ - private void scrollToChild(View child) { - child.getDrawingRect(mTempRect); - - /* Offset from child's local coordinates to ScrollView coordinates */ - offsetDescendantRectToMyCoords(child, mTempRect); - - int scrollDeltaX = computeScrollDeltaToGetChildRectOnScreenX(mTempRect); - int scrollDeltaY = computeScrollDeltaToGetChildRectOnScreenY(mTempRect); - - if (scrollDeltaY != 0 || scrollDeltaX != 0) { - scrollBy(scrollDeltaX, scrollDeltaY); - } - } - - /** - * If rect is off screen, scroll just enough to get it (or at least the - * first screen size chunk of it) on screen. - * - * @param rect The rectangle. - * @param immediate True to scroll immediately without animation - * @return true if scrolling was performed - */ - private boolean scrollToChildRect(Rect rect, boolean immediate) { - final int deltaX = computeScrollDeltaToGetChildRectOnScreenX(rect); - final int deltaY = computeScrollDeltaToGetChildRectOnScreenY(rect); - final boolean scroll = deltaX != 0 || deltaY != 0; - if (scroll) { - if (immediate) { - scrollBy(deltaX, deltaY); - } else { - smoothScrollBy(deltaX, deltaY); - } - } - return scroll; - } - - /** - * Compute the amount to scroll in the X direction in order to get - * a rectangle completely on the screen (or, if taller than the screen, - * at least the first screen size chunk of it). - * - * @param rect The rect. - * @return The scroll delta. - */ - protected int computeScrollDeltaToGetChildRectOnScreenX(Rect rect) { - - int width = getWidth(); - int screenLeft = getScrollX(); - int screenRight = screenLeft + width; - - int fadingEdge = getHorizontalFadingEdgeLength(); - - // leave room for left fading edge as long as rect isn't at very left - if (rect.left > 0) { - screenLeft += fadingEdge; - } - - // leave room for right fading edge as long as rect isn't at very right - if (rect.right < getChildAt(0).getWidth()) { - screenRight -= fadingEdge; - } - - int scrollXDelta = 0; - - if (localLOGV) Log.v(TAG, "child=" + rect.toString() - + " screenLeft=" + screenLeft + " screenRight=" + screenRight - + " width=" + width); - if (rect.right > screenRight && rect.left > screenLeft) { - // need to move right to get it in view: move right just enough so - // that the entire rectangle is in view (or at least the first - // screen size chunk). - - if (rect.width() > width) { - // just enough to get screen size chunk on - scrollXDelta += (rect.left - screenLeft); - } else { - // get entire rect at right of screen - scrollXDelta += (rect.right - screenRight); - } - - // make sure we aren't scrolling beyond the end of our content - int right = getChildAt(getChildCount() - 1).getRight(); - int distanceToRight = right - screenRight; - if (localLOGV) Log.v(TAG, "scrollXDelta=" + scrollXDelta - + " distanceToRight=" + distanceToRight); - scrollXDelta = Math.min(scrollXDelta, distanceToRight); - - } else if (rect.left < screenLeft && rect.right < screenRight) { - // need to move left to get it in view: move left just enough so that - // entire rectangle is in view (or at least the first screen - // size chunk of it). - - if (rect.width() > width) { - // screen size chunk - scrollXDelta -= (screenRight - rect.right); - } else { - // entire rect at left - scrollXDelta -= (screenLeft - rect.left); - } - - // make sure we aren't scrolling any further than the left our content - scrollXDelta = Math.max(scrollXDelta, -getScrollX()); - } - return scrollXDelta; - } - /** - * Compute the amount to scroll in the Y direction in order to get - * a rectangle completely on the screen (or, if taller than the screen, - * at least the first screen size chunk of it). - * - * @param rect The rect. - * @return The scroll delta. - */ - protected int computeScrollDeltaToGetChildRectOnScreenY(Rect rect) { - - int height = getHeight(); - int screenTop = getScrollY(); - int screenBottom = screenTop + height; - - int fadingEdge = getVerticalFadingEdgeLength(); - - // leave room for top fading edge as long as rect isn't at very top - if (rect.top > 0) { - screenTop += fadingEdge; - } - - // leave room for bottom fading edge as long as rect isn't at very bottom - if (rect.bottom < getChildAt(0).getHeight()) { - screenBottom -= fadingEdge; - } - - int scrollYDelta = 0; - - if (localLOGV) Log.v(TAG, "child=" + rect.toString() - + " screenTop=" + screenTop + " screenBottom=" + screenBottom - + " height=" + height); - if (rect.bottom > screenBottom && rect.top > screenTop) { - // need to move down to get it in view: move down just enough so - // that the entire rectangle is in view (or at least the first - // screen size chunk). - - if (rect.height() > height) { - // just enough to get screen size chunk on - scrollYDelta += (rect.top - screenTop); - } else { - // get entire rect at bottom of screen - scrollYDelta += (rect.bottom - screenBottom); - } - - // make sure we aren't scrolling beyond the end of our content - int bottom = getChildAt(getChildCount() - 1).getBottom(); - int distanceToBottom = bottom - screenBottom; - if (localLOGV) Log.v(TAG, "scrollYDelta=" + scrollYDelta - + " distanceToBottom=" + distanceToBottom); - scrollYDelta = Math.min(scrollYDelta, distanceToBottom); - - } else if (rect.top < screenTop && rect.bottom < screenBottom) { - // need to move up to get it in view: move up just enough so that - // entire rectangle is in view (or at least the first screen - // size chunk of it). - - if (rect.height() > height) { - // screen size chunk - scrollYDelta -= (screenBottom - rect.bottom); - } else { - // entire rect at top - scrollYDelta -= (screenTop - rect.top); - } - - // make sure we aren't scrolling any further than the top our content - scrollYDelta = Math.max(scrollYDelta, -getScrollY()); - } - return scrollYDelta; - } - - @Override - public void requestChildFocus(View child, View focused) { - if (!mScrollViewMovedFocus) { - if (!mIsLayoutDirty) { - scrollToChild(focused); - } else { - // The child may not be laid out yet, we can't compute the scroll yet - mChildToScrollTo = focused; - } - } - super.requestChildFocus(child, focused); - } - - - /** - * When looking for focus in children of a scroll view, need to be a little - * more careful not to give focus to something that is scrolled off screen. - * - * This is more expensive than the default {@link android.view.ViewGroup} - * implementation, otherwise this behavior might have been made the default. - */ - @Override - protected boolean onRequestFocusInDescendants(int direction, - Rect previouslyFocusedRect) { - - // convert from forward / backward notation to up / down / left / right - // (ugh). - if (direction == View.FOCUS_FORWARD) { - direction = View.FOCUS_DOWN; - } else if (direction == View.FOCUS_BACKWARD) { - direction = View.FOCUS_UP; - } - - final View nextFocus = previouslyFocusedRect == null ? - FocusFinder.getInstance().findNextFocus(this, null, direction) : - FocusFinder.getInstance().findNextFocusFromRect(this, - previouslyFocusedRect, direction); - - if (nextFocus == null) { - return false; - } - - if (isOffScreen(nextFocus)) { - return false; - } - - return nextFocus.requestFocus(direction, previouslyFocusedRect); - } - - @Override - public boolean requestChildRectangleOnScreen(View child, Rect rectangle, - boolean immediate) { - // offset into coordinate space of this scroll view - rectangle.offset(child.getLeft() - child.getScrollX(), - child.getTop() - child.getScrollY()); - - return scrollToChildRect(rectangle, immediate); - } - - @Override - public void requestLayout() { - mIsLayoutDirty = true; - super.requestLayout(); - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - super.onLayout(changed, l, t, r, b); - mIsLayoutDirty = false; - // Give a child focus if it needs it - if (mChildToScrollTo != null && isViewDescendantOf(mChildToScrollTo, this)) { - scrollToChild(mChildToScrollTo); - } - mChildToScrollTo = null; - - // Calling this with the present values causes it to re-clam them - scrollTo(mScrollX, mScrollY); - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - - View currentFocused = findFocus(); - if (null == currentFocused || this == currentFocused) - return; - - final int maxJumpX = getRight() - getLeft(); - final int maxJumpY = getBottom() - getTop(); - - if (isWithinDeltaOfScreenX(currentFocused, maxJumpX)) { - currentFocused.getDrawingRect(mTempRect); - offsetDescendantRectToMyCoords(currentFocused, mTempRect); - int scrollDeltaX = computeScrollDeltaToGetChildRectOnScreenX(mTempRect); - doScrollX(scrollDeltaX); - } - if (isWithinDeltaOfScreenY(currentFocused, maxJumpY)) { - currentFocused.getDrawingRect(mTempRect); - offsetDescendantRectToMyCoords(currentFocused, mTempRect); - int scrollDeltaY = computeScrollDeltaToGetChildRectOnScreenY(mTempRect); - doScrollY(scrollDeltaY); - } - } - - /** - * Return true if child is an descendant of parent, (or equal to the parent). - */ - private boolean isViewDescendantOf(View child, View parent) { - if (child == parent) { - return true; - } - - final ViewParent theParent = child.getParent(); - return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent); - } - - /** - * Fling the scroll view - * - * @param velocityX The initial velocity in the X direction. Positive - * numbers mean that the finger/cursor is moving right the screen, - * which means we want to scroll towards the left. - */ - public void flingX(int velocityX) { - int width = getWidth(); - int right = getChildAt(getChildCount() - 1).getRight(); - - mScroller.fling(mScrollX, mScrollY, velocityX, 0, 0, 0, 0, right - width); - - final boolean movingRight = velocityX > 0; - - View newFocused = - findFocusableViewInMyBoundsX(movingRight, mScroller.getFinalX(), findFocus()); - if (newFocused == null) { - newFocused = this; - } - - if (newFocused != findFocus() - && newFocused.requestFocus(movingRight ? View.FOCUS_DOWN : View.FOCUS_UP)) { - mScrollViewMovedFocus = true; - mScrollViewMovedFocus = false; - } - - invalidate(); - } - - /** - * Fling the scroll view - * - * @param velocityY The initial velocity in the Y direction. Positive - * numbers mean that the finger/cursor is moving down the screen, - * which means we want to scroll towards the top. - */ - public void flingY(int velocityY) { - int height = getHeight(); - int bottom = getChildAt(getChildCount() - 1).getBottom(); - - mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0, bottom - height); - - final boolean movingDown = velocityY > 0; - - View newFocused = - findFocusableViewInMyBoundsY(movingDown, mScroller.getFinalY(), findFocus()); - if (newFocused == null) { - newFocused = this; - } - - if (newFocused != findFocus() - && newFocused.requestFocus(movingDown ? View.FOCUS_DOWN : View.FOCUS_UP)) { - mScrollViewMovedFocus = true; - mScrollViewMovedFocus = false; - } - - invalidate(); - } - - /** - * {@inheritDoc} - * - *

This version also clamps the scrolling to the bounds of our child. - */ - public void scrollTo(int x, int y) { - // we rely on the fact the View.scrollBy calls scrollTo. - if (getChildCount() > 0) { - View child = getChildAt(0); - x = clamp(x, this.getWidth(), child.getWidth()); - y = clamp(y, this.getHeight(), child.getHeight()); - if (x != mScrollX || y != mScrollY) { - super.scrollTo(x, y); - mScrollX=x;//FIX Changed, pjv. - mScrollY=y;//FIX Changed, pjv. - } - } - if(localLOGV) Log.v(TAG, "scrollTo x="+x+" y="+y); - } - - private int clamp(int n, int my, int child) { - if (my >= child || n < 0) { - /* my >= child is this case: - * |--------------- me ---------------| - * |------ child ------| - * or - * |--------------- me ---------------| - * |------ child ------| - * or - * |--------------- me ---------------| - * |------ child ------| - * - * n < 0 is this case: - * |------ me ------| - * |-------- child --------| - * |-- mScrollX --| - */ - return 0; - } - if ((my+n) > child) { - /* this case: - * |------ me ------| - * |------ child ------| - * |-- mScrollX --| - */ - return child-my; - } - return n; - } -}