HorizontalListView的应用:Listview中完美嵌套HorizontalListView



一、前言

 最近做的东西里面碰到一个界面中要求Listview中嵌套一个水平的Listview,看了很多网上的资料,对于单独的水平Listview,采用的是国外一位大牛继承AdapterView<ListAdapter>构造的HorizontalListView,接下来问题来了,哈哈,父Listview如果直接使用原生的Listview会出现滑动事件冲突的问题(当在HorizontalListView上下滑动时,父Listview并没有出现滑动的效果),因此,对于父Listview,我自己自定义了一下用以解决该问题。

二、效果图

    废话不多说,直接上效果图~o(∩_∩)o

三、布局文件

     主布局 main2.xml

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:background="#F0F0F0"  
  6.     android:orientation="vertical" >  
  7.   
  8.     <com.hiwhitley.demo.MyListview  
  9.         android:id="@+id/mylist"  
  10.         android:layout_width="wrap_content"  
  11.         android:layout_height="wrap_content"   
  12.         android:divider="#F0F0F0"  
  13.         android:dividerHeight="5dp"  
  14.         >  
  15.     </com.hiwhitley.demo.MyListview>  
  16.   
  17. </LinearLayout>  
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#F0F0F0"
    android:orientation="vertical" >

    <com.hiwhitley.demo.MyListview
        android:id="@+id/mylist"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:divider="#F0F0F0"
        android:dividerHeight="5dp"
        >
    </com.hiwhitley.demo.MyListview>

</LinearLayout>

四、部分代码

        自定义MyListView.java

  1. package com.hiwhitley.demo;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5. import android.view.GestureDetector;  
  6. import android.view.GestureDetector.SimpleOnGestureListener;  
  7. import android.view.MotionEvent;  
  8. import android.view.View;  
  9. import android.widget.ListView;  
  10.   
  11. public class MyListview extends ListView {  
  12.   
  13.     public MyListview(Context context, AttributeSet attrs, int defStyle) {  
  14.         super(context, attrs, defStyle);  
  15.     }  
  16.   
  17.     private GestureDetector mGestureDetector;  
  18.     View.OnTouchListener mGestureListener;  
  19.   
  20.     public MyListview(Context context, AttributeSet attrs) {  
  21.         super(context, attrs);  
  22.         mGestureDetector = new GestureDetector(new YScrollDetector());  
  23.         setFadingEdgeLength(0);  
  24.     }  
  25.   
  26.     @Override  
  27.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  28.         super.onInterceptTouchEvent(ev);  
  29.         return mGestureDetector.onTouchEvent(ev);  
  30.   
  31.     }  
  32.   
  33.     class YScrollDetector extends SimpleOnGestureListener {  
  34.         @Override  
  35.         public boolean onScroll(MotionEvent e1, MotionEvent e2,  
  36.                 float distanceX, float distanceY) {  
  37.             //判断是否上下滑动  
  38.             if (Math.abs(distanceY) / Math.abs(distanceX) > 2) {  
  39.                 return true;//截获事件  
  40.             }  
  41.             return false;  
  42.         }  
  43.   
  44.     }  
  45.   
  46. }  
package com.hiwhitley.demo;

import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ListView;

public class MyListview extends ListView {

	public MyListview(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	private GestureDetector mGestureDetector;
	View.OnTouchListener mGestureListener;

	public MyListview(Context context, AttributeSet attrs) {
		super(context, attrs);
		mGestureDetector = new GestureDetector(new YScrollDetector());
		setFadingEdgeLength(0);
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		super.onInterceptTouchEvent(ev);
		return mGestureDetector.onTouchEvent(ev);

	}

	class YScrollDetector extends SimpleOnGestureListener {
		@Override
		public boolean onScroll(MotionEvent e1, MotionEvent e2,
				float distanceX, float distanceY) {
			//判断是否上下滑动
			if (Math.abs(distanceY) / Math.abs(distanceX) > 2) {
				return true;//截获事件
			}
			return false;
		}

	}

}

       这里存在着一个冲突,就是我们在上下滑动listview的时候,不想把这个滑动事件被水平listview消耗掉,因此我用了一个手势监听器来监听上下的滑动,如果是上下滑动,就在onInterceptTouchEvent()中截获该事件,这样就完美解决了。

