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

来源:互联网 发布:2016年11月网络热搜 编辑:程序博客网 时间:2024/04/25 13:56

一、前言

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

二、效果图

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

三、布局文件

     主布局 main2.xml

<?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

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);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {super.onInterceptTouchEvent(ev);return mGestureDetector.onTouchEvent(ev);}class YScrollDetector extends SimpleOnGestureListener {@Overridepublic 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

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

      这个很简单,看看就能懂~~
<?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中国






2 0
原创粉丝点击