├── .idea ├── .name ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── vcs.xml ├── libraries │ ├── jargs_1_0.xml │ ├── antlr_2_7_4.xml │ ├── chardet_1_0.xml │ ├── cpdetector_1_0_10.xml │ ├── litepal_1_3_0_src.xml │ ├── support_annotations_23_0_1.xml │ └── support_v4_23_0_1.xml ├── inspectionProfiles │ ├── profiles_settings.xml │ └── Project_Default.xml ├── modules.xml ├── runConfigurations.xml ├── gradle.xml ├── compiler.xml └── misc.xml ├── settings.gradle ├── README.md ├── app ├── lib │ ├── jargs-1.0.jar │ ├── antlr-2.7.4.jar │ ├── chardet-1.0.jar │ ├── cpdetector_1.0.10.jar │ └── litepal-1.3.0-src.jar ├── src │ └── main │ │ ├── res │ │ ├── drawable-hdpi │ │ │ └── ic_launcher.png │ │ ├── drawable-mdpi │ │ │ └── ic_launcher.png │ │ ├── drawable-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── home_drawer_shadow.9.png │ │ ├── drawable-xxhdpi │ │ │ └── ic_launcher.png │ │ ├── values-sw600dp │ │ │ └── dimens.xml │ │ ├── values │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ ├── menu │ │ │ └── main.xml │ │ ├── values-sw720dp-land │ │ │ └── dimens.xml │ │ ├── layout │ │ │ ├── activity_main.xml │ │ │ └── view_new.xml │ │ ├── values-v11 │ │ │ └── styles.xml │ │ └── values-v14 │ │ │ └── styles.xml │ │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── testscroll │ │ │ ├── app │ │ │ └── MyApplication.java │ │ │ ├── utils │ │ │ └── CharsetDetector.java │ │ │ ├── view │ │ │ ├── ReadView.java │ │ │ └── FlipperLayout.java │ │ │ ├── model │ │ │ └── MyPage.java │ │ │ └── MainActivity.java │ │ ├── assets │ │ └── litepal.xml │ │ └── AndroidManifest.xml ├── build.gradle └── app.iml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── Reader.iml ├── import-summary.txt ├── gradlew.bat ├── captures └── SystemInfo_2015.12.09_14.35.25.txt └── gradlew /.idea/.name: -------------------------------------------------------------------------------- 1 | TestScrollActivity -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reader 2 | Android小说阅读器实现,分页显示逻辑实现,滑动翻页 3 | -------------------------------------------------------------------------------- /app/lib/jargs-1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nspduanlei/Reader/HEAD/app/lib/jargs-1.0.jar -------------------------------------------------------------------------------- /app/lib/antlr-2.7.4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nspduanlei/Reader/HEAD/app/lib/antlr-2.7.4.jar -------------------------------------------------------------------------------- /app/lib/chardet-1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nspduanlei/Reader/HEAD/app/lib/chardet-1.0.jar -------------------------------------------------------------------------------- /app/lib/cpdetector_1.0.10.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nspduanlei/Reader/HEAD/app/lib/cpdetector_1.0.10.jar -------------------------------------------------------------------------------- /app/lib/litepal-1.3.0-src.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nspduanlei/Reader/HEAD/app/lib/litepal-1.3.0-src.jar -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nspduanlei/Reader/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nspduanlei/Reader/HEAD/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nspduanlei/Reader/HEAD/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nspduanlei/Reader/HEAD/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nspduanlei/Reader/HEAD/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/home_drawer_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nspduanlei/Reader/HEAD/app/src/main/res/drawable-xhdpi/home_drawer_shadow.9.png -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/libraries/jargs_1_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values-sw600dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16dp 5 | 16dp 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/libraries/antlr_2_7_4.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/chardet_1_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/cpdetector_1_0_10.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/testscroll/app/MyApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.testscroll.app; 2 | 3 | import org.litepal.LitePalApplication; 4 | 5 | /** 6 | * Author: duanlei 7 | * Date: 2015-12-11 8 | */ 9 | public class MyApplication extends LitePalApplication { 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TestScrollActivity 5 | Settings 6 | Hello world! 7 | 8 | 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Oct 21 11:34:03 PDT 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip 7 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/assets/litepal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/menu/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values-sw720dp-land/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 128dp 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/litepal_1_3_0_src.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values-v11/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | 15 | # Gradle files 16 | .gradle/ 17 | build/ 18 | /*/build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | -------------------------------------------------------------------------------- /app/src/main/res/values-v14/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/support_annotations_23_0_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/testscroll/utils/CharsetDetector.java: -------------------------------------------------------------------------------- 1 | package com.example.testscroll.utils; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.nio.charset.Charset; 6 | 7 | import info.monitorenter.cpdetector.io.JChardetFacade; 8 | 9 | public class CharsetDetector { 10 | 11 | /** 12 | * 检测当前文件的编码方式 13 | */ 14 | public static Charset detect(InputStream in) { 15 | JChardetFacade detector = JChardetFacade.getInstance(); 16 | Charset charset = null; 17 | try { 18 | in.mark(100); 19 | charset = detector.detectCodepage(in, 100); 20 | in.reset(); 21 | } catch (IllegalArgumentException | IOException e) { 22 | e.printStackTrace(); 23 | } 24 | return charset; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 14 | 15 | 16 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 6 | 7 | defaultConfig { 8 | applicationId "com.example.testscrollactivity" 9 | minSdkVersion 8 10 | targetSdkVersion 23 11 | } 12 | 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | compile 'com.android.support:support-v4:23.0.1' 23 | compile files('lib/cpdetector_1.0.10.jar') 24 | compile files('lib/antlr-2.7.4.jar') 25 | compile files('lib/chardet-1.0.jar') 26 | compile files('lib/jargs-1.0.jar') 27 | compile files('lib/litepal-1.3.0-src.jar') 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/res/layout/view_new.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 16 | 17 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /.idea/libraries/support_v4_23_0_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Reader.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/testscroll/view/ReadView.java: -------------------------------------------------------------------------------- 1 | package com.example.testscroll.view; 2 | 3 | import android.content.Context; 4 | import android.text.Layout; 5 | import android.util.AttributeSet; 6 | import android.widget.TextView; 7 | 8 | /** 9 | * Author: duanlei 10 | * Date: 2015-12-09 11 | */ 12 | public class ReadView extends TextView { 13 | 14 | public ReadView(Context context) { 15 | super(context); 16 | init(); 17 | } 18 | 19 | public ReadView(Context context, AttributeSet attrs) { 20 | super(context, attrs); 21 | init(); 22 | } 23 | 24 | public ReadView(Context context, AttributeSet attrs, int defStyle) { 25 | super(context, attrs, defStyle); 26 | init(); 27 | } 28 | 29 | private void init() { 30 | 31 | } 32 | 33 | @Override 34 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 35 | super.onLayout(changed, left, top, right, bottom); 36 | } 37 | 38 | 39 | /** 40 | * 获取当前页总字数 41 | */ 42 | public int getCharNum() { 43 | return getLayout().getLineEnd(getLineNum()); 44 | } 45 | 46 | /** 47 | * 获取当前页总行数 48 | */ 49 | public int getLineNum() { 50 | Layout layout = getLayout(); 51 | int topOfLastLine = getHeight() - getPaddingTop() - getPaddingBottom() - getLineHeight(); 52 | return layout.getLineForVertical(topOfLastLine); 53 | } 54 | } -------------------------------------------------------------------------------- /import-summary.txt: -------------------------------------------------------------------------------- 1 | ECLIPSE ANDROID PROJECT IMPORT SUMMARY 2 | ====================================== 3 | 4 | Ignored Files: 5 | -------------- 6 | The following files were *not* copied into the new Gradle project; you 7 | should evaluate whether these are still needed in your project and if 8 | so manually move them: 9 | 10 | * ic_launcher-web.png 11 | * proguard-project.txt 12 | 13 | Replaced Jars with Dependencies: 14 | -------------------------------- 15 | The importer recognized the following .jar files as third party 16 | libraries and replaced them with Gradle dependencies instead. This has 17 | the advantage that more explicit version information is known, and the 18 | libraries can be updated automatically. However, it is possible that 19 | the .jar file in your project was of an older version than the 20 | dependency we picked, which could render the project not compileable. 21 | You can disable the jar replacement in the import wizard and try again: 22 | 23 | android-support-v4.jar => com.android.support:support-v4:19.1.0 24 | 25 | Moved Files: 26 | ------------ 27 | Android Gradle projects use a different directory structure than ADT 28 | Eclipse projects. Here's how the projects were restructured: 29 | 30 | * AndroidManifest.xml => app\src\main\AndroidManifest.xml 31 | * assets\ => app\src\main\assets\ 32 | * res\ => app\src\main\res\ 33 | * src\ => app\src\main\java\ 34 | 35 | Next Steps: 36 | ----------- 37 | You can now build the project. The Gradle project needs network 38 | connectivity to download dependencies. 39 | 40 | Bugs: 41 | ----- 42 | If for some reason your project does not build, and you determine that 43 | it is due to a bug or limitation of the Eclipse to Gradle importer, 44 | please file a bug at http://b.android.com with category 45 | Component-Tools. 46 | 47 | (This import summary is for your information only, and can be deleted 48 | after import once you are satisfied with the results.) 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/testscroll/model/MyPage.java: -------------------------------------------------------------------------------- 1 | package com.example.testscroll.model; 2 | 3 | import org.litepal.crud.DataSupport; 4 | 5 | /** 6 | * Author: duanlei 7 | * Date: 2015-12-10 8 | */ 9 | public class MyPage extends DataSupport { 10 | 11 | 12 | private int id; 13 | private int bookId; 14 | private int chapter; 15 | private int startPosition; 16 | private int pageSize; 17 | 18 | private String FileName; 19 | 20 | public MyPage() { 21 | 22 | } 23 | 24 | public int getId() { 25 | return id; 26 | } 27 | 28 | public void setId(int id) { 29 | this.id = id; 30 | } 31 | 32 | public int getBookId() { 33 | return bookId; 34 | } 35 | 36 | public void setBookId(int bookId) { 37 | this.bookId = bookId; 38 | } 39 | 40 | public int getChapter() { 41 | return chapter; 42 | } 43 | 44 | public void setChapter(int chapter) { 45 | this.chapter = chapter; 46 | } 47 | 48 | public int getStartPosition() { 49 | return startPosition; 50 | } 51 | 52 | public void setStartPosition(int startPosition) { 53 | this.startPosition = startPosition; 54 | } 55 | 56 | public int getPageSize() { 57 | return pageSize; 58 | } 59 | 60 | public void setPageSize(int pageSize) { 61 | this.pageSize = pageSize; 62 | } 63 | 64 | public String getFileName() { 65 | return FileName; 66 | } 67 | 68 | public void setFileName(String fileName) { 69 | FileName = fileName; 70 | } 71 | 72 | @Override 73 | public String toString() { 74 | return "MyPage{" + 75 | "id=" + id + 76 | ", bookId=" + bookId + 77 | ", chapter=" + chapter + 78 | ", startPosition=" + startPosition + 79 | ", pageSize=" + pageSize + 80 | ", FileName='" + FileName + '\'' + 81 | '}'; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | 14 | 26 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 53 | 54 | 55 | 56 | 57 | 1.7 58 | 59 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /captures/SystemInfo_2015.12.09_14.35.25.txt: -------------------------------------------------------------------------------- 1 | TASK com.example.testscrollactivity id=2926 2 | ACTIVITY com.example.testscrollactivity/com.example.testscroll.MainActivity 290a55bc pid=19812 3 | Local Activity 1595b6a3 State: 4 | mResumed=true mStopped=false mFinished=false 5 | mLoadersStarted=true 6 | mChangingConfigurations=false 7 | mCurrentConfig={1.0 460mcc2mnc zh_CN ldltr sw360dp w360dp h567dp 320dpi nrml port finger -keyb/v/h -nav/h s.6?spn} 8 | FragmentManager misc state: 9 | mActivity=com.example.testscroll.MainActivity@1595b6a3 10 | mContainer=android.app.Activity$1@33769da0 11 | mCurState=5 mStateSaved=false mDestroyed=false 12 | ViewRoot: 13 | mAdded=true mRemoved=false 14 | mConsumeBatchedInputScheduled=false 15 | mConsumeBatchedInputImmediatelyScheduled=false 16 | mPendingInputEventCount=0 17 | mProcessInputEventsScheduled=false 18 | mTraversalScheduled=false 19 | android.view.ViewRootImpl$NativePreImeInputStage: mQueueLength=0 20 | android.view.ViewRootImpl$ImeInputStage: mQueueLength=0 21 | android.view.ViewRootImpl$NativePostImeInputStage: mQueueLength=0 22 | Choreographer: 23 | mFrameScheduled=false 24 | mLastFrameTime=166426425 (3187 ms ago) 25 | View Hierarchy: 26 | com.android.internal.policy.impl.PhoneWindow$DecorView{17480d59 V.E..... R.....I. 0,0-720,1184} 27 | com.android.internal.widget.ActionBarOverlayLayout{994fd1e V.ED.... ......I. 0,0-720,1184 #1020375 android:id/decor_content_parent} 28 | android.widget.FrameLayout{1cc319ff V.E..... ......I. 0,146-720,1184 #1020002 android:id/content} 29 | com.example.testscroll.view.FlipperLayout{b37f3cc V.E..... ......I. 0,0-720,1038 #7f080000 app:id/container} 30 | android.widget.LinearLayout{2ee32615 V.E..... ........ 0,0-720,1038} 31 | android.widget.TextView{358fcd2a V.ED.... ........ 0,0-710,1038 #7f080001 app:id/textview} 32 | android.view.View{3739571b V.ED.... ........ 710,0-720,1038} 33 | android.widget.LinearLayout{35d680b8 V.E..... ........ 0,0-720,1038} 34 | android.widget.TextView{ddd0e91 V.ED.... ........ 0,0-710,1038 #7f080001 app:id/textview} 35 | android.view.View{16b591f6 V.ED.... ........ 710,0-720,1038} 36 | android.view.View{180c49f7 G.ED.... ......I. 0,0-720,1038} 37 | com.android.internal.widget.ActionBarContainer{27cef064 V.ED.... ........ 0,50-720,146 #1020376 android:id/action_bar_container} 38 | com.android.internal.widget.ActionBarView{11b302cd V.E..... ........ 0,0-720,96 #1020377 android:id/action_bar} 39 | android.widget.LinearLayout{ba0d782 V.....C. ........ 17,0-391,96} 40 | com.android.internal.widget.ActionBarView$HomeView{25b18e93 V.E..... ........ 0,0-80,96} 41 | android.widget.ImageView{3ebcaed0 G.ED.... ......I. 0,0-0,0 #102003b android:id/up} 42 | android.widget.ImageView{3879fec9 V.ED.... ........ 8,16-72,80 #102002c android:id/home} 43 | android.widget.LinearLayout{1008e9ce V.E..... ........ 80,23-374,72} 44 | android.widget.TextView{2e5c80ef V.ED.... ........ 0,0-278,49 #10202b6 android:id/action_bar_title} 45 | android.widget.TextView{1485e7fc G.ED.... ......I. 0,0-0,0 #10202b7 android:id/action_bar_subtitle} 46 | android.widget.ActionMenuView{329abe85 V.ED.... ......ID 720,0-720,96} 47 | com.android.internal.widget.ActionBarContextView{11cdd4da G.E..... ......ID 0,0-0,0 #1020378 android:id/action_context_bar} 48 | com.android.internal.widget.ActionBarContainer{61a3d0b G.ED.... ......ID 0,0-0,0 #1020379 android:id/split_action_bar} 49 | Looper (main, tid 1) {194787e8} 50 | (Total messages: 0, idling=false, quitting=false) 51 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/testscroll/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.testscroll; 2 | 3 | import android.app.Activity; 4 | import android.content.res.AssetManager; 5 | import android.os.Bundle; 6 | import android.os.Handler; 7 | import android.os.Message; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.View.OnClickListener; 11 | import android.view.ViewTreeObserver; 12 | 13 | import com.example.testscroll.model.MyPage; 14 | import com.example.testscroll.utils.CharsetDetector; 15 | import com.example.testscroll.view.FlipperLayout; 16 | import com.example.testscroll.view.FlipperLayout.TouchListener; 17 | import com.example.testscroll.view.ReadView; 18 | 19 | import org.litepal.crud.DataSupport; 20 | 21 | import java.io.BufferedReader; 22 | import java.io.IOException; 23 | import java.io.InputStream; 24 | import java.io.InputStreamReader; 25 | import java.nio.CharBuffer; 26 | import java.nio.charset.Charset; 27 | 28 | public class MainActivity extends Activity implements OnClickListener, TouchListener { 29 | 30 | private static final int MSG_DRAW_TEXT = 1; 31 | CharBuffer buffer = CharBuffer.allocate(8000); 32 | boolean oneIsLayout; 33 | 34 | private Handler mHandler = new Handler() { 35 | @Override 36 | public void handleMessage(Message msg) { 37 | switch (msg.what) { 38 | case MSG_DRAW_TEXT: 39 | 40 | FlipperLayout rootLayout = (FlipperLayout) findViewById(R.id.container); 41 | 42 | View recoverView = LayoutInflater.from(MainActivity.this).inflate(R.layout.view_new, null); 43 | View view1 = LayoutInflater.from(MainActivity.this).inflate(R.layout.view_new, null); 44 | View view2 = LayoutInflater.from(MainActivity.this).inflate(R.layout.view_new, null); 45 | 46 | rootLayout.initFlipperViews(MainActivity.this, view2, view1, recoverView); 47 | 48 | final ReadView readView1 = (ReadView) view1.findViewById(R.id.textview); 49 | final ReadView readView2 = (ReadView) view2.findViewById(R.id.textview); 50 | 51 | buffer.position(0); 52 | 53 | //填充第一页的文本 54 | readView1.setText(buffer); 55 | 56 | //填充第二页的文本 57 | ViewTreeObserver vto1 = readView1.getViewTreeObserver(); 58 | vto1.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 59 | @Override 60 | public void onGlobalLayout() { 61 | if (oneIsLayout) 62 | return; 63 | 64 | int charNum = readView1.getCharNum(); 65 | 66 | 67 | MyPage page = new MyPage(); 68 | page.setPageSize(charNum); 69 | page.setStartPosition(charNum); 70 | page.setId(1); 71 | 72 | //将第一页的数据存储在数据库中,如果该数据不存在 73 | if (isSavePage(1)) { 74 | page.update(1); 75 | } else { 76 | page.save(); 77 | } 78 | 79 | buffer.position(charNum); 80 | readView2.setText(buffer); 81 | 82 | oneIsLayout = true; 83 | } 84 | }); 85 | 86 | 87 | ViewTreeObserver vto2 = readView2.getViewTreeObserver(); 88 | vto2.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 89 | @Override 90 | public void onGlobalLayout() { 91 | int charNum = readView2.getCharNum(); 92 | if (charNum == 0) 93 | return; 94 | 95 | //将第二页的数据存储在数据库中, 如果该数据不存在 96 | MyPage page = new MyPage(); 97 | page.setPageSize(charNum); 98 | page.setStartPosition(charNum + getStartPosition(1)); 99 | page.setId(2); 100 | 101 | if (isSavePage(2)) { 102 | page.update(2); 103 | } else { 104 | page.save(); 105 | } 106 | } 107 | }); 108 | break; 109 | } 110 | } 111 | }; 112 | 113 | 114 | //该页是否存储 115 | private boolean isSavePage(int pageNo) { 116 | return DataSupport.find(MyPage.class, pageNo) != null; 117 | } 118 | 119 | //获取该页的结束位置 120 | private int getStartPosition(int pageNo) { 121 | if (pageNo < 1) { 122 | return 0; 123 | } 124 | 125 | if (isSavePage(pageNo)) { 126 | return DataSupport.find(MyPage.class, pageNo).getStartPosition(); 127 | } 128 | return 0; 129 | } 130 | 131 | 132 | @Override 133 | protected void onCreate(Bundle savedInstanceState) { 134 | super.onCreate(savedInstanceState); 135 | setContentView(R.layout.activity_main); 136 | new ReadingThread().start(); 137 | } 138 | 139 | 140 | @Override 141 | public void onClick(View v) { 142 | 143 | } 144 | 145 | @Override 146 | public View createView(final int direction, final int index) { 147 | View newView; 148 | if (direction == TouchListener.MOVE_TO_LEFT) { //下一页 149 | buffer.position(getStartPosition(index)); 150 | 151 | newView = LayoutInflater.from(this).inflate(R.layout.view_new, null); 152 | final ReadView readView = (ReadView) newView.findViewById(R.id.textview); 153 | readView.setText(buffer); 154 | 155 | ViewTreeObserver vto2 = readView.getViewTreeObserver(); 156 | vto2.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 157 | @Override 158 | public void onGlobalLayout() { 159 | int charNm = readView.getCharNum(); 160 | 161 | MyPage page = new MyPage(); 162 | page.setPageSize(charNm); 163 | 164 | page.setStartPosition(getStartPosition(index) + charNm); 165 | page.setBookId(index + 1); 166 | page.setId(index + 1); 167 | 168 | if (isSavePage(index + 1)) { 169 | page.update(index + 1); 170 | } else { 171 | page.save(); 172 | } 173 | } 174 | }); 175 | } else { 176 | buffer.position(getStartPosition(index-2)); 177 | newView = LayoutInflater.from(this).inflate(R.layout.view_new, null); 178 | final ReadView readView = (ReadView) newView.findViewById(R.id.textview); 179 | readView.setText(buffer); 180 | } 181 | 182 | return newView; 183 | } 184 | 185 | @Override 186 | public boolean whetherHasNextPage() { 187 | return true; 188 | } 189 | 190 | @Override 191 | public boolean currentIsLastPage() { 192 | return true; 193 | } 194 | 195 | private class ReadingThread extends Thread { 196 | public void run() { 197 | BufferedReader reader = null; 198 | InputStream in = null; 199 | AssetManager assets = getAssets(); 200 | try { 201 | in = assets.open("text.txt"); 202 | 203 | Charset charset = CharsetDetector.detect(in); 204 | reader = new BufferedReader(new InputStreamReader(in, charset)); 205 | reader.read(buffer); 206 | 207 | mHandler.obtainMessage(MSG_DRAW_TEXT).sendToTarget(); 208 | } catch (IOException e) { 209 | e.printStackTrace(); 210 | } finally { 211 | if (reader != null) { 212 | try { 213 | reader.close(); 214 | } catch (IOException e) { 215 | e.printStackTrace(); 216 | } 217 | } 218 | if (in != null) { 219 | try { 220 | in.close(); 221 | } catch (IOException e) { 222 | e.printStackTrace(); 223 | } 224 | } 225 | } 226 | } 227 | } 228 | 229 | } 230 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/testscroll/view/FlipperLayout.java: -------------------------------------------------------------------------------- 1 | package com.example.testscroll.view; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.util.Log; 6 | import android.view.MotionEvent; 7 | import android.view.VelocityTracker; 8 | import android.view.View; 9 | import android.view.ViewConfiguration; 10 | import android.view.ViewGroup; 11 | import android.widget.Scroller; 12 | 13 | public class FlipperLayout extends ViewGroup { 14 | 15 | private int index = 1; 16 | private static final String TAG = "FlipperLayout"; 17 | 18 | /** 19 | * 弹性滑动对象,实现过渡效果的滑动 20 | */ 21 | private Scroller mScroller; 22 | 23 | private VelocityTracker mVelocityTracker; 24 | 25 | private int mVelocityValue = 0; 26 | 27 | /** 28 | * 商定这个滑动是否有效的距离 29 | */ 30 | private int limitDistance = 0; 31 | 32 | private int screenWidth = 0; 33 | 34 | /** 35 | * 手指移动的方向 36 | */ 37 | private static final int MOVE_TO_LEFT = 0; 38 | private static final int MOVE_TO_RIGHT = 1; 39 | private static final int MOVE_NO_RESULT = 2; 40 | 41 | /** 42 | * 最后触摸的结果方向 43 | */ 44 | private int mTouchResult = MOVE_NO_RESULT; 45 | /** 46 | * 一开始的方向 47 | */ 48 | private int mDirection = MOVE_NO_RESULT; 49 | 50 | /** 51 | * 触摸的模式 52 | */ 53 | private static final int MODE_NONE = 0; 54 | private static final int MODE_MOVE = 1; 55 | 56 | private int mMode = MODE_NONE; 57 | 58 | /** 59 | * 滑动的view 60 | */ 61 | private View mScrollerView = null; 62 | 63 | /** 64 | * 最上层的view(处于边缘的,看不到的) 65 | */ 66 | private View currentTopView = null; 67 | 68 | /** 69 | * 显示的view,显示在屏幕 70 | */ 71 | private View currentShowView = null; 72 | 73 | /** 74 | * 最底层的view(看不到的) 75 | */ 76 | private View currentBottomView = null; 77 | 78 | public FlipperLayout(Context context) { 79 | super(context); 80 | init(context); 81 | } 82 | 83 | public FlipperLayout(Context context, AttributeSet attrs, int defStyle) { 84 | super(context, attrs, defStyle); 85 | init(context); 86 | } 87 | 88 | public FlipperLayout(Context context, AttributeSet attrs) { 89 | super(context, attrs); 90 | init(context); 91 | } 92 | 93 | private void init(Context context) { 94 | mScroller = new Scroller(context); 95 | screenWidth = context.getResources().getDisplayMetrics().widthPixels; 96 | //limitDistance = screenWidth / 3; 97 | } 98 | 99 | /*** 100 | * @param listener 101 | * @param currentBottomView 最底层的view,初始状态看不到 102 | * @param currentShowView 正在显示的View 103 | * @param currentTopView 最上层的View,初始化时滑出屏幕 104 | */ 105 | public void initFlipperViews(TouchListener listener, View currentBottomView, View currentShowView, View currentTopView) { 106 | this.currentBottomView = currentBottomView; 107 | this.currentShowView = currentShowView; 108 | this.currentTopView = currentTopView; 109 | setTouchResultListener(listener); 110 | addView(currentBottomView); 111 | addView(currentShowView); 112 | addView(currentTopView); 113 | /** 默认将最上层的view滑动到边缘(用于查看上一页) */ 114 | currentTopView.scrollTo(-screenWidth, 0); 115 | } 116 | 117 | @Override 118 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 119 | for (int i = 0; i < getChildCount(); i++) { 120 | View child = getChildAt(i); 121 | int height = child.getMeasuredHeight(); 122 | int width = child.getMeasuredWidth(); 123 | child.layout(0, 0, width, height); 124 | } 125 | } 126 | 127 | @Override 128 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 129 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 130 | int width = MeasureSpec.getSize(widthMeasureSpec); 131 | int height = MeasureSpec.getSize(heightMeasureSpec); 132 | setMeasuredDimension(width, height); 133 | for (int i = 0; i < getChildCount(); i++) { 134 | getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec); 135 | } 136 | } 137 | 138 | private int startX = 0; 139 | 140 | @Override 141 | public boolean dispatchTouchEvent(MotionEvent ev) { 142 | switch (ev.getAction()) { 143 | case MotionEvent.ACTION_DOWN: 144 | if (!mScroller.isFinished()) { 145 | break; 146 | } 147 | startX = (int) ev.getX(); 148 | break; 149 | } 150 | return super.dispatchTouchEvent(ev); 151 | } 152 | 153 | @SuppressWarnings("deprecation") 154 | @Override 155 | public boolean onTouchEvent(MotionEvent event) { 156 | 157 | //在View的onTouchEvent方法中追踪当前单击事件的速度 158 | obtainVelocityTracker(event); 159 | 160 | switch (event.getAction()) { 161 | case MotionEvent.ACTION_MOVE: 162 | 163 | 164 | if (!mScroller.isFinished()) { 165 | return super.onTouchEvent(event); 166 | } 167 | 168 | if (startX == 0) { 169 | startX = (int) event.getX(); 170 | } 171 | 172 | //确定滑动方向 173 | final int distance = startX - (int) event.getX(); 174 | if (mDirection == MOVE_NO_RESULT) { 175 | if (mListener.whetherHasNextPage() && distance > 0) { //左滑 176 | mDirection = MOVE_TO_LEFT; 177 | // } else if (mListener.whetherHasPreviousPage() && distance < 0) { //右滑 178 | // mDirection = MOVE_TO_RIGHT; 179 | // } 180 | } else if (index > 1 && distance < 0) { //右滑 181 | mDirection = MOVE_TO_RIGHT; 182 | } 183 | 184 | } 185 | 186 | //是否停止滑动判断 187 | //当暂停时如果没有上一页或者下一页时停止 188 | // if (mMode == MODE_NONE 189 | // && ((mDirection == MOVE_TO_LEFT && mListener.whetherHasNextPage()) || (mDirection == MOVE_TO_RIGHT && mListener 190 | // .whetherHasPreviousPage()))) { 191 | // mMode = MODE_MOVE; 192 | // } 193 | 194 | if (mMode == MODE_NONE 195 | && ((mDirection == MOVE_TO_LEFT && mListener.whetherHasNextPage()) || (mDirection == MOVE_TO_RIGHT 196 | && index > 1))) { 197 | mMode = MODE_MOVE; 198 | } 199 | 200 | if (mMode == MODE_MOVE && 201 | (mDirection == MOVE_TO_LEFT && distance <= 0)) { 202 | mMode = MODE_NONE; 203 | } 204 | 205 | if (mDirection != MOVE_NO_RESULT) { 206 | //确定滑动的view 207 | if (mDirection == MOVE_TO_LEFT) { 208 | if (mScrollerView != currentShowView) { 209 | mScrollerView = currentShowView; 210 | } 211 | } else { 212 | if (mScrollerView != currentTopView) { 213 | mScrollerView = currentTopView; 214 | } 215 | } 216 | 217 | if (mMode == MODE_MOVE) { 218 | //计算当前的滑动速度 时间间隔1000ms 219 | mVelocityTracker.computeCurrentVelocity(1000, ViewConfiguration.getMaximumFlingVelocity()); 220 | 221 | if (mDirection == MOVE_TO_LEFT) { 222 | mScrollerView.scrollTo(distance, 0); 223 | } else { 224 | mScrollerView.scrollTo(screenWidth + distance - startX, 0); 225 | } 226 | } else { 227 | final int scrollX = mScrollerView.getScrollX(); 228 | 229 | if (mDirection == MOVE_TO_LEFT && scrollX != 0 && mListener.whetherHasNextPage()) { 230 | mScrollerView.scrollTo(0, 0); 231 | } else if (mDirection == MOVE_TO_RIGHT && index > 1 && screenWidth != Math.abs(scrollX)) { 232 | mScrollerView.scrollTo(-screenWidth, 0); 233 | } 234 | } 235 | } 236 | 237 | break; 238 | 239 | case MotionEvent.ACTION_UP: 240 | if (mScrollerView == null) { 241 | return super.onTouchEvent(event); 242 | } 243 | 244 | //左正 右负 245 | final int scrollX = mScrollerView.getScrollX(); 246 | 247 | //获取当前的速度 248 | mVelocityValue = (int) mVelocityTracker.getXVelocity(); 249 | 250 | int time = 500; 251 | 252 | if (mMode == MODE_MOVE && mDirection == MOVE_TO_LEFT) { 253 | if (scrollX > limitDistance || mVelocityValue < -time) { 254 | // 手指向左移动,可以翻屏幕 255 | mTouchResult = MOVE_TO_LEFT; 256 | if (mVelocityValue < -time) { 257 | time = 200; 258 | } 259 | mScroller.startScroll(scrollX, 0, screenWidth - scrollX, 0, time); 260 | } else { 261 | mTouchResult = MOVE_NO_RESULT; 262 | mScroller.startScroll(scrollX, 0, -scrollX, 0, time); 263 | } 264 | } else if (mMode == MODE_MOVE && mDirection == MOVE_TO_RIGHT) { 265 | if ((screenWidth - scrollX) > limitDistance || mVelocityValue > time) { 266 | // 手指向右移动,可以翻屏幕 267 | mTouchResult = MOVE_TO_RIGHT; 268 | if (mVelocityValue > time) { 269 | time = 250; 270 | } 271 | mScroller.startScroll(scrollX, 0, -scrollX, 0, time); 272 | } else { 273 | mTouchResult = MOVE_NO_RESULT; 274 | mScroller.startScroll(scrollX, 0, screenWidth - scrollX, 0, time); 275 | } 276 | } 277 | resetVariables(); 278 | postInvalidate(); 279 | break; 280 | } 281 | return true; 282 | } 283 | 284 | private void resetVariables() { 285 | mDirection = MOVE_NO_RESULT; 286 | mMode = MODE_NONE; 287 | startX = 0; 288 | releaseVelocityTracker(); 289 | } 290 | 291 | private TouchListener mListener; 292 | 293 | private void setTouchResultListener(TouchListener listener) { 294 | this.mListener = listener; 295 | } 296 | 297 | @Override 298 | public void computeScroll() { 299 | super.computeScroll(); 300 | if (mScroller.computeScrollOffset()) { 301 | mScrollerView.scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); 302 | postInvalidate(); 303 | } else if (mScroller.isFinished() && mListener != null && mTouchResult != MOVE_NO_RESULT) { 304 | 305 | if (mTouchResult == MOVE_TO_LEFT) { //下一页 306 | 307 | index ++; 308 | 309 | if (currentTopView != null) { 310 | removeView(currentTopView); 311 | } 312 | currentTopView = mScrollerView; 313 | currentShowView = currentBottomView; 314 | 315 | if (mListener.currentIsLastPage()) { //当前页不是最后一页 316 | final View newView = mListener.createView(mTouchResult, index); 317 | currentBottomView = newView; 318 | addView(newView, 0); 319 | } else { //当前页是最后一页 320 | currentBottomView = new View(getContext()); 321 | currentBottomView.setVisibility(View.GONE); 322 | addView(currentBottomView, 0); 323 | } 324 | } else { //上一页 325 | if(index > 1) 326 | index --; 327 | 328 | if (currentBottomView != null) { 329 | removeView(currentBottomView); 330 | } 331 | currentBottomView = currentShowView; 332 | currentShowView = mScrollerView; 333 | 334 | //if (mListener.currentIsFirstPage()) { 335 | if (index == 1) { 336 | currentTopView = new View(getContext()); 337 | currentTopView.scrollTo(-screenWidth, 0); 338 | currentTopView.setVisibility(View.GONE); 339 | addView(currentTopView); 340 | } else { 341 | currentTopView = mListener.createView(mTouchResult, index); 342 | currentTopView.scrollTo(-screenWidth, 0); 343 | addView(currentTopView); 344 | } 345 | } 346 | 347 | Log.d(TAG, "index:" + index); 348 | mTouchResult = MOVE_NO_RESULT; 349 | } 350 | } 351 | 352 | 353 | private void obtainVelocityTracker(MotionEvent event) { 354 | if (mVelocityTracker == null) { 355 | mVelocityTracker = VelocityTracker.obtain(); 356 | } 357 | mVelocityTracker.addMovement(event); 358 | } 359 | 360 | private void releaseVelocityTracker() { 361 | if (mVelocityTracker != null) { 362 | mVelocityTracker.recycle(); 363 | mVelocityTracker = null; 364 | } 365 | } 366 | 367 | /*** 368 | * 用来实时回调触摸事件回调 369 | * 370 | * @author freeson 371 | */ 372 | public interface TouchListener { 373 | 374 | /** 375 | * 手指向左滑动,即查看下一章节 376 | */ 377 | int MOVE_TO_LEFT = 0; 378 | 379 | 380 | /** 381 | * 创建一个承载Text的View 382 | * 383 | * @param direction 滑动方向 384 | * @param index 新建页的索引 385 | * @return 386 | */ 387 | View createView(final int direction, int index); 388 | 389 | 390 | /*** 391 | * 当前页是否是最后一页 392 | * 393 | * @return 394 | */ 395 | boolean currentIsLastPage(); 396 | 397 | /*** 398 | * 当前页是否有下一页(用来判断可滑动性) 399 | * 400 | * @return 401 | */ 402 | boolean whetherHasNextPage(); 403 | } 404 | 405 | } 406 | --------------------------------------------------------------------------------