    HorizontalListView.java

  1. package com.hiwhitley.demo;  
  2.   
  3. /*  
  4.  * HorizontalListView.java v1.5  
  5.  *  
  6.  *   
  7.  * The MIT License  
  8.  * Copyright (c) 2011 Paul Soucy (paul@dev-smart.com)  
  9.  *   
  10.  * Permission is hereby granted, free of charge, to any person obtaining a copy  
  11.  * of this software and associated documentation files (the "Software"), to deal  
  12.  * in the Software without restriction, including without limitation the rights  
  13.  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell  
  14.  * copies of the Software, and to permit persons to whom the Software is  
  15.  * furnished to do so, subject to the following conditions:  
  16.  *   
  17.  * The above copyright notice and this permission notice shall be included in  
  18.  * all copies or substantial portions of the Software.  
  19.  *   
  20.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  
  21.  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,  
  22.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE  
  23.  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER  
  24.  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  
  25.  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN  
  26.  * THE SOFTWARE.  
  27.  *  
  28.  */    
  29.     
  30.     
  31. import java.util.LinkedList;    
  32. import java.util.Queue;    
  33.     
  34. import android.content.Context;    
  35. import android.database.DataSetObserver;    
  36. import android.graphics.Rect;    
  37. import android.util.AttributeSet;    
  38. import android.view.GestureDetector;    
  39. import android.view.GestureDetector.OnGestureListener;    
  40. import android.view.MotionEvent;    
  41. import android.view.View;    
  42. import android.widget.AdapterView;    
  43. import android.widget.ListAdapter;    
  44. import android.widget.Scroller;    
  45.     
  46. public class HorizontalListView extends AdapterView<ListAdapter> {    
  47.     
  48.     public boolean mAlwaysOverrideTouch = true;    
  49.     protected ListAdapter mAdapter;    
  50.     private int mLeftViewIndex = -1;    
  51.     private int mRightViewIndex = 0;    
  52.     protected int mCurrentX;    
  53.     protected int mNextX;    
  54.     private int mMaxX = Integer.MAX_VALUE;    
  55.     private int mDisplayOffset = 0;    
  56.     protected Scroller mScroller;    
  57.     private GestureDetector mGesture;    
  58.     private Queue<View> mRemovedViewQueue = new LinkedList<View>();    
  59.     private OnItemSelectedListener mOnItemSelected;    
  60.     private OnItemClickListener mOnItemClicked;    
  61.     private OnItemLongClickListener mOnItemLongClicked;    
  62.     private boolean mDataChanged = false;    
  63.         
  64.     
  65.     public HorizontalListView(Context context, AttributeSet attrs) {    
  66.         super(context, attrs);    
  67.         initView();    
  68.     }    
  69.         
  70.     private synchronized void initView() {    
  71.         mLeftViewIndex = -1;    
  72.         mRightViewIndex = 0;    
  73.         mDisplayOffset = 0;    
  74.         mCurrentX = 0;    
  75.         mNextX = 0;    
  76.         mMaxX = Integer.MAX_VALUE;    
  77.         mScroller = new Scroller(getContext());    
  78.         mGesture = new GestureDetector(getContext(), mOnGesture);    
  79.     }    
  80.         
  81.     @Override    
  82.     public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) {    
  83.         mOnItemSelected = listener;    
  84.     }    
  85.         
  86.     @Override    
  87.     public void setOnItemClickListener(AdapterView.OnItemClickListener listener){    
  88.         mOnItemClicked = listener;    
  89.     }    
  90.         
  91.     @Override    
  92.     public void setOnItemLongClickListener(AdapterView.OnItemLongClickListener listener) {    
  93.         mOnItemLongClicked = listener;    
  94.     }    
  95.     
  96.     private DataSetObserver mDataObserver = new DataSetObserver() {    
  97.     
  98.         @Override    
  99.         public void onChanged() {    
  100.             synchronized(HorizontalListView.this){    
  101.                 mDataChanged = true;    
  102.             }    
  103.             invalidate();    
  104.             requestLayout();    
  105.         }    
  106.     
  107.         @Override    
  108.         public void onInvalidated() {    
  109.             reset();    
  110.             invalidate();    
  111.             requestLayout();    
  112.         }    
  113.             
  114.     };    
  115.     
  116.     @Override    
  117.     public ListAdapter getAdapter() {    
  118.         return mAdapter;    
  119.     }    
  120.     
  121.     @Override    
  122.     public View getSelectedView() {    
  123.         //TODO: implement    
  124.         return null;    
  125.     }    
  126.     
  127.     @Override    
  128.     public void setAdapter(ListAdapter adapter) {    
  129.         if(mAdapter != null) {    
  130.             mAdapter.unregisterDataSetObserver(mDataObserver);    
  131.         }    
  132.         mAdapter = adapter;    
  133.         mAdapter.registerDataSetObserver(mDataObserver);    
  134.         reset();    
  135.     }    
  136.         
  137.     private synchronized void reset(){    
  138.         initView();    
  139.         removeAllViewsInLayout();    
  140.         requestLayout();    
  141.     }    
  142.     
  143.     @Override    
  144.     public void setSelection(int position) {    
  145.         //TODO: implement    
  146.     }    
  147.         
  148.     private void addAndMeasureChild(final View child, int viewPos) {    
  149.         LayoutParams params = child.getLayoutParams();    
  150.         if(params == null) {    
  151.             params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);    
  152.         }    
  153.     
  154.         addViewInLayout(child, viewPos, params, true);    
  155.         child.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),    
  156.                 MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));    
  157.     }    
  158.         
  159.         
  160.     
  161.     @Override    
  162.     protected synchronized void onLayout(boolean changed, int left, int top, int right, int bottom) {    
  163.         super.onLayout(changed, left, top, right, bottom);    
  164.     
  165.         if(mAdapter == null){    
  166.             return;    
  167.         }    
  168.             
  169.         if(mDataChanged){    
  170.             int oldCurrentX = mCurrentX;    
  171.             initView();    
  172.             removeAllViewsInLayout();    
  173.             mNextX = oldCurrentX;    
  174.             mDataChanged = false;    
  175.         }    
  176.     
  177.         if(mScroller.computeScrollOffset()){    
  178.             int scrollx = mScroller.getCurrX();    
  179.             mNextX = scrollx;    
  180.         }    
  181.             
  182.         if(mNextX <= 0){    
  183.             mNextX = 0;    
  184.             mScroller.forceFinished(true);    
  185.         }    
  186.         if(mNextX >= mMaxX) {    
  187.             mNextX = mMaxX;    
  188.             mScroller.forceFinished(true);    
  189.         }    
  190.             
  191.         int dx = mCurrentX - mNextX;    
  192.             
  193.         removeNonVisibleItems(dx);    
  194.         fillList(dx);    
  195.         positionItems(dx);    
  196.             
  197.         mCurrentX = mNextX;    
  198.             
  199.         if(!mScroller.isFinished()){    
  200.             post(new Runnable(){    
  201.                 @Override    
  202.                 public void run() {    
  203.                     requestLayout();    
  204.                 }    
  205.             });    
  206.                 
  207.         }    
  208.     }    
  209.         
  210.     private void fillList(final int dx) {    
  211.         int edge = 0;    
  212.         View child = getChildAt(getChildCount()-1);    
  213.         if(child != null) {    
  214.             edge = child.getRight();    
  215.         }    
  216.         fillListRight(edge, dx);    
  217.             
  218.         edge = 0;    
  219.         child = getChildAt(0);    
  220.         if(child != null) {    
  221.             edge = child.getLeft();    
  222.         }    
  223.         fillListLeft(edge, dx);    
  224.             
  225.             
  226.     }    
  227.         
  228.     private void fillListRight(int rightEdge, final int dx) {    
  229.         while(rightEdge + dx < getWidth() && mRightViewIndex < mAdapter.getCount()) {    
  230.                 
  231.             View child = mAdapter.getView(mRightViewIndex, mRemovedViewQueue.poll(), this);    
  232.             addAndMeasureChild(child, -1);    
  233.             rightEdge += child.getMeasuredWidth();    
  234.                 
  235.             if(mRightViewIndex == mAdapter.getCount()-1) {    
  236.                 mMaxX = mCurrentX + rightEdge - getWidth();    
  237.             }    
  238.                 
  239.             if (mMaxX < 0) {    
  240.                 mMaxX = 0;    
  241.             }    
  242.             mRightViewIndex++;    
  243.         }    
  244.             
  245.     }    
  246.         
  247.     private void fillListLeft(int leftEdge, final int dx) {    
  248.         while(leftEdge + dx > 0 && mLeftViewIndex >= 0) {    
  249.             View child = mAdapter.getView(mLeftViewIndex, mRemovedViewQueue.poll(), this);    
  250.             addAndMeasureChild(child, 0);    
  251.             leftEdge -= child.getMeasuredWidth();    
  252.             mLeftViewIndex--;    
  253.             mDisplayOffset -= child.getMeasuredWidth();    
  254.         }    
  255.     }    
  256.         
  257.     private void removeNonVisibleItems(final int dx) {    
  258.         View child = getChildAt(0);    
  259.         while(child != null && child.getRight() + dx <= 0) {    
  260.             mDisplayOffset += child.getMeasuredWidth();    
  261.             mRemovedViewQueue.offer(child);    
  262.             removeViewInLayout(child);    
  263.             mLeftViewIndex++;    
  264.             child = getChildAt(0);    
  265.                 
  266.         }    
  267.             
  268.         child = getChildAt(getChildCount()-1);    
  269.         while(child != null && child.getLeft() + dx >= getWidth()) {    
  270.             mRemovedViewQueue.offer(child);    
  271.             removeViewInLayout(child);    
  272.             mRightViewIndex--;    
  273.             child = getChildAt(getChildCount()-1);    
  274.         }    
  275.     }    
  276.         
  277.     private void positionItems(final int dx) {    
  278.         if(getChildCount() > 0){    
  279.             mDisplayOffset += dx;    
  280.             int left = mDisplayOffset;    
  281.             for(int i=0;i<getChildCount();i++){    
  282.                 View child = getChildAt(i);    
  283.                 int childWidth = child.getMeasuredWidth();    
  284.                 child.layout(left, 0, left + childWidth, child.getMeasuredHeight());    
  285.                 left += childWidth + child.getPaddingRight();    
  286.             }    
  287.         }    
  288.     }    
  289.         
  290.     public synchronized void scrollTo(int x) {    
  291.         mScroller.startScroll(mNextX, 0, x - mNextX, 0);    
  292.         requestLayout();    
  293.     }    
  294.         
  295.       
  296.     @Override    
  297.     public boolean dispatchTouchEvent(MotionEvent ev) {    
  298.         boolean handled = super.dispatchTouchEvent(ev);    
  299.         handled |= mGesture.onTouchEvent(ev);    
  300.         return handled;    
  301.     }    
  302.         
  303.     protected boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,    
  304.                 float velocityY) {    
  305.         synchronized(HorizontalListView.this){    
  306.             mScroller.fling(mNextX, 0, (int)-velocityX, 00, mMaxX, 00);    
  307.         }    
  308.         requestLayout();    
  309.             
  310.         return true;    
  311.     }    
  312.         
  313.     protected boolean onDown(MotionEvent e) {    
  314.         mScroller.forceFinished(true);    
  315.         return true;    
  316.     }    
  317.         
  318.     private OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() {    
  319.     
  320.         @Override    
  321.         public boolean onDown(MotionEvent e) {    
  322.             return HorizontalListView.this.onDown(e);    
  323.         }    
  324.     
  325.         @Override    
  326.         public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,    
  327.                 float velocityY) {    
  328.             return HorizontalListView.this.onFling(e1, e2, velocityX, velocityY);    
  329.         }    
  330.     
  331.         @Override    
  332.         public boolean onScroll(MotionEvent e1, MotionEvent e2,    
  333.                 float distanceX, float distanceY) {    
  334.                 
  335.             if (Math.abs(distanceY) > Math.abs(distanceX)) {  
  336.                 return true;  
  337.             }  
  338.               
  339.             synchronized(HorizontalListView.this){    
  340.                 mNextX += (int)distanceX;    
  341.             }    
  342.             requestLayout();    
  343.                 
  344.             return true;    
  345.         }    
  346.     
  347.         @Override    
  348.         public boolean onSingleTapConfirmed(MotionEvent e) {    
  349.             for(int i=0;i<getChildCount();i++){    
  350.                 View child = getChildAt(i);    
  351.                 if (isEventWithinView(e, child)) {    
  352.                     if(mOnItemClicked != null){    
  353.                         mOnItemClicked.onItemClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ));    
  354.                     }    
  355.                     if(mOnItemSelected != null){    
  356.                         mOnItemSelected.onItemSelected(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ));    
  357.                     }    
  358.                     break;    
  359.                 }    
  360.                     
  361.             }    
  362.             return true;    
  363.         }    
  364.             
  365.         @Override    
  366.         public void onLongPress(MotionEvent e) {    
  367.             int childCount = getChildCount();    
  368.             for (int i = 0; i < childCount; i++) {    
  369.                 View child = getChildAt(i);    
  370.                 if (isEventWithinView(e, child)) {    
  371.                     if (mOnItemLongClicked != null) {    
  372.                         mOnItemLongClicked.onItemLongClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i));    
  373.                     }    
  374.                     break;    
  375.                 }    
  376.     
  377.             }    
  378.         }    
  379.     
  380.         private boolean isEventWithinView(MotionEvent e, View child) {    
  381.             Rect viewRect = new Rect();    
  382.             int[] childPosition = new int[2];    
  383.             child.getLocationOnScreen(childPosition);    
  384.             int left = childPosition[0];    
  385.             int right = left + child.getWidth();    
  386.             int top = childPosition[1];    
  387.             int bottom = top + child.getHeight();    
  388.             viewRect.set(left, top, right, bottom);    
  389.             return viewRect.contains((int) e.getRawX(), (int) e.getRawY());    
  390.         }    
  391.     };    
  392.      
  393.     
  394. }    
