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;
}
}