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程序设计有所帮助。