package com.hiwhitley.demo;

/* 
 * HorizontalListView.java v1.5 
 * 
 *  
 * The MIT License 
 * Copyright (c) 2011 Paul Soucy (paul@dev-smart.com) 
 *  
 * 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. 
 * 
 */  
  
  
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 HorizontalListView 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 OnItemLongClickListener mOnItemLongClicked;  
    private boolean mDataChanged = false;  
      
  
    public HorizontalListView(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;  
    }  
      
    @Override  
    public void setOnItemLongClickListener(AdapterView.OnItemLongClickListener listener) {  
        mOnItemLongClicked = listener;  
    }  
  
    private DataSetObserver mDataObserver = new DataSetObserver() {  
  
        @Override  
        public void onChanged() {  
            synchronized(HorizontalListView.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(){  
                @Override  
                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();  
            }  
              
            if (mMaxX < 0) {  
                mMaxX = 0;  
            }  
            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 + child.getPaddingRight();  
            }  
        }  
    }  
      
    public synchronized void scrollTo(int x) {  
        mScroller.startScroll(mNextX, 0, x - mNextX, 0);  
        requestLayout();  
    }  
      
    
    @Override  
    public boolean dispatchTouchEvent(MotionEvent ev) {  
        boolean handled = super.dispatchTouchEvent(ev);  
        handled |= mGesture.onTouchEvent(ev);  
        return handled;  
    }  
      
    protected boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,  
                float velocityY) {  
        synchronized(HorizontalListView.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 HorizontalListView.this.onDown(e);  
        }  
  
        @Override  
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,  
                float velocityY) {  
            return HorizontalListView.this.onFling(e1, e2, velocityX, velocityY);  
        }  
  
        @Override  
        public boolean onScroll(MotionEvent e1, MotionEvent e2,  
                float distanceX, float distanceY) {  
              
        	if (Math.abs(distanceY) > Math.abs(distanceX)) {
        		return true;
        	}
        	
            synchronized(HorizontalListView.this){  
                mNextX += (int)distanceX;  
            }  
            requestLayout();  
              
            return true;  
        }  
  
        @Override  
        public boolean onSingleTapConfirmed(MotionEvent e) {  
            for(int i=0;i<getChildCount();i++){  
                View child = getChildAt(i);  
                if (isEventWithinView(e, child)) {  
                    if(mOnItemClicked != null){  
                        mOnItemClicked.onItemClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ));  
                    }  
                    if(mOnItemSelected != null){  
                        mOnItemSelected.onItemSelected(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ));  
                    }  
                    break;  
                }  
                  
            }  
            return true;  
        }  
          
        @Override  
        public void onLongPress(MotionEvent e) {  
            int childCount = getChildCount();  
            for (int i = 0; i < childCount; i++) {  
                View child = getChildAt(i);  
                if (isEventWithinView(e, child)) {  
                    if (mOnItemLongClicked != null) {  
                        mOnItemLongClicked.onItemLongClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i));  
                    }  
                    break;  
                }  
  
            }  
        }  
  
        private boolean isEventWithinView(MotionEvent e, View child) {  
            Rect viewRect = new Rect();  
            int[] childPosition = new int[2];  
            child.getLocationOnScreen(childPosition);  
            int left = childPosition[0];  
            int right = left + child.getWidth();  
            int top = childPosition[1];  
            int bottom = top + child.getHeight();  
            viewRect.set(left, top, right, bottom);  
            return viewRect.contains((int) e.getRawX(), (int) e.getRawY());  
        }  
    };  
   
  
}  

