2013-02-09 139 views
4

我正在尝试在horizontal listview内创建一个通用列表视图。水平滚动很好,但垂直滚动不起作用。我试图使用this解决方案,但没有发生任何事情。我该怎么办?在列表视图中的垂直和水平滚动

Horizo​​ntalListviewDemo.java

package com.devsmart.android.ui; 

import java.util.ArrayList; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 

import android.app.Activity; 
import android.os.Bundle; 
import android.view.LayoutInflater; 
import android.view.MotionEvent; 
import android.view.View; 
import android.view.View.OnTouchListener; 
import android.view.ViewGroup; 
import android.widget.BaseAdapter; 
import android.widget.LinearLayout.LayoutParams; 
import android.widget.ListView; 
import android.widget.SimpleAdapter; 
import android.widget.TextView; 

import com.devsmart.android.ui.HorizontialListView; 
import com.example.horlistview.R; 

public class HorizontalListViewDemo extends Activity { 

    private final static String ITEM = "item"; 
    private static int COLUMN_COUNT = 20; 
    private static int ROW_COUNT = 50; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     setContentView(R.layout.listviewdemo); 

     HorizontialListView listview = (HorizontialListView) findViewById(R.id.listview); 
     listview.setAdapter(mAdapter); 
    } 

    static class VerticalViewHolder { 
     protected TextView textview; 
    } 

    public void setColumnCount(int n) { 
     COLUMN_COUNT = n; 
    } 

    public void setRowCount(int n) { 
     ROW_COUNT = n; 
    } 

    private BaseAdapter mAdapter = new BaseAdapter() { 

     public int getCount() { 
      return COLUMN_COUNT; 
     } 


     public Object getItem(int position) { 
      return null; 
     } 

     public long getItemId(int position) { 
      return 0; 
     } 

     public View getView(int position, View convertView, ViewGroup parent) { 
      final List<Map<String, String>> l = createList(); 
      convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.viewitem, null); 
      final ListView listview = (ListView) convertView.findViewById(R.id.viewitem_listview); 

      if (position==0) { 
       listview.setLayoutParams(new LayoutParams(parent.getWidth()-50, LayoutParams.WRAP_CONTENT)); 

       listview.setAdapter(new SimpleAdapter(
         getApplicationContext(), 
         l, 
         R.layout.viewitemitem, 
         new String[] {ITEM}, 
         new int[] {R.id.textView}) { 

        @Override 
        public View getView(int position, View convertView, ViewGroup parent) { 
         VerticalViewHolder verticalViewHolder = null; 
         if (convertView==null) { 
          convertView = super.getView(position, convertView, parent); 

          verticalViewHolder = new VerticalViewHolder(); 
          verticalViewHolder.textview = (TextView) convertView.findViewById(R.id.textView); 

          LayoutParams lp = (LayoutParams) verticalViewHolder.textview.getLayoutParams(); 
          if (position==0) lp.setMargins(1, 2, 1, 0); 
          else    lp.setMargins(1, 0, 1, 0); 

          convertView.setTag(verticalViewHolder); 
          convertView.setTag(R.id.textView, verticalViewHolder.textview); 
         } else { 
          verticalViewHolder = (VerticalViewHolder) convertView.getTag(); 
         } 
         verticalViewHolder.textview.setText(l.get(position).get(ITEM)); 
         return convertView; 
        } 
       }); 
      } 
      else { 
       if (COLUMN_COUNT>=5) 
        listview.setLayoutParams(new LayoutParams(parent.getWidth()/5, LayoutParams.WRAP_CONTENT)); 
       else 
        listview.setLayoutParams(new LayoutParams(parent.getWidth()/COLUMN_COUNT, LayoutParams.WRAP_CONTENT)); 

       listview.setAdapter(new SimpleAdapter(
         getApplicationContext(), 
         l, 
         R.layout.viewitemitem, 
         new String[] {ITEM}, 
         new int[] {R.id.textView}) { 

        @Override 
        public View getView(int position, View convertView, ViewGroup parent) { 
         View retval = super.getView(position, convertView, parent); 
         TextView textview = (TextView) retval.findViewById(R.id.textView); 
         LayoutParams lp = (LayoutParams) textview.getLayoutParams(); 
         if (position==0)    lp.setMargins(0, 2, 1, 0); 
         else if (position==ROW_COUNT-1) lp.setMargins(0, 0, 1, 1); 
         else       lp.setMargins(0, 0, 1, 0); 
         return retval; 
        } 
       }); 
      } 

      convertView.setOnTouchListener(new OnTouchListener() { 

       public boolean onTouch(View v, MotionEvent event) { 
        listview.setSmoothScrollbarEnabled(true); 
        listview.dispatchTouchEvent(event); 
        return false; 
       } 
      }); 

      return convertView; 
     } 

     private List<Map<String, String>> createList() { 
      List<Map<String, String>> values = new ArrayList<Map<String, String>>(); 
      Map<String, String> v = new HashMap<String, String>(); 

      for (int j = 0; j < ROW_COUNT; j++) { 
       v = new HashMap<String, String>(); 
       v.put(ITEM, ""+j); 
       values.add(v); 
      } 

      return values; 
     } 
    }; 

} 

