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
- HorizontalListView的应用:Listview中完美嵌套HorizontalListView
- HorizontalListView的应用:Listview中完美嵌套HorizontalListView
- ListView中嵌套HorizontalListView问题
- listview 嵌套HorizontalListView
- HorizontalListView横着的Listview
- HorizontalListView
- HorizontalListView
- HorizontalListView
- HorizontalListView
- Android中HorizontalListView的实现
- 自定义控件HorizontalListView,横向的ListView
- 自定义控件HorizontalListView,横向的ListView
- HorizontalListView 横向listview
- android横向ListView(HorizontalListView)
- 横向listview------ HorizontalListView
- HorizontalListView 横向listview
- HorizontalListView 横向listview
- Android 横向ListView HorizontalListView
- UI 动画之CALayer+CAAnimation 之 CATransition
- 文本挖掘概念学习(一)
- UVALive 5874 - Social Holidaying(一般图匹配带花树)
- Git merge 合并分区详解
- point on C_4 语句
- HorizontalListView的应用:Listview中完美嵌套HorizontalListView
- Extjs extjs datefield日期格式
- iOS开发------多线程编程(2)
- point on C_5 操作符和表达式
- strchrMy的实现
- ML基石_9_LinearRegression
- 大整数加法
- Android Studio 之路1
- 程序在内存中占的四个区域