首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 移动开发 > 移动应用 >

[Android学UI之一]简略实现移动应用左右滑动导航页面

2012-11-26 
[Android学UI之一]简单实现移动应用左右滑动导航页面功能:实现图片切换。首页导航。使用原因:使用起来非常的

[Android学UI之一]简单实现移动应用左右滑动导航页面


功能:

    实现图片切换。首页导航。


使用原因:

    使用起来非常的简单。只要一个类即可实现功能。


效果图:

    有图有真相。不过例子太简单了。图也简单。


[Android学UI之一]简略实现移动应用左右滑动导航页面

实现左右滑动。


代码:Activity

/* * Modifications by Yoni Samlan; based on RealViewSwitcher, whose license is: * * Copyright (C) 2010 Marc Reichelt * * Work derived from Workspace.java of the Launcher application *  see http://android.git.kernel.org/?p=platform/packages/apps/Launcher.git * * 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 com.bbswp.viewswitcher.view;import android.content.Context;import android.util.AttributeSet;import android.util.DisplayMetrics;import android.view.Display;import android.view.MotionEvent;import android.view.VelocityTracker;import android.view.View;import android.view.ViewConfiguration;import android.view.ViewGroup;import android.view.WindowManager;import android.widget.Scroller;/** * A view group that allows users to switch between multiple screens (layouts) in the same way as * the Android home screen (Launcher application). * <p> * You can add and remove views using the normal methods {@link ViewGroup#addView(View)}, * {@link ViewGroup#removeView(View)} etc. You may want to listen for updates by calling * {@link HorizontalPager#setOnScreenSwitchListener(OnScreenSwitchListener)} in order to perform * operations once a new screen has been selected. * * Modifications from original version (ysamlan): Animate argument in setCurrentScreen and duration * in snapToScreen; onInterceptTouchEvent handling to support nesting a vertical Scrollview inside * the RealViewSwitcher; allowing snapping to a view even during an ongoing scroll; snap to * next/prev view on 25% scroll change; density-independent swipe sensitivity; width-independent * pager animation durations on scrolling to properly handle large screens without excessively * long animations. * * Other modifications: * (aveyD) Handle orientation changes properly and fully snap to the right position. * * @author Marc Reichelt, <a href="http://www.marcreichelt.de/">http://www.marcreichelt.de/</a> * @version 0.1.0 */public final class HorizontalPager extends ViewGroup {    /*     * How long to animate between screens when programmatically setting with setCurrentScreen using     * the animate parameter     */    private static final int ANIMATION_SCREEN_SET_DURATION_MILLIS = 500;    // What fraction (1/x) of the screen the user must swipe to indicate a page change    private static final int FRACTION_OF_SCREEN_WIDTH_FOR_SWIPE = 4;    private static final int INVALID_SCREEN = -1;    /*     * Velocity of a swipe (in density-independent pixels per second) to force a swipe to the     * next/previous screen. Adjusted into mDensityAdjustedSnapVelocity on init.     */    private static final int SNAP_VELOCITY_DIP_PER_SECOND = 600;    // Argument to getVelocity for units to give pixels per second (1 = pixels per millisecond).    private static final int VELOCITY_UNIT_PIXELS_PER_SECOND = 1000;    private static final int TOUCH_STATE_REST = 0;    private static final int TOUCH_STATE_HORIZONTAL_SCROLLING = 1;    private static final int TOUCH_STATE_VERTICAL_SCROLLING = -1;    private int mCurrentScreen;    private int mDensityAdjustedSnapVelocity;    private boolean mFirstLayout = true;    private float mLastMotionX;    private float mLastMotionY;    private OnScreenSwitchListener mOnScreenSwitchListener;    private int mMaximumVelocity;    private int mNextScreen = INVALID_SCREEN;    private Scroller mScroller;    private int mTouchSlop;    private int mTouchState = TOUCH_STATE_REST;    private VelocityTracker mVelocityTracker;    private int mLastSeenLayoutWidth = -1;    /**     * Simple constructor to use when creating a view from code.     *     * @param context The Context the view is running in, through which it can     *        access the current theme, resources, etc.     */    public HorizontalPager(final Context context) {        super(context);        init();    }    /**     * Constructor that is called when inflating a view from XML. This is called     * when a view is being constructed from an XML file, supplying attributes     * that were specified in the XML file. This version uses a default style of     * 0, so the only attribute values applied are those in the Context's Theme     * and the given AttributeSet.     *     * <p>     * The method onFinishInflate() will be called after all children have been     * added.     *     * @param context The Context the view is running in, through which it can     *        access the current theme, resources, etc.     * @param attrs The attributes of the XML tag that is inflating the view.     * @see #View(Context, AttributeSet, int)     */    public HorizontalPager(final Context context, final AttributeSet attrs) {        super(context, attrs);        init();    }    /**     * Sets up the scroller and touch/fling sensitivity parameters for the pager.     */    private void init() {        mScroller = new Scroller(getContext());        // Calculate the density-dependent snap velocity in pixels        DisplayMetrics displayMetrics = new DisplayMetrics();        ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay()                .getMetrics(displayMetrics);        mDensityAdjustedSnapVelocity =                (int) (displayMetrics.density * SNAP_VELOCITY_DIP_PER_SECOND);        final ViewConfiguration configuration = ViewConfiguration.get(getContext());        mTouchSlop = configuration.getScaledTouchSlop();        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();    }    @Override    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        final int width = MeasureSpec.getSize(widthMeasureSpec);        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);        if (widthMode != MeasureSpec.EXACTLY) {            throw new IllegalStateException("ViewSwitcher can only be used in EXACTLY mode.");        }        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);        if (heightMode != MeasureSpec.EXACTLY) {            throw new IllegalStateException("ViewSwitcher can only be used in EXACTLY mode.");        }        // The children are given the same width and height as the workspace        final int count = getChildCount();        for (int i = 0; i < count; i++) {            getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);        }        if (mFirstLayout) {            scrollTo(mCurrentScreen * width, 0);            mFirstLayout = false;        }        else if (width != mLastSeenLayoutWidth) { // Width has changed            /*             * Recalculate the width and scroll to the right position to be sure we're in the right             * place in the event that we had a rotation that didn't result in an activity restart             * (code by aveyD). Without this you can end up between two pages after a rotation.             */            Display display =                    ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE))                            .getDefaultDisplay();            int displayWidth = display.getWidth();            mNextScreen = Math.max(0, Math.min(getCurrentScreen(), getChildCount() - 1));            final int newX = mNextScreen * displayWidth;            final int delta = newX - getScrollX();            mScroller.startScroll(getScrollX(), 0, delta, 0, 0);        }        mLastSeenLayoutWidth   = width;    }    @Override    protected void onLayout(final boolean changed, final int l, final int t, final int r,            final int b) {        int childLeft = 0;        final int count = getChildCount();        for (int i = 0; i < count; i++) {            final View child = getChildAt(i);            if (child.getVisibility() != View.GONE) {                final int childWidth = child.getMeasuredWidth();                child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight());                childLeft += childWidth;            }        }    }    @Override    public boolean onInterceptTouchEvent(final MotionEvent ev) {        /*         * By Yoni Samlan: Modified onInterceptTouchEvent based on standard ScrollView's         * onIntercept. The logic is designed to support a nested vertically scrolling view inside         * this one; once a scroll registers for X-wise scrolling, handle it in this view and don't         * let the children, but once a scroll registers for y-wise scrolling, let the children         * handle it exclusively.         */        final int action = ev.getAction();        boolean intercept = false;        switch (action) {            case MotionEvent.ACTION_MOVE:                /*                 * If we're in a horizontal scroll event, take it (intercept further events). But if                 * we're mid-vertical-scroll, don't even try; let the children deal with it. If we                 * haven't found a scroll event yet, check for one.                 */                if (mTouchState == TOUCH_STATE_HORIZONTAL_SCROLLING) {                    /*                     * We've already started a horizontal scroll; set intercept to true so we can                     * take the remainder of all touch events in onTouchEvent.                     */                    intercept = true;                } else if (mTouchState == TOUCH_STATE_VERTICAL_SCROLLING) {                    // Let children handle the events for the duration of the scroll event.                    intercept = false;                } else { // We haven't picked up a scroll event yet; check for one.                    /*                     * If we detected a horizontal scroll event, start stealing touch events (mark                     * as scrolling). Otherwise, see if we had a vertical scroll event -- if so, let                     * the children handle it and don't look to intercept again until the motion is                     * done.                     */                    final float x = ev.getX();                    final int xDiff = (int) Math.abs(x - mLastMotionX);                    boolean xMoved = xDiff > mTouchSlop;                    if (xMoved) {                        // Scroll if the user moved far enough along the X axis                        mTouchState = TOUCH_STATE_HORIZONTAL_SCROLLING;                        mLastMotionX = x;                    }                    final float y = ev.getY();                    final int yDiff = (int) Math.abs(y - mLastMotionY);                    boolean yMoved = yDiff > mTouchSlop;                    if (yMoved) {                        mTouchState = TOUCH_STATE_VERTICAL_SCROLLING;                    }                }                break;            case MotionEvent.ACTION_CANCEL:            case MotionEvent.ACTION_UP:                // Release the drag.                mTouchState = TOUCH_STATE_REST;                break;            case MotionEvent.ACTION_DOWN:                /*                 * No motion yet, but register the coordinates so we can check for intercept at the                 * next MOVE event.                 */                mLastMotionY = ev.getY();                mLastMotionX = ev.getX();                break;            default:                break;            }        return intercept;    }    @Override    public boolean onTouchEvent(final MotionEvent ev) {        if (mVelocityTracker == null) {            mVelocityTracker = VelocityTracker.obtain();        }        mVelocityTracker.addMovement(ev);        final int action = ev.getAction();        final float x = ev.getX();        switch (action) {            case MotionEvent.ACTION_DOWN:                /*                 * If being flinged and user touches, stop the fling. isFinished will be false if                 * being flinged.                 */                if (!mScroller.isFinished()) {                    mScroller.abortAnimation();                }                // Remember where the motion event started                mLastMotionX = x;                if (mScroller.isFinished()) {                    mTouchState = TOUCH_STATE_REST;                } else {                    mTouchState = TOUCH_STATE_HORIZONTAL_SCROLLING;                }                break;            case MotionEvent.ACTION_MOVE:                final int xDiff = (int) Math.abs(x - mLastMotionX);                boolean xMoved = xDiff > mTouchSlop;                if (xMoved) {                    // Scroll if the user moved far enough along the X axis                    mTouchState = TOUCH_STATE_HORIZONTAL_SCROLLING;                }                if (mTouchState == TOUCH_STATE_HORIZONTAL_SCROLLING) {                    // Scroll to follow the motion event                    final int deltaX = (int) (mLastMotionX - x);                    mLastMotionX = x;                    final int scrollX = getScrollX();                    if (deltaX < 0) {                        if (scrollX > 0) {                            scrollBy(Math.max(-scrollX, deltaX), 0);                        }                    } else if (deltaX > 0) {                        final int availableToScroll =                                getChildAt(getChildCount() - 1).getRight() - scrollX - getWidth();                        if (availableToScroll > 0) {                            scrollBy(Math.min(availableToScroll, deltaX), 0);                        }                    }                }                break;            case MotionEvent.ACTION_UP:                if (mTouchState == TOUCH_STATE_HORIZONTAL_SCROLLING) {                    final VelocityTracker velocityTracker = mVelocityTracker;                    velocityTracker.computeCurrentVelocity(VELOCITY_UNIT_PIXELS_PER_SECOND,                            mMaximumVelocity);                    int velocityX = (int) velocityTracker.getXVelocity();                    if (velocityX > mDensityAdjustedSnapVelocity && mCurrentScreen > 0) {                        // Fling hard enough to move left                        snapToScreen(mCurrentScreen - 1);                    } else if (velocityX < -mDensityAdjustedSnapVelocity                            && mCurrentScreen < getChildCount() - 1) {                        // Fling hard enough to move right                        snapToScreen(mCurrentScreen + 1);                    } else {                        snapToDestination();                    }                    if (mVelocityTracker != null) {                        mVelocityTracker.recycle();                        mVelocityTracker = null;                    }                }                mTouchState = TOUCH_STATE_REST;                break;            case MotionEvent.ACTION_CANCEL:                mTouchState = TOUCH_STATE_REST;                break;            default:                break;        }        return true;    }    @Override    public void computeScroll() {        if (mScroller.computeScrollOffset()) {            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());            postInvalidate();        } else if (mNextScreen != INVALID_SCREEN) {            mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1));            // Notify observer about screen change            if (mOnScreenSwitchListener != null) {                mOnScreenSwitchListener.onScreenSwitched(mCurrentScreen);            }            mNextScreen = INVALID_SCREEN;        }    }    /**     * Returns the index of the currently displayed screen.     *     * @return The index of the currently displayed screen.     */    public int getCurrentScreen() {        return mCurrentScreen;    }    /**     * Sets the current screen.     *     * @param currentScreen The new screen.     * @param animate True to smoothly scroll to the screen, false to snap instantly     */    public void setCurrentScreen(final int currentScreen, final boolean animate) {        mCurrentScreen = Math.max(0, Math.min(currentScreen, getChildCount() - 1));        if (animate) {            snapToScreen(currentScreen, ANIMATION_SCREEN_SET_DURATION_MILLIS);        } else {            scrollTo(mCurrentScreen * getWidth(), 0);        }        invalidate();    }    /**     * Sets the {@link OnScreenSwitchListener}.     *     * @param onScreenSwitchListener The listener for switch events.     */    public void setOnScreenSwitchListener(final OnScreenSwitchListener onScreenSwitchListener) {        mOnScreenSwitchListener = onScreenSwitchListener;    }    /**     * Snaps to the screen we think the user wants (the current screen for very small movements; the     * next/prev screen for bigger movements).     */    private void snapToDestination() {        final int screenWidth = getWidth();        int scrollX = getScrollX();        int whichScreen = mCurrentScreen;        int deltaX = scrollX - (screenWidth * mCurrentScreen);        // Check if they want to go to the prev. screen        if ((deltaX < 0) && mCurrentScreen != 0                && ((screenWidth / FRACTION_OF_SCREEN_WIDTH_FOR_SWIPE) < -deltaX)) {            whichScreen--;            // Check if they want to go to the next screen        } else if ((deltaX > 0) && (mCurrentScreen + 1 != getChildCount())                && ((screenWidth / FRACTION_OF_SCREEN_WIDTH_FOR_SWIPE) < deltaX)) {            whichScreen++;        }        snapToScreen(whichScreen);    }    /**     * Snap to a specific screen, animating automatically for a duration proportional to the     * distance left to scroll.     *     * @param whichScreen Screen to snap to     */    private void snapToScreen(final int whichScreen) {        snapToScreen(whichScreen, -1);    }    /**     * Snaps to a specific screen, animating for a specific amount of time to get there.     *     * @param whichScreen Screen to snap to     * @param duration -1 to automatically time it based on scroll distance; a positive number to     *            make the scroll take an exact duration.     */    private void snapToScreen(final int whichScreen, final int duration) {        /*         * Modified by Yoni Samlan: Allow new snapping even during an ongoing scroll animation. This         * is intended to make HorizontalPager work as expected when used in conjunction with a         * RadioGroup used as "tabbed" controls. Also, make the animation take a percentage of our         * normal animation time, depending how far they've already scrolled.         */        mNextScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));        final int newX = mNextScreen * getWidth();        final int delta = newX - getScrollX();        if (duration < 0) {             // E.g. if they've scrolled 80% of the way, only animation for 20% of the duration            mScroller.startScroll(getScrollX(), 0, delta, 0, (int) (Math.abs(delta)                    / (float) getWidth() * ANIMATION_SCREEN_SET_DURATION_MILLIS));        } else {            mScroller.startScroll(getScrollX(), 0, delta, 0, duration);        }        invalidate();    }    /**     * Listener for the event that the HorizontalPager switches to a new view.     */    public static interface OnScreenSwitchListener {        /**         * Notifies listeners about the new screen. Runs after the animation completed.         *         * @param screen The new screen index.         */        void onScreenSwitched(int screen);    }}



代码下载:http://download.csdn.net/detail/hudan2714/4802333





热点排行