Horizo​​ntalListView.java

/* 
* HorizontalListView.java v1.5 
* 
* 
* The MIT License 
* Copyright (c) 2011 Paul Soucy ([email protected]) 
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy 
* of this software and associated documentation files (the "Software"), to deal 
* in the Software without restriction, including without limitation the rights 
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
* copies of the Software, and to permit persons to whom the Software is 
* furnished to do so, subject to the following conditions: 
* 
* The above copyright notice and this permission notice shall be included in 
* all copies or substantial portions of the Software. 
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
* THE SOFTWARE. 
* 
*/ 

package com.devsmart.android.ui; 

import java.util.LinkedList; 
import java.util.Queue; 

import android.content.Context; 
import android.database.DataSetObserver; 
import android.graphics.Rect; 
import android.util.AttributeSet; 
import android.view.GestureDetector; 
import android.view.GestureDetector.OnGestureListener; 
import android.view.MotionEvent; 
import android.view.View; 
import android.widget.AdapterView; 
import android.widget.ListAdapter; 
import android.widget.Scroller; 

public class HorizontialListView extends AdapterView<ListAdapter> { 

    public boolean mAlwaysOverrideTouch = true; 
    protected ListAdapter mAdapter; 
    private int mLeftViewIndex = -1; 
    private int mRightViewIndex = 0; 
    protected int mCurrentX; 
    protected int mNextX; 
    private int mMaxX = Integer.MAX_VALUE; 
    private int mDisplayOffset = 0; 
    protected Scroller mScroller; 
    private GestureDetector mGesture; 
    private Queue<View> mRemovedViewQueue = new LinkedList<View>(); 
    private OnItemSelectedListener mOnItemSelected; 
    private OnItemClickListener mOnItemClicked; 
    private boolean mDataChanged = false; 