水平listview的item项 item_goods.xml

      这个很简单,看看就能懂~~
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="wrap_content"  
  5.     android:background="@drawable/grass_green_round_stroke_no_padding_bg"  
  6.     android:orientation="vertical" >  
  7.   
  8.   
  9.         <RelativeLayout  
  10.             android:layout_width="wrap_content"  
  11.             android:layout_height="match_parent"  
  12.             android:orientation="horizontal"  
  13.             android:gravity="center_vertical"  
  14.             android:padding="5dp" >  
  15.   
  16.             <TextView  
  17.                 android:id="@+id/tv_name"  
  18.                 android:layout_width="wrap_content"  
  19.                 android:layout_height="wrap_content"  
  20.                 android:layout_marginLeft="30dp"  
  21.                 android:text="小明"  
  22.                 android:textColor="#FF333333"  
  23.                 android:textSize="18sp" />  
  24.   
  25.             <TextView  
  26.                 android:id="@+id/tv_org"  
  27.                 android:layout_width="wrap_content"  
  28.                 android:layout_height="wrap_content"  
  29.                 android:layout_alignParentRight="true"  
  30.                 android:layout_marginRight="20dp"  
  31.                 android:text="CSDN"  
  32.                 android:textColor="#FF555555"  
  33.                 android:textSize="15sp" />  
  34.         </RelativeLayout>  
  35.   
  36.         <View  
  37.             android:layout_width="match_parent"  
  38.             android:layout_height="1px"  
  39.             android:background="#FFAAAAAA" />  
  40.   
  41.         <com.hiwhitley.demo.HorizontalListView  
  42.             android:id="@+id/horizontal_listview"  
  43.             android:layout_width="wrap_content"  
  44.             android:layout_height="150dp" >  
  45.         </com.hiwhitley.demo.HorizontalListView>  
  46.   
  47. </LinearLayout>  
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/grass_green_round_stroke_no_padding_bg"
    android:orientation="vertical" >


        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:orientation="horizontal"
            android:gravity="center_vertical"
            android:padding="5dp" >

            <TextView
                android:id="@+id/tv_name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="30dp"
                android:text="小明"
                android:textColor="#FF333333"
                android:textSize="18sp" />

            <TextView
                android:id="@+id/tv_org"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:layout_marginRight="20dp"
                android:text="CSDN"
                android:textColor="#FF555555"
                android:textSize="15sp" />
        </RelativeLayout>

        <View
            android:layout_width="match_parent"
            android:layout_height="1px"
            android:background="#FFAAAAAA" />

        <com.hiwhitley.demo.HorizontalListView
            android:id="@+id/horizontal_listview"
            android:layout_width="wrap_content"
            android:layout_height="150dp" >
        </com.hiwhitley.demo.HorizontalListView>

</LinearLayout>

五、总结

       实现还是比较简单,就是在事件冲突处理上花费了不少时间,希望这篇文章可以帮助到大家!o(∩_∩)o

          源码下载

    Git中国

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值