Android编程实现小说阅读器滑动效果的方法
本文实例讲述了Android编程实现小说阅读器滑动效果的方法。分享给大家供大家参考,具体如下:
看过小说都知道小说阅读器翻页有好多种效果,比如仿真翻页,滑动翻页,等等。由于某种原因,突然想写一个简单点的滑动翻页效果。在这里写出来也没有什么意图,希望大家可以根据这个效果举一反三,写出其他的效果。图就不上了。
下面是代码:大家理解onTouch事件即可
packagecom.example.testscroll.view; importandroid.content.Context; importandroid.util.AttributeSet; importandroid.view.MotionEvent; importandroid.view.VelocityTracker; importandroid.view.View; importandroid.view.ViewConfiguration; importandroid.view.ViewGroup; importandroid.widget.Scroller; publicclassFlipperLayoutextendsViewGroup{ privateScrollermScroller; privateVelocityTrackermVelocityTracker; privateintmVelocityValue=0; /**商定这个滑动是否有效的距离*/ privateintlimitDistance=0; privateintscreenWidth=0; /**手指移动的方向*/ privatestaticfinalintMOVE_TO_LEFT=0; privatestaticfinalintMOVE_TO_RIGHT=1; privatestaticfinalintMOVE_NO_RESULT=2; /**最后触摸的结果方向*/ privateintmTouchResult=MOVE_NO_RESULT; /**一开始的方向*/ privateintmDirection=MOVE_NO_RESULT; /**触摸的模式*/ privatestaticfinalintMODE_NONE=0; privatestaticfinalintMODE_MOVE=1; privateintmMode=MODE_NONE; /**滑动的view*/ privateViewmScrollerView=null; /**最上层的view(处于边缘的,看不到的)*/ privateViewcurrentTopView=null; /**显示的view,显示在屏幕*/ privateViewcurrentShowView=null; /**最底层的view(看不到的)*/ privateViewcurrentBottomView=null; publicFlipperLayout(Contextcontext){ super(context); init(context); } publicFlipperLayout(Contextcontext,AttributeSetattrs,intdefStyle){ super(context,attrs,defStyle); init(context); } publicFlipperLayout(Contextcontext,AttributeSetattrs){ super(context,attrs); init(context); } privatevoidinit(Contextcontext){ mScroller=newScroller(context); screenWidth=context.getResources().getDisplayMetrics().widthPixels; limitDistance=screenWidth/3; } /*** * *@paramlistener *@paramcurrentBottomView *最底层的view,初始状态看不到 *@paramcurrentShowView *正在显示的View *@paramcurrentTopView *最上层的View,初始化时滑出屏幕 */ publicvoidinitFlipperViews(TouchListenerlistener,ViewcurrentBottomView,ViewcurrentShowView,ViewcurrentTopView){ this.currentBottomView=currentBottomView; this.currentShowView=currentShowView; this.currentTopView=currentTopView; setTouchResultListener(listener); addView(currentBottomView); addView(currentShowView); addView(currentTopView); /**默认将最上层的view滑动的边缘(用于查看上一页)*/ currentTopView.scrollTo(-screenWidth,0); } @Override protectedvoidonLayout(booleanchanged,intl,intt,intr,intb){ for(inti=0;i<getChildCount();i++){ Viewchild=getChildAt(i); intheight=child.getMeasuredHeight(); intwidth=child.getMeasuredWidth(); child.layout(0,0,width,height); } } @Override protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){ super.onMeasure(widthMeasureSpec,heightMeasureSpec); intwidth=MeasureSpec.getSize(widthMeasureSpec); intheight=MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(width,height); for(inti=0;i<getChildCount();i++){ getChildAt(i).measure(widthMeasureSpec,heightMeasureSpec); } } privateintstartX=0; @Override publicbooleandispatchTouchEvent(MotionEventev){ switch(ev.getAction()){ caseMotionEvent.ACTION_DOWN: if(!mScroller.isFinished()){ break; } startX=(int)ev.getX(); break; } returnsuper.dispatchTouchEvent(ev); } @SuppressWarnings("deprecation") @Override publicbooleanonTouchEvent(MotionEventevent){ obtainVelocityTracker(event); switch(event.getAction()){ caseMotionEvent.ACTION_MOVE: if(!mScroller.isFinished()){ returnsuper.onTouchEvent(event); } if(startX==0){ startX=(int)event.getX(); } finalintdistance=startX-(int)event.getX(); if(mDirection==MOVE_NO_RESULT){ if(mListener.whetherHasNextPage()&&distance>0){ mDirection=MOVE_TO_LEFT; }elseif(mListener.whetherHasPreviousPage()&&distance<0){ mDirection=MOVE_TO_RIGHT; } } if(mMode==MODE_NONE &&((mDirection==MOVE_TO_LEFT&&mListener.whetherHasNextPage())||(mDirection==MOVE_TO_RIGHT&&mListener .whetherHasPreviousPage()))){ mMode=MODE_MOVE; } if(mMode==MODE_MOVE){ if((mDirection==MOVE_TO_LEFT&&distance<=0)||(mDirection==MOVE_TO_RIGHT&&distance>=0)){ mMode=MODE_NONE; } } if(mDirection!=MOVE_NO_RESULT){ if(mDirection==MOVE_TO_LEFT){ if(mScrollerView!=currentShowView){ mScrollerView=currentShowView; } }else{ if(mScrollerView!=currentTopView){ mScrollerView=currentTopView; } } if(mMode==MODE_MOVE){ mVelocityTracker.computeCurrentVelocity(1000,ViewConfiguration.getMaximumFlingVelocity()); if(mDirection==MOVE_TO_LEFT){ mScrollerView.scrollTo(distance,0); }else{ mScrollerView.scrollTo(screenWidth+distance,0); } }else{ finalintscrollX=mScrollerView.getScrollX(); if(mDirection==MOVE_TO_LEFT&&scrollX!=0&&mListener.whetherHasNextPage()){ mScrollerView.scrollTo(0,0); }elseif(mDirection==MOVE_TO_RIGHT&&mListener.whetherHasPreviousPage()&&screenWidth!=Math.abs(scrollX)){ mScrollerView.scrollTo(-screenWidth,0); } } } break; caseMotionEvent.ACTION_UP: if(mScrollerView==null){ returnsuper.onTouchEvent(event); } finalintscrollX=mScrollerView.getScrollX(); mVelocityValue=(int)mVelocityTracker.getXVelocity(); //scroll左正,右负(),(startX+dx)的值如果为0,即复位 /* *android.widget.Scroller.startScroll(intstartX,intstartY,int *dx,intdy,intduration) */ inttime=500; if(mMode==MODE_MOVE&&mDirection==MOVE_TO_LEFT){ if(scrollX>limitDistance||mVelocityValue<-time){ //手指向左移动,可以翻屏幕 mTouchResult=MOVE_TO_LEFT; if(mVelocityValue<-time){ time=200; } mScroller.startScroll(scrollX,0,screenWidth-scrollX,0,time); }else{ mTouchResult=MOVE_NO_RESULT; mScroller.startScroll(scrollX,0,-scrollX,0,time); } }elseif(mMode==MODE_MOVE&&mDirection==MOVE_TO_RIGHT){ if((screenWidth-scrollX)>limitDistance||mVelocityValue>time){ //手指向右移动,可以翻屏幕 mTouchResult=MOVE_TO_RIGHT; if(mVelocityValue>time){ time=250; } mScroller.startScroll(scrollX,0,-scrollX,0,time); }else{ mTouchResult=MOVE_NO_RESULT; mScroller.startScroll(scrollX,0,screenWidth-scrollX,0,time); } } resetVariables(); postInvalidate(); break; } returntrue; } privatevoidresetVariables(){ mDirection=MOVE_NO_RESULT; mMode=MODE_NONE; startX=0; releaseVelocityTracker(); } privateTouchListenermListener; privatevoidsetTouchResultListener(TouchListenerlistener){ this.mListener=listener; } @Override publicvoidcomputeScroll(){ super.computeScroll(); if(mScroller.computeScrollOffset()){ mScrollerView.scrollTo(mScroller.getCurrX(),mScroller.getCurrY()); postInvalidate(); }elseif(mScroller.isFinished()&&mListener!=null&&mTouchResult!=MOVE_NO_RESULT){ if(mTouchResult==MOVE_TO_LEFT){ if(currentTopView!=null){ removeView(currentTopView); } currentTopView=mScrollerView; currentShowView=currentBottomView; if(mListener.currentIsLastPage()){ finalViewnewView=mListener.createView(mTouchResult); currentBottomView=newView; addView(newView,0); }else{ currentBottomView=newView(getContext()); currentBottomView.setVisibility(View.GONE); addView(currentBottomView,0); } }else{ if(currentBottomView!=null){ removeView(currentBottomView); } currentBottomView=currentShowView; currentShowView=mScrollerView; if(mListener.currentIsFirstPage()){ finalViewnewView=mListener.createView(mTouchResult); currentTopView=newView; currentTopView.scrollTo(-screenWidth,0); addView(currentTopView); }else{ currentTopView=newView(getContext()); currentTopView.scrollTo(-screenWidth,0); currentTopView.setVisibility(View.GONE); addView(currentTopView); } } mTouchResult=MOVE_NO_RESULT; } } privatevoidobtainVelocityTracker(MotionEventevent){ if(mVelocityTracker==null){ mVelocityTracker=VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); } privatevoidreleaseVelocityTracker(){ if(mVelocityTracker!=null){ mVelocityTracker.recycle(); mVelocityTracker=null; } } /*** *用来实时回调触摸事件回调 * *@authorfreeson */ publicinterfaceTouchListener{ /**手指向左滑动,即查看下一章节*/ finalintMOVE_TO_LEFT=0; /**手指向右滑动,即查看上一章节*/ finalintMOVE_TO_RIGHT=1; /** *创建一个承载Text的View * *@paramdirection *{@linkMOVE_TO_LEFT,MOVE_TO_RIGHT} *@return */ publicViewcreateView(finalintdirection); /*** *当前页是否是第一页 * *@return */ publicbooleancurrentIsFirstPage(); /*** *当前页是否是最后一页 * *@return */ publicbooleancurrentIsLastPage(); /** *当前页是否有上一页(用来判断可滑动性) * *@return */ publicbooleanwhetherHasPreviousPage(); /*** *当前页是否有下一页(用来判断可滑动性) * *@return */ publicbooleanwhetherHasNextPage(); } }
Activity测试文件:
packagecom.example.testscroll; importjava.io.ByteArrayOutputStream; importjava.io.IOException; importjava.io.InputStream; importandroid.app.Activity; importandroid.content.res.AssetManager; importandroid.os.Bundle; importandroid.os.Handler; importandroid.view.LayoutInflater; importandroid.view.View; importandroid.view.View.OnClickListener; importandroid.widget.TextView; importcom.example.testscroll.view.FlipperLayout; importcom.example.testscroll.view.FlipperLayout.TouchListener; importcom.example.testscrollactivity.R; publicclassMainActivityextendsActivityimplementsOnClickListener,TouchListener{ privateStringtext=""; privateinttextLenght=0; privatestaticfinalintCOUNT=400; privateintcurrentTopEndIndex=0; privateintcurrentShowEndIndex=0; privateintcurrentBottomEndIndex=0; privateHandlerhandler=newHandler(){ publicvoidhandleMessage(android.os.Messagemsg){ FlipperLayoutrootLayout=(FlipperLayout)findViewById(R.id.container); ViewrecoverView=LayoutInflater.from(MainActivity.this).inflate(R.layout.view_new,null); Viewview1=LayoutInflater.from(MainActivity.this).inflate(R.layout.view_new,null); Viewview2=LayoutInflater.from(MainActivity.this).inflate(R.layout.view_new,null); rootLayout.initFlipperViews(MainActivity.this,view2,view1,recoverView); textLenght=text.length(); System.out.println("----textLenght----->"+textLenght); TextViewtextView=(TextView)view1.findViewById(R.id.textview); if(textLenght>COUNT){ textView.setText(text.subSequence(0,COUNT)); textView=(TextView)view2.findViewById(R.id.textview); if(textLenght>(COUNT<<1)){ textView.setText(text.subSequence(COUNT,COUNT*2)); currentShowEndIndex=COUNT; currentBottomEndIndex=COUNT<<1; }else{ textView.setText(text.subSequence(COUNT,textLenght)); currentShowEndIndex=textLenght; currentBottomEndIndex=textLenght; } }else{ textView.setText(text.subSequence(0,textLenght)); currentShowEndIndex=textLenght; currentBottomEndIndex=textLenght; } }; }; @Override protectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); newReadingThread().start(); } @Override publicvoidonClick(Viewv){ } @Override publicViewcreateView(finalintdirection){ Stringtxt=""; if(direction==TouchListener.MOVE_TO_LEFT){ currentTopEndIndex=currentShowEndIndex; finalintnextIndex=currentBottomEndIndex+COUNT; currentShowEndIndex=currentBottomEndIndex; if(textLenght>nextIndex){ txt=text.substring(currentBottomEndIndex,nextIndex); currentBottomEndIndex=nextIndex; }else{ txt=text.substring(currentBottomEndIndex,textLenght); currentBottomEndIndex=textLenght; } }else{ currentBottomEndIndex=currentShowEndIndex; currentShowEndIndex=currentTopEndIndex; currentTopEndIndex=currentTopEndIndex-COUNT; txt=text.substring(currentTopEndIndex-COUNT,currentTopEndIndex); } Viewview=LayoutInflater.from(this).inflate(R.layout.view_new,null); TextViewtextView=(TextView)view.findViewById(R.id.textview); textView.setText(txt); System.out.println("-top->"+currentTopEndIndex+"-show->"+currentShowEndIndex+"--bottom-->"+currentBottomEndIndex); returnview; } @Override publicbooleanwhetherHasPreviousPage(){ returncurrentShowEndIndex>COUNT; } @Override publicbooleanwhetherHasNextPage(){ returncurrentShowEndIndex<textLenght; } @Override publicbooleancurrentIsFirstPage(){ booleanshould=currentTopEndIndex>COUNT; if(!should){ currentBottomEndIndex=currentShowEndIndex; currentShowEndIndex=currentTopEndIndex; currentTopEndIndex=currentTopEndIndex-COUNT; } returnshould; } @Override publicbooleancurrentIsLastPage(){ booleanshould=currentBottomEndIndex<textLenght; if(!should){ currentTopEndIndex=currentShowEndIndex; finalintnextIndex=currentBottomEndIndex+COUNT; currentShowEndIndex=currentBottomEndIndex; if(textLenght>nextIndex){ currentBottomEndIndex=nextIndex; }else{ currentBottomEndIndex=textLenght; } } returnshould; } privateclassReadingThreadextendsThread{ publicvoidrun(){ AssetManageram=getAssets(); InputStreamresponse; try{ response=am.open("text.txt"); if(response!=null){ ByteArrayOutputStreambaos=newByteArrayOutputStream(); inti=-1; while((i=response.read())!=-1){ baos.write(i); } text=newString(baos.toByteArray(),"UTF-8"); baos.close(); response.close(); handler.sendEmptyMessage(0); } }catch(IOExceptione){ e.printStackTrace(); } } } }
xml布局文件:
<?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <TextView android:id="@+id/textview" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1.0" android:background="#666666" android:gravity="center" android:text="新建的View" android:textColor="@android:color/white" android:textSize="16sp" android:visibility="visible"/> <View android:layout_width="5dp" android:layout_height="match_parent" android:background="#FFFF00" android:gravity="center" android:textSize="25sp" android:visibility="visible"/> </LinearLayout>
activity布局文件:
<com.example.testscroll.view.FlipperLayoutxmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent"> </com.example.testscroll.view.FlipperLayout>
备注:上面为什么加一个速率计算器呢,其实只是为了识别这个动作是不是快速滑动的动作,就算滑动的距离不到屏幕的1/3,但是只要速率满足都可以判定改滑动是一个翻页的动作。
注意哦:这只是其中一个滑动的效果而已啊,不包括小说分章节的逻辑哦。虽然有些粗糙,但是还是有可以值得学习的地方,大家如果还有什么好的解决方案,可以一起讨论。
附上demo下载地址点击下载demo。
希望本文所述对大家Android程序设计有所帮助。