Android中实现水平滑动(横向滑动)ListView示例
水平的ListView-HorizontalListView的使用
Android中ListView默认的是竖直方向的滑动,由于项目的需求,需要ListView是水平滑动的。有很多的方式可以实现,但是比较好的一种方式就是自己封装一个控件,使用方式和ListView的使用方式是一样的。需要完善的地方:获取到的图片大小没有处理。在界面上展示的是图片的原大小。为了更好的展示效果,应该压缩成统一的尺寸。
HorizontalListView.java代码如下:
/** *横向的ListView * **@authorscd * */ publicclassHorizontalListViewextendsAdapterView<ListAdapter>{ publicbooleanmAlwaysOverrideTouch=true; protectedListAdaptermAdapter; privateintmLeftViewIndex=-1; privateintmRightViewIndex=0; protectedintmCurrentX; protectedintmNextX; privateintmMaxX=Integer.MAX_VALUE; privateintmDisplayOffset=0; protectedScrollermScroller; privateGestureDetectormGesture; privateQueue<View>mRemovedViewQueue=newLinkedList<View>(); privateOnItemSelectedListenermOnItemSelected; privateOnItemClickListenermOnItemClicked; privateOnItemLongClickListenermOnItemLongClicked; privatebooleanmDataChanged=false; publicHorizontalListView(Contextcontext,AttributeSetattrs){ super(context,attrs); initView(); } privatesynchronizedvoidinitView(){ mLeftViewIndex=-1; mRightViewIndex=0; mDisplayOffset=0; mCurrentX=0; mNextX=0; mMaxX=Integer.MAX_VALUE; mScroller=newScroller(getContext()); mGesture=newGestureDetector(getContext(),mOnGesture); } @Override publicvoidsetOnItemSelectedListener( AdapterView.OnItemSelectedListenerlistener){ mOnItemSelected=listener; } @Override publicvoidsetOnItemClickListener(AdapterView.OnItemClickListenerlistener){ mOnItemClicked=listener; } @Override publicvoidsetOnItemLongClickListener( AdapterView.OnItemLongClickListenerlistener){ mOnItemLongClicked=listener; } privateDataSetObservermDataObserver=newDataSetObserver(){ @Override publicvoidonChanged(){ synchronized(HorizontalListView.this){ mDataChanged=true; } invalidate(); requestLayout(); } @Override publicvoidonInvalidated(){ reset(); invalidate(); requestLayout(); } }; @Override publicListAdaptergetAdapter(){ returnmAdapter; } @Override publicViewgetSelectedView(){ //TODO:implement returnnull; } @Override publicvoidsetAdapter(ListAdapteradapter){ if(mAdapter!=null){ mAdapter.unregisterDataSetObserver(mDataObserver); } mAdapter=adapter; mAdapter.registerDataSetObserver(mDataObserver); reset(); } privatesynchronizedvoidreset(){ initView(); removeAllViewsInLayout(); requestLayout(); } @Override publicvoidsetSelection(intposition){ //TODO:implement } privatevoidaddAndMeasureChild(finalViewchild,intviewPos){ LayoutParamsparams=child.getLayoutParams(); if(params==null){ params=newLayoutParams(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 protectedsynchronizedvoidonLayout(booleanchanged,intleft,inttop, intright,intbottom){ super.onLayout(changed,left,top,right,bottom); if(mAdapter==null){ return; } if(mDataChanged){ intoldCurrentX=mCurrentX; initView(); removeAllViewsInLayout(); mNextX=oldCurrentX; mDataChanged=false; } if(mScroller.computeScrollOffset()){ intscrollx=mScroller.getCurrX(); mNextX=scrollx; } if(mNextX<=0){ mNextX=0; mScroller.forceFinished(true); } if(mNextX>=mMaxX){ mNextX=mMaxX; mScroller.forceFinished(true); } intdx=mCurrentX-mNextX; removeNonVisibleItems(dx); fillList(dx); positionItems(dx); mCurrentX=mNextX; if(!mScroller.isFinished()){ post(newRunnable(){ @Override publicvoidrun(){ requestLayout(); } }); } } privatevoidfillList(finalintdx){ intedge=0; Viewchild=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); } privatevoidfillListRight(intrightEdge,finalintdx){ while(rightEdge+dx<getWidth() &&mRightViewIndex<mAdapter.getCount()){ Viewchild=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++; } } privatevoidfillListLeft(intleftEdge,finalintdx){ while(leftEdge+dx>0&&mLeftViewIndex>=0){ Viewchild=mAdapter.getView(mLeftViewIndex, mRemovedViewQueue.poll(),this); addAndMeasureChild(child,0); leftEdge-=child.getMeasuredWidth(); mLeftViewIndex--; mDisplayOffset-=child.getMeasuredWidth(); } } privatevoidremoveNonVisibleItems(finalintdx){ Viewchild=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); } } privatevoidpositionItems(finalintdx){ if(getChildCount()>0){ mDisplayOffset+=dx; intleft=mDisplayOffset; for(inti=0;i<getChildCount();i++){ Viewchild=getChildAt(i); intchildWidth=child.getMeasuredWidth(); child.layout(left,0,left+childWidth, child.getMeasuredHeight()); left+=childWidth+child.getPaddingRight(); } } } publicsynchronizedvoidscrollTo(intx){ mScroller.startScroll(mNextX,0,x-mNextX,0); requestLayout(); } @Override publicbooleandispatchTouchEvent(MotionEventev){ booleanhandled=super.dispatchTouchEvent(ev); handled|=mGesture.onTouchEvent(ev); returnhandled; } protectedbooleanonFling(MotionEvente1,MotionEvente2,floatvelocityX, floatvelocityY){ synchronized(HorizontalListView.this){ mScroller.fling(mNextX,0,(int)-velocityX,0,0,mMaxX,0,0); } requestLayout(); returntrue; } protectedbooleanonDown(MotionEvente){ mScroller.forceFinished(true); returntrue; } privateOnGestureListenermOnGesture=newGestureDetector.SimpleOnGestureListener(){ @Override publicbooleanonDown(MotionEvente){ returnHorizontalListView.this.onDown(e); } @Override publicbooleanonFling(MotionEvente1,MotionEvente2,floatvelocityX, floatvelocityY){ returnHorizontalListView.this .onFling(e1,e2,velocityX,velocityY); } @Override publicbooleanonScroll(MotionEvente1,MotionEvente2, floatdistanceX,floatdistanceY){ synchronized(HorizontalListView.this){ mNextX+=(int)distanceX; } requestLayout(); returntrue; } @Override publicbooleanonSingleTapConfirmed(MotionEvente){ for(inti=0;i<getChildCount();i++){ Viewchild=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; } } returntrue; } @Override publicvoidonLongPress(MotionEvente){ intchildCount=getChildCount(); for(inti=0;i<childCount;i++){ Viewchild=getChildAt(i); if(isEventWithinView(e,child)){ if(mOnItemLongClicked!=null){ mOnItemLongClicked.onItemLongClick( HorizontalListView.this,child,mLeftViewIndex +1+i, mAdapter.getItemId(mLeftViewIndex+1+i)); } break; } } } privatebooleanisEventWithinView(MotionEvente,Viewchild){ RectviewRect=newRect(); int[]childPosition=newint[2]; child.getLocationOnScreen(childPosition); intleft=childPosition[0]; intright=left+child.getWidth(); inttop=childPosition[1]; intbottom=top+child.getHeight(); viewRect.set(left,top,right,bottom); returnviewRect.contains((int)e.getRawX(),(int)e.getRawY()); } }; }
适配器HorizontalListViewAdapter.java如下:
publicclassHorizontalListViewAdapterextendsBaseAdapter{ /**上下文*/ privateContextmContext; /**图像数据源*/ privateArrayList<Map<String,Integer>>mImageList; /**数据源*/ privateArrayList<Map<String,Integer>>mTextList; /**Image*/ privatestaticStringIMAGE="ic_"; privateMap<String,Integer>mMap=null; /**构造方法*/ publicHorizontalListViewAdapter(Contextcontext){ this.mContext=context; initData(); } /**初始化数据*/ publicvoidinitData(){ mImageList=newArrayList<Map<String,Integer>>(); /* *反射技术 */ Class<?>imageClzz=R.drawable.class; R.drawableinstance=newR.drawable(); //取得drawable类中所有的字段 Field[]fields=imageClzz.getDeclaredFields(); for(Fieldfield:fields){ //获得字段的名字 Stringname=field.getName(); if(name!=null&&name.startsWith(IMAGE)){ try{ mMap=newHashMap<String,Integer>(); mMap.put(IMAGE,(Integer)field.get(instance)); mImageList.add(mMap); }catch(IllegalAccessExceptione){ e.printStackTrace(); } } } } @Override publicintgetCount(){ returnmImageList.size(); } @Override publicMap<String,Integer>getItem(intposition){ returnmImageList==null?null:mImageList.get(position); } @Override publiclonggetItemId(intposition){ returnposition; } @Override publicViewgetView(intposition,ViewconvertView,ViewGroupparent){ ViewHolderholder; if(convertView==null){ holder=newViewHolder(); convertView=LayoutInflater.from(mContext).inflate( R.layout.horizontal_list_item,null); holder.mImage=(ImageView)convertView .findViewById(R.id.iv_list_item); holder.mTitle=(TextView)convertView .findViewById(R.id.tv_list_item); convertView.setTag(holder); }else{ holder=(ViewHolder)convertView.getTag(); } if(position==mSelectIndex){ convertView.setSelected(true); }else{ convertView.setSelected(false); } holder.mImage.setImageResource(getItem(position).get(IMAGE)); returnconvertView; } privateclassViewHolder{ /**图像*/ privateImageViewmImage; } }