如何创建像原始Android菜单的水平列表?

时间:2013-01-28 07:55:28

标签: android

我有一些信息列表,我想在屏幕上显示每个项目。并且屏幕下方或上方应该有点显示我的顺序。你可以想象原始的Android菜单

enter image description here

如何创建这样的列表?

2 个答案:

答案 0 :(得分:0)

使用ViewPager尝试PageTitleStrip。或者在顶部添加您自己的自定义视图,并显示哪个索引ViewPager

答案 1 :(得分:0)

您可以使用此自定义类来满足您的要求:

<强> Horizo​​ntalPagerWithPageControl

   /**
    * A view group that allows users to switch between multiple screens (layouts)
     * in the same way as the Android home screen (Launcher application).     
     * 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 HorizontalPagerWithPageControl#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.
    * 
    * Modifications from the version of ysamlan: Added page control to the bottom
    * of this view.
    * 
    * 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 HorizontalPagerWithPageControl 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 m_currentScreen;
private int m_densityAdjustedSnapVelocity;
private boolean m_firstLayout = true;
private float m_lastMotionX;
private float m_lastMotionY;
private OnScreenSwitchListener m_onScreenSwitchListener;
private int m_maximumVelocity;
private int m_nextScreen = INVALID_SCREEN;
private Scroller m_scroller;
private int m_touchSlop;
private int m_touchState = TOUCH_STATE_REST;
private VelocityTracker m_velocityTracker;
private int m_lastSeenLayoutWidth = -1;
// Active and inactive draw
private Drawable m_activeDrawable;
private Drawable m_inactiveDrawable;
// The size for the drawables
private float m_indicatorSize;
// The screen scale to get px to dip
private static float SCALE;
/**
 * Simple constructor to use when creating a view from code.
 * 
 * @param p_context
 *            The Context the view is running in, through which it can
 *            access the current theme, resources, etc.
 */