    public HorizontialListView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     initView(); 
    } 

    private synchronized void initView() { 
     mLeftViewIndex = -1; 
     mRightViewIndex = 0; 
     mDisplayOffset = 0; 
     mCurrentX = 0; 
     mNextX = 0; 
     mMaxX = Integer.MAX_VALUE; 
     mScroller = new Scroller(getContext()); 
     mGesture = new GestureDetector(getContext(), mOnGesture); 
    } 

    @Override 
    public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) { 
     mOnItemSelected = listener; 
    } 

    @Override 
    public void setOnItemClickListener(AdapterView.OnItemClickListener listener){ 
     mOnItemClicked = listener; 
    } 

    private DataSetObserver mDataObserver = new DataSetObserver() { 

     @Override 
     public void onChanged() { 
      synchronized(HorizontialListView.this){ 
       mDataChanged = true; 
      } 
      invalidate(); 
      requestLayout(); 
     } 

     @Override 
     public void onInvalidated() { 
      reset(); 
      invalidate(); 
      requestLayout(); 
     } 

    }; 

    @Override 
    public ListAdapter getAdapter() { 
     return mAdapter; 
    } 

    @Override 
    public View getSelectedView() { 
     //TODO: implement 
     return null; 
    } 

    @Override 
    public void setAdapter(ListAdapter adapter) { 
     if(mAdapter != null) { 
      mAdapter.unregisterDataSetObserver(mDataObserver); 
     } 
     mAdapter = adapter; 
     mAdapter.registerDataSetObserver(mDataObserver); 
     reset(); 
    } 

    private synchronized void reset(){ 
     initView(); 
     removeAllViewsInLayout(); 
     requestLayout(); 
    } 

    @Override 
    public void setSelection(int position) { 
     //TODO: implement 
    } 

    private void addAndMeasureChild(final View child, int viewPos) { 
     LayoutParams params = child.getLayoutParams(); 
     if(params == null) { 
      params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); 
     } 

     addViewInLayout(child, viewPos, params, true); 
     child.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST), 
       MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST)); 
    } 

    @Override 
    protected synchronized void onLayout(boolean changed, int left, int top, int right, int bottom) { 
     super.onLayout(changed, left, top, right, bottom); 

     if(mAdapter == null){ 
      return; 
     } 

     if(mDataChanged){ 
      int oldCurrentX = mCurrentX; 
      initView(); 
      removeAllViewsInLayout(); 
      mNextX = oldCurrentX; 
      mDataChanged = false; 
     } 

     if(mScroller.computeScrollOffset()){ 
      int scrollx = mScroller.getCurrX(); 
      mNextX = scrollx; 
     } 

     if(mNextX < 0){ 
      mNextX = 0; 
      mScroller.forceFinished(true); 
     } 
     if(mNextX > mMaxX) { 
      mNextX = mMaxX; 
      mScroller.forceFinished(true); 
     } 

     int dx = mCurrentX - mNextX; 

     removeNonVisibleItems(dx); 
     fillList(dx); 
     positionItems(dx); 

     mCurrentX = mNextX; 

     if(!mScroller.isFinished()){ 
      post(new Runnable(){ 
       public void run() { 
        requestLayout(); 
       } 
      }); 

     } 
    } 

    private void fillList(final int dx) { 
     int edge = 0; 
     View child = getChildAt(getChildCount()-1); 
     if(child != null) { 
      edge = child.getRight(); 
     } 
     fillListRight(edge, dx); 

     edge = 0; 
     child = getChildAt(0); 
     if(child != null) { 
      edge = child.getLeft(); 
     } 
     fillListLeft(edge, dx); 


    } 

    private void fillListRight(int rightEdge, final int dx) { 
     while(rightEdge + dx < getWidth() && mRightViewIndex < mAdapter.getCount()) { 

      View child = mAdapter.getView(mRightViewIndex, mRemovedViewQueue.poll(), this); 
      addAndMeasureChild(child, -1); 
      rightEdge += child.getMeasuredWidth(); 

      if(mRightViewIndex == mAdapter.getCount()-1){ 
       mMaxX = mCurrentX + rightEdge - getWidth(); 
      } 
      mRightViewIndex++; 
     } 

    } 

    private void fillListLeft(int leftEdge, final int dx) { 
     while(leftEdge + dx > 0 && mLeftViewIndex >= 0) { 
      View child = mAdapter.getView(mLeftViewIndex, mRemovedViewQueue.poll(), this); 
      addAndMeasureChild(child, 0); 
      leftEdge -= child.getMeasuredWidth(); 
      mLeftViewIndex--; 
      mDisplayOffset -= child.getMeasuredWidth(); 
     } 
    } 

    private void removeNonVisibleItems(final int dx) { 
     View child = getChildAt(0); 
     while(child != null && child.getRight() + dx <= 0) { 
      mDisplayOffset += child.getMeasuredWidth(); 
      mRemovedViewQueue.offer(child); 
      removeViewInLayout(child); 
      mLeftViewIndex++; 
      child = getChildAt(0); 

     } 

     child = getChildAt(getChildCount()-1); 
     while(child != null && child.getLeft() + dx >= getWidth()) { 
      mRemovedViewQueue.offer(child); 
      removeViewInLayout(child); 
      mRightViewIndex--; 
      child = getChildAt(getChildCount()-1); 
     } 
    } 

    private void positionItems(final int dx) { 
     if(getChildCount() > 0){ 
      mDisplayOffset += dx; 
      int left = mDisplayOffset; 
      for(int i=0;i<getChildCount();i++){ 
       View child = getChildAt(i); 
       int childWidth = child.getMeasuredWidth(); 
       child.layout(left, 0, left + childWidth, child.getMeasuredHeight()); 
       left += childWidth; 
      } 
     } 
    } 

    public synchronized void scrollTo(int x) { 
     mScroller.startScroll(mNextX, 0, x - mNextX, 0); 
     requestLayout(); 
    } 

    @Override 
    public boolean dispatchTouchEvent(MotionEvent ev) { 
     boolean handled = mGesture.onTouchEvent(ev); 
     return handled; 
    } 

    protected boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, 
       float velocityY) { 
     synchronized(HorizontialListView.this){ 
      mScroller.fling(mNextX, 0, (int)-velocityX, 0, 0, mMaxX, 0, 0); 
     } 
     requestLayout(); 

     return true; 
    } 

    protected boolean onDown(MotionEvent e) { 
     mScroller.forceFinished(true); 
     return true; 
    } 

    private OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() { 

     @Override 
     public boolean onDown(MotionEvent e) { 
      return HorizontialListView.this.onDown(e); 
     } 

     @Override 
     public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, 
       float velocityY) { 
      return HorizontialListView.this.onFling(e1, e2, velocityX, velocityY); 
     } 

     @Override 
     public boolean onScroll(MotionEvent e1, MotionEvent e2, 
       float distanceX, float distanceY) { 

      synchronized(HorizontialListView.this){ 
       mNextX += (int)distanceX; 
      } 
      requestLayout(); 

      return true; 
     } 

     @Override 
     public boolean onSingleTapConfirmed(MotionEvent e) { 
      Rect viewRect = new Rect(); 
      for(int i=0;i<getChildCount();i++){ 
       View child = getChildAt(i); 
       int left = child.getLeft(); 
       int right = child.getRight(); 
       int top = child.getTop(); 
       int bottom = child.getBottom(); 
       viewRect.set(left, top, right, bottom); 
       if(viewRect.contains((int)e.getX(), (int)e.getY())){ 
        if(mOnItemClicked != null){ 
         mOnItemClicked.onItemClick(HorizontialListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i)); 
        } 
        if(mOnItemSelected != null){ 
         mOnItemSelected.onItemSelected(HorizontialListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i)); 
        } 
        break; 
       }     
      } 
      return true; 
     }   
    };  
} 