public HorizontalPagerWithPageControl(final Context p_context) {
    super(p_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 p_context
 *            The Context the view is running in, through which it can
 *            access the current theme, resources, etc.
 * @param p_attrs
 *            The attributes of the XML tag that is inflating the view.
 * @see #View(Context, AttributeSet, int)
 */
public HorizontalPagerWithPageControl(final Context p_context,
        final AttributeSet p_attrs) {
    super(p_context, p_attrs);
    init();
}
/**
 * Sets up the scroller and touch/fling sensitivity parameters for the
 * pager.
 */
private void init() {
    m_scroller = new Scroller(getContext());
    // get the screen density
    SCALE = getResources().getDisplayMetrics().density;
    // set the indicator size resolution independent
    m_indicatorSize = (7 * SCALE);
    // Calculate the density-dependent snap velocity in pixels
    DisplayMetrics m_displayMetrics = new DisplayMetrics();
    ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE))
            .getDefaultDisplay().getMetrics(m_displayMetrics);
    m_densityAdjustedSnapVelocity = (int) (m_displayMetrics.density * SNAP_VELOCITY_DIP_PER_SECOND);
    final ViewConfiguration m_configuration = ViewConfiguration
            .get(getContext());
    m_touchSlop = m_configuration.getScaledTouchSlop();
    m_maximumVelocity = m_configuration.getScaledMaximumFlingVelocity();
    // draw the shapes
    makeShapes();
}
@Override
protected void onMeasure(final int p_widthMeasureSpec,
        final int p_heightMeasureSpec) {
    super.onMeasure(p_widthMeasureSpec, p_heightMeasureSpec);
    final int m_width = MeasureSpec.getSize(p_widthMeasureSpec);
    final int m_widthMode = MeasureSpec.getMode(p_widthMeasureSpec);
    if (m_widthMode != MeasureSpec.EXACTLY) {
        throw new IllegalStateException(
                "ViewSwitcher can only be used in EXACTLY mode.");
    }
    final int m_heightMode = MeasureSpec.getMode(p_heightMeasureSpec);
    if (m_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 m_count = getChildCount();
    for (int m_i = 0; m_i < m_count; m_i++) {
        getChildAt(m_i).measure(p_widthMeasureSpec, p_heightMeasureSpec);
    }
    if (m_firstLayout) {
        scrollTo(m_currentScreen * m_width, 0);
        m_firstLayout = false;
    }
    else if (m_width != m_lastSeenLayoutWidth) { // 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 m_display = ((WindowManager) getContext().getSystemService(
                Context.WINDOW_SERVICE)).getDefaultDisplay();
        int m_displayWidth = m_display.getWidth();
        m_nextScreen = Math.max(0,
                Math.min(getCurrentScreen(), getChildCount() - 1));
        final int m_newX = m_nextScreen * m_displayWidth;
        final int m_delta = m_newX - getScrollX();
        m_scroller.startScroll(getScrollX(), 0, m_delta, 0, 0);
    }
    m_lastSeenLayoutWidth = m_width;
}
@Override
protected void onLayout(final boolean p_changed, final int p_l,
        final int p_t, final int p_r, final int p_b) {
    int m_childLeft = 0;
    final int m_count = getChildCount();
    for (int m_i = 0; m_i < m_count; m_i++) {
        final View m_child = getChildAt(m_i);
        if (m_child.getVisibility() != View.GONE) {
            final int m_childWidth = m_child.getMeasuredWidth();
            m_child.layout(m_childLeft, 0, m_childLeft + m_childWidth,
                    m_child.getMeasuredHeight());
            m_childLeft += m_childWidth;
        }
    }
}
@Override
public boolean onInterceptTouchEvent(final MotionEvent p_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 m_action = p_ev.getAction();
    boolean m_intercept = false;
    switch (m_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 (m_touchState == 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.
             */
            m_intercept = true;
        } else if (m_touchState == TOUCH_STATE_VERTICAL_SCROLLING) {
            // Let children handle the events for the duration of the scroll
            // event.
            m_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 m_x = p_ev.getX();
            final int m_xDiff = (int) Math.abs(m_x - m_lastMotionX);
            boolean m_xMoved = m_xDiff > m_touchSlop;
            if (m_xMoved) {
                // Scroll if the user moved far enough along the X axis
                m_touchState = TOUCH_STATE_HORIZONTAL_SCROLLING;
                m_lastMotionX = m_x;
            }
            final float m_y = p_ev.getY();
            final int m_yDiff = (int) Math.abs(m_y - m_lastMotionY);
            boolean m_yMoved = m_yDiff > m_touchSlop;
            if (m_yMoved) {
                m_touchState = TOUCH_STATE_VERTICAL_SCROLLING;
            }
        }
        break;
    case MotionEvent.ACTION_CANCEL:
    case MotionEvent.ACTION_UP:
        // Release the drag.
        m_touchState = 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.
         */
        m_lastMotionY = p_ev.getY();
        m_lastMotionX = p_ev.getX();
        break;
    default:
        break;
    }
    return m_intercept;
}
@Override
public boolean onTouchEvent(final MotionEvent p_ev) {
    if (m_velocityTracker == null) {
        m_velocityTracker = VelocityTracker.obtain();
    }
    m_velocityTracker.addMovement(p_ev);
    final int m_action = p_ev.getAction();
    final float m_x = p_ev.getX();
    switch (m_action) {
    case MotionEvent.ACTION_DOWN:
        /*
         * If being flinged and user touches, stop the fling. isFinished
         * will be false if being flinged.
         */
        if (!m_scroller.isFinished()) {
            m_scroller.abortAnimation();
        }
        // Remember where the motion event started
        m_lastMotionX = m_x;
        if (m_scroller.isFinished()) {
            m_touchState = TOUCH_STATE_REST;
        } else {
            m_touchState = TOUCH_STATE_HORIZONTAL_SCROLLING;
        }
        break;
    case MotionEvent.ACTION_MOVE:
        final int m_xDiff = (int) Math.abs(m_x - m_lastMotionX);
        boolean m_xMoved = m_xDiff > m_touchSlop;
        if (m_xMoved) {
            // Scroll if the user moved far enough along the X axis
            m_touchState = TOUCH_STATE_HORIZONTAL_SCROLLING;
        }
        if (m_touchState == TOUCH_STATE_HORIZONTAL_SCROLLING) {
            // Scroll to follow the motion event
            final int m_deltaX = (int) (m_lastMotionX - m_x);
            m_lastMotionX = m_x;
            final int m_scrollX = getScrollX();
            if (m_deltaX < 0) {
                if (m_scrollX > 0) {
                    scrollBy(Math.max(-m_scrollX, m_deltaX), 0);
                }
            } else if (m_deltaX > 0) {
                final int m_availableToScroll = getChildAt(
                        getChildCount() - 1).getRight()
                        - m_scrollX - getWidth();
                if (m_availableToScroll > 0) {
                    scrollBy(Math.min(m_availableToScroll, m_deltaX), 0);
                }
            }
        }
        break;
    case MotionEvent.ACTION_UP:
        if (m_touchState == TOUCH_STATE_HORIZONTAL_SCROLLING) {
            final VelocityTracker m_velocityTrack = m_velocityTracker;
            m_velocityTrack.computeCurrentVelocity(
                    VELOCITY_UNIT_PIXELS_PER_SECOND, m_maximumVelocity);
            int m_velocityX = (int) m_velocityTrack.getXVelocity();
            if (m_velocityX > m_densityAdjustedSnapVelocity
                    && m_currentScreen > 0) {
                // Fling hard enough to move left
                snapToScreen(m_currentScreen - 1);
            } else if (m_velocityX < -m_densityAdjustedSnapVelocity
                    && m_currentScreen < getChildCount() - 1) {
                // Fling hard enough to move right
                snapToScreen(m_currentScreen + 1);
            } else {
                snapToDestination();
            }
            if (m_velocityTracker != null) {
                m_velocityTracker.recycle();
                m_velocityTracker = null;
            }
        }
        m_touchState = TOUCH_STATE_REST;
        break;
    case MotionEvent.ACTION_CANCEL:
        m_touchState = TOUCH_STATE_REST;
        break;
    default:
        break;
    }
    return true;
}
@Override
public void computeScroll() {
    if (m_scroller.computeScrollOffset()) {
        scrollTo(m_scroller.getCurrX(), m_scroller.getCurrY());
        postInvalidate();
    } else if (m_nextScreen != INVALID_SCREEN) {
        m_currentScreen = Math.max(0,
                Math.min(m_nextScreen, getChildCount() - 1));
        // Notify observer about screen change
        if (m_onScreenSwitchListener != null) {
            m_onScreenSwitchListener.onScreenSwitched(m_currentScreen);
        }
        m_nextScreen = INVALID_SCREEN;
    }
}
/**
 * Returns the index of the currently displayed screen.
 * 
 * @return The index of the currently displayed screen.
 */