listviewdemo.xml

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:orientation="vertical" > 

    <com.devsmart.android.ui.HorizontialListView 
     android:id="@+id/listview" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:background="#fff"/> 

</LinearLayout> 

viewitem.xml

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/viewitem_linearlayout" 
    android:layout_width="wrap_content" 
    android:layout_height="match_parent" > 

    <ListView 
     android:id="@+id/viewitem_listview" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" > 

    </ListView> 

</LinearLayout> 

viewitemitem.xml

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/viewitemitem" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:background="#fff" > 

    <TextView 
     android:id="@+id/textView" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:background="#000" 
     android:paddingLeft="3dp" 
     android:text="@string/hello_world" /> 

</LinearLayout> 

回答

4

试试我的下面的代码被定制HorizontalScrollingPage其提供垂直滚动&水平包含列表视图也视图的功能..

Horizo​​ntalPagerWithPageControl.class

package com.wli.horizontalpager.withpagecontrol; 
    import android.content.Context; 
    import android.content.res.TypedArray; 
    import android.graphics.Color; 
    import android.graphics.drawable.Drawable; 
    import android.graphics.drawable.ShapeDrawable; 
    import android.graphics.drawable.shapes.OvalShape; 
    import android.graphics.drawable.shapes.Shape; 
    import android.util.AttributeSet; 
    import android.util.DisplayMetrics; 
    import android.view.Display; 
    import android.view.Gravity; 
    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.ImageView; 
    import android.widget.LinearLayout; 
    import android.widget.Scroller;  
    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; 
} 
    } 

Horizo​​ntalPagerWithPageControlActivity.class

public class HorizontalPagerWithPageControlActivity extends Activity { 
private HorizontalPagerWithPageControl m_pager; 
// The data to show 
List<Map<String, String>> planetsList = new ArrayList<Map<String, String>>(); 
@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. 
    */ 
    initList(); 
    // We get the ListView component from the layout 
    ListView lv = (ListView) findViewById(R.id.listView); 
    // This is a simple adapter that accepts as parameter 
    // Context 
    // Data list 
    // The row layout that is used during the row creation 
    // The keys used to retrieve the data 
    // The View id used to show the data. The key number and the view id 
    // must match 
    SimpleAdapter simpleAdpt = new SimpleAdapter(this, planetsList, 
      android.R.layout.simple_list_item_1, new String[] { "planet" }, 
      new int[] { android.R.id.text1 }); 
    lv.setAdapter(simpleAdpt); 
    m_pager.addPagerControl(); 
} 
private void initList() { 
    // We populate the planets 
    planetsList.add(createPlanet("planet", "Mercury")); 
    planetsList.add(createPlanet("planet", "Venus")); 
    planetsList.add(createPlanet("planet", "Mars")); 
    planetsList.add(createPlanet("planet", "Jupiter")); 
    planetsList.add(createPlanet("planet", "Saturn")); 
    planetsList.add(createPlanet("planet", "Uranus")); 
    planetsList.add(createPlanet("planet", "Neptune")); 
} 
private HashMap<String, String> createPlanet(String key, String name) { 
    HashMap<String, String> planet = new HashMap<String, String>(); 
    planet.put(key, name); 
    return planet; 
} 
} 

horizo​​ntal_pager_with_page_control.xml

<?xml version="1.0" encoding="utf-8"?> 
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:orientation="vertical" > 
<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" > 
    <TextView 
     android:layout_width="fill_parent" 
     android:layout_height="fill_parent" 
     android:background="#f00" 
     android:text="@string/lipsum" 
     android:textColor="#000" 
     android:textSize="24sp" 
     android:textStyle="bold" /> 
    <ScrollView 
     android:layout_width="fill_parent" 
     android:layout_height="fill_parent" > 
     <TextView 
      android:layout_width="fill_parent" 
      android:layout_height="fill_parent" 
      android:background="#0f0" 
      android:text="@string/lipsum" 
      android:textColor="#000" 
      android:textSize="24sp" 
      android:textStyle="bold" /> 
    </ScrollView> 
     <ListView 
      android:id="@+id/listView" 
      android:layout_width="fill_parent" 
      android:layout_height="fill_parent" /> 
</com.wli.horizontalpager.withpagecontrol.HorizontalPagerWithPageControl> 
</LinearLayout> 

我希望这会帮助你。

谢谢。