public int getCurrentScreen() {
    return m_currentScreen;
}
/**
 * Sets the current screen.
 * 
 * @param p_currentScreen
 *            The new screen.
 * @param p_animate
 *            True to smoothly scroll to the screen, false to snap instantly
 */
public void setCurrentScreen(final int p_currentScreen,
        final boolean p_animate) {
    m_currentScreen = Math.max(0,
            Math.min(p_currentScreen, getChildCount() - 1));
    if (p_animate) {
        snapToScreen(p_currentScreen, ANIMATION_SCREEN_SET_DURATION_MILLIS);
    } else {
        scrollTo(m_currentScreen * getWidth(), 0);
    }
    invalidate();
}
/**
 * Sets the {@link OnScreenSwitchListener}.
 * 
 * @param onScreenSwitchListener
 *            The listener for switch events.
 */
public void setOnScreenSwitchListener(
        final OnScreenSwitchListener onScreenSwitchListener) {
    m_onScreenSwitchListener = 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 m_screenWidth = getWidth();
    int m_scrollX = getScrollX();
    int m_whichScreen = m_currentScreen;
    int m_deltaX = m_scrollX - (m_screenWidth * m_currentScreen);
    // Check if they want to go to the prev. screen
    if ((m_deltaX < 0)
            && m_currentScreen != 0
            && ((m_screenWidth / FRACTION_OF_SCREEN_WIDTH_FOR_SWIPE) < -m_deltaX)) {
        m_whichScreen--;
        // Check if they want to go to the next screen
    } else if ((m_deltaX > 0)
            && (m_currentScreen + 1 != getChildCount())
            && ((m_screenWidth / FRACTION_OF_SCREEN_WIDTH_FOR_SWIPE) < m_deltaX)) {
        m_whichScreen++;
    }
    snapToScreen(m_whichScreen);
}
/**
 * Snap to a specific screen, animating automatically for a duration
 * proportional to the distance left to scroll.
 * 
 * @param p_whichScreen
 *            Screen to snap to
 */
private void snapToScreen(final int p_whichScreen) {
    snapToScreen(p_whichScreen, -1);
}
/**
 * Snaps to a specific screen, animating for a specific amount of time to
 * get there.
 * 
 * @param p_whichScreen
 *            Screen to snap to
 * @param p_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 p_whichScreen, final int p_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.
     */
    m_nextScreen = Math
            .max(0, Math.min(p_whichScreen, getChildCount() - 1));
    final int m_newX = m_nextScreen * getWidth();
    final int m_delta = m_newX - getScrollX();
    if (p_duration < 0) {
        // E.g. if they've scrolled 80% of the way, only animation for 20%
        // of the duration
        m_scroller
                .startScroll(
                        getScrollX(),
                        0,
                        m_delta,
                        0,
                        (int) (Math.abs(m_delta) / (float) getWidth() * ANIMATION_SCREEN_SET_DURATION_MILLIS));
    } else {
        m_scroller.startScroll(getScrollX(), 0, m_delta, 0, p_duration);
    }
    // sets the drawables when the user swipes
    setActiveInactiveDrawables(p_whichScreen);
    // redraw screen
    invalidate();
}
/**
 * Allways called when the user swipes to another view. Gets the current
 * view an sets the active drawable / shape to the curretn view. (in the
 * page control)
 */
public void setActiveInactiveDrawables(int p_whichScreen) {
    // Getting the Linear Layout where the page control drawables are inside
    LinearLayout m_linLayout = (LinearLayout) ((ViewGroup) this.getParent())
            .getChildAt(((LinearLayout) this.getParent()).getChildCount() - 1);
    // get every imageview and set the one of the current screen to active
    for (int m_i = 0; m_i < this.getChildCount(); m_i++) {
        ImageView m_imgView = (ImageView) m_linLayout.getChildAt(m_i);
        if (m_i == p_whichScreen) {
            m_imgView.setBackgroundDrawable(m_activeDrawable);
        } else {
            m_imgView.setBackgroundDrawable(m_inactiveDrawable);
        }
    }
}
/**
 * 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);
}
/**
 * Builds the active and inactive shapes / drawables for the page control
 */
private void makeShapes() {
    m_activeDrawable = new ShapeDrawable();
    m_inactiveDrawable = new ShapeDrawable();
    m_activeDrawable.setBounds(0, 0, (int) m_indicatorSize,
            (int) m_indicatorSize);
    m_inactiveDrawable.setBounds(0, 0, (int) m_indicatorSize,
            (int) m_indicatorSize);
    int m_i[] = new int[2];
    m_i[0] = android.R.attr.textColorSecondary;
    m_i[1] = android.R.attr.textColorSecondaryInverse;
    TypedArray a = getContext().getTheme().obtainStyledAttributes(m_i);
    Shape m_s1 = new OvalShape();
    m_s1.resize(m_indicatorSize, m_indicatorSize);
    Shape m_s2 = new OvalShape();
    m_s2.resize(m_indicatorSize, m_indicatorSize);
    ((ShapeDrawable) m_activeDrawable).getPaint().setColor(
            a.getColor(0, Color.DKGRAY));
    ((ShapeDrawable) m_inactiveDrawable).getPaint().setColor(
            a.getColor(1, Color.LTGRAY));
    ((ShapeDrawable) m_activeDrawable).setShape(m_s1);
    ((ShapeDrawable) m_inactiveDrawable).setShape(m_s2);
}
/**
 * Called by the Activity when all Views are added to the horizontal pager
 * to
 */
public void addPagerControl() {
    ViewGroup m_view = (ViewGroup) this.getParent();
    m_view.addView(initPageControl());
}
private LinearLayout initPageControl() {
    LinearLayout m_linearLayout = new LinearLayout(getContext());
    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.FILL_PARENT,
            LinearLayout.LayoutParams.FILL_PARENT, 2f);
    m_linearLayout.setLayoutParams(params);
    m_linearLayout.setOrientation(LinearLayout.HORIZONTAL);
    m_linearLayout.setGravity(Gravity.CENTER_HORIZONTAL
            | Gravity.CENTER_VERTICAL);
    m_linearLayout.setBackgroundColor(Color.BLACK);
    return setPageCount(this.getChildCount(), m_linearLayout);
}
/**
 * Initializes the page control layout at be bottom of the news view. Draws
 * all page control shapes an set the active shape to the first view
 * 
 * @param p_pageCount
 *            the cout of the pages the user can swipe to
 * @param p_linearLayout2
 *            the page control linearlayout
 * @return the given layout filled with the page control shapes
 */
private LinearLayout setPageCount(int p_pageCount,
        LinearLayout p_linearLayout2) {
    for (int m_i = 0; m_i < p_pageCount; m_i++) {
        final ImageView m_imageView = new ImageView(getContext());
        LinearLayout.LayoutParams m_params = new LinearLayout.LayoutParams(
                (int) m_indicatorSize, (int) m_indicatorSize);
        m_params.setMargins((int) m_indicatorSize / 2,
                (int) m_indicatorSize, (int) m_indicatorSize / 2,
                (int) m_indicatorSize);
        m_imageView.setLayoutParams(m_params);
        m_imageView.setBackgroundDrawable(m_inactiveDrawable);
        if (m_i == 0) {
            m_imageView.setBackgroundDrawable(m_activeDrawable);
        }
        p_linearLayout2.addView(m_imageView);
    }
    return p_linearLayout2;
}
   }

使用以上calss如下:

 private HorizontalPagerWithPageControl m_pager;
@Override
public void onCreate(final Bundle p_savedInstanceState) {
    super.onCreate(p_savedInstanceState);
    setContentView(R.layout.horizontal_pager_with_page_control);
    m_pager = (HorizontalPagerWithPageControl) findViewById(R.id.hphorizontal_pager);
    /*
     * You can add more views to the horizontal pager here with
     * mPager.addChild() or in xml. When every view is in the horizontal
     * pager just call addPagerControl() on the horzizontal pager.
     */
    m_pager.addPagerControl();
}

布局文件:

  <com.wli.horizontalpager.withpagecontrol.HorizontalPagerWithPageControl
    android:id="@+id/hphorizontal_pager"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_weight="0.1"
    android:background="#00000000">
        <TextView
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:text="@string/lipsum"
            android:textSize="24sp"
            android:textColor="#000"
            android:textStyle="bold"
            android:background="#f00" />
    <ScrollView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
        <TextView
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:text="@string/lipsum"
            android:textSize="24sp"
            android:textColor="#000"
            android:textStyle="bold"
            android:background="#0f0" />
    </ScrollView>
       </com.wli.horizontalpager.withpagecontrol.HorizontalPagerWithPageControl>

我希望它会对你有所帮助。

感谢。