从阅读器开始(FBReader类开启),到页面显示完成的过程
首先在FBReader中实现了fileFromIntent()方法,从Intent对象中读取要打开的文本路径,因为是直接打开所以此处获得的路径为null,用这个去实例化一个
@Override protected ZLFile fileFromIntent(Intent intent) { //如果是从文本打开的方式进入,则此处能够获取到文件路径 String filePath = intent.getStringExtra(BOOK_PATH_KEY); if (filePath == null) { final Uri data = intent.getData(); if (data != null) { filePath = data.getPath(); } } return filePath != null ? ZLFile.createFileByPath(filePath) : null; } |
@Override protected FBReaderApp createApplication(ZLFile file) { if (SQLiteBooksDatabase.Instance() == null) { new SQLiteBooksDatabase(this, "READER"); } return new FBReaderApp(file != null ? file.getPath() : null); }
|
ZLAndroidActivity的OnCreate中调用
final ZLFile fileToOpen = fileFromIntent(getIntent()); if (((ZLAndroidApplication) getApplication()).myMainWindow == null) { ZLApplication application = createApplication(fileToOpen); ((ZLAndroidApplication) getApplication()).myMainWindow = new ZLAndroidApplicationWindow( application); application.initWindow(); } else { ZLApplication.Instance().openFile(fileToOpen); } |
详解:
由于fileToOpen为null,且并未为myMainWindow初始化,则
首先创建FBReaderApp对象,之后再通过instance()方法就能够得到该对象,
由于ZLApplication和其子类FBReaderApp都实现了initWindow(),则会调用子类的(因为此处的application为FBReaderApp对象),如下:
/** * 该方法在ZLAndroidActivity的OnCreate中有调用 */ public void initWindow() { super.initWindow(); wait("loadingBook", new Runnable() { public void run() { Book book = createBookForFile(ZLFile.createFileByPath(myArg0)); if (book == null) { book = Library.getRecentBook(); } if ((book == null) || !book.File.exists()) { book = Book.getByFile(Library.getHelpFile()); //book = Book.getByFile(ZLResourceFile.createResourceFile("data/help/Noname1.txt")); } openBookInternal(book, null); } }); } |
其wait()方法即打开书籍并显示等待进度条,等待进度条的信息根据当前语言而有所不同。
运行到这里,preparePaintInfo执行前,会呈现图书加载中,请等待...的进度框。
在该构造方法中调用了setView方法,如下:
protected final void setView(ZLView view) { if (view != null) { myView = view; final ZLViewWidget widget = getViewWidget(); if (widget != null) { widget.reset(); widget.repaint(); } //隐藏活动popup onViewChanged(); } } |
此时getViewWidget()为null,则里面的if不执行。
接着,初始化myMainWindow,并初始化,初始化时在ZLAndroidApplicationWindow父类构造方法中,把该window加载到FBReaderApp上了
接着调用FBReaderApp.initWindow()方法,其内部同样调用了setView方法,不同之处在于此时getViewWidget()不为null。
public final ZLViewWidget getViewWidget() { return myWindow != null ? myWindow.getViewWidget() : null; } |
如上,因为此时myWindow不为null,则调用 myWindow.getViewWidget()方法
此方法最终执行如下:
public ZLAndroidWidget getWidget() { if (myWidget == null) { myWidget = (ZLAndroidWidget)myActivity.findViewById(R.id.main_view); } return myWidget; } |
即能够获得当前Activity中的id为main_view的Widget。
所以调用如下:
widget.reset();
widget.repaint();
上面的对BitmapManager进行初始化其页码。
下面的调用重画Widget。
该Widget就是小说画面的承载类,提供了如点击左右方翻页,点击下方出菜单等功能。其具有onDrawInScrolling(),onDrawStatic()方法,分别是翻页中画,和静止中画。
我们主要关注静止中(分页中主要就是一些计算和动画)。
/** * 静止中画 * * @param canvas */ private void onDrawStatic(Canvas canvas) { //System.out.println("ondrawstatic invoke"); // 重新设置小说部分的宽高(主题部分的高是要减去底部 myBitmapManager.setSize(getWidth(), getMainAreaHeight()); // 画出小说部分 canvas.drawBitmap(myBitmapManager.getBitmap(ZLView.PageIndex.current), 0, 0, myPaint); drawFooter(canvas); } |
我们看到其调用了:
myBitmapManager.getBitmap(ZLView.PageIndex.current);
该方法如下:
/** * 通过传入的页面参数决定返回的位图 * @param index * @return */ Bitmap getBitmap(ZLView.PageIndex index) { for (int i = 0; i < SIZE; ++i) { if (index == myIndexes[i]) { return myBitmaps[i]; } } final int iIndex = getInternalIndex(index); myIndexes[iIndex] = index; if (myBitmaps[iIndex] == null) { myBitmaps[iIndex] = Bitmap.createBitmap(myWidth, myHeight, Bitmap.Config.RGB_565); } //向myBitmaps[index]上画bitmap myWidget.drawOnBitmap(myBitmaps[iIndex], index); return myBitmaps[iIndex]; } |
最主要的就是: myWidget.drawOnBitmap(myBitmaps[iIndex], index);
如下:
/** * 在位图上画 * 该方法在BitmapManager中调用。 * * @param bitmap * @param index */ void drawOnBitmap(Bitmap bitmap, ZLView.PageIndex index) { final ZLView view = ZLApplication.Instance().getCurrentView(); if (view == null) { return; } //把ZLAndroidWigetPaintContext与Bitmap进行绑定 final ZLAndroidPaintContext context = new ZLAndroidPaintContext( new Canvas(bitmap), getWidth(), getMainAreaHeight(), view.isScrollbarShown() ? getVerticalScrollbarWidth() : 0); view.paint(context, index); } |
最主要的仍是最后一句: view.paint(context,index);
此方法代码较长,详情参见ZLTextView. paint(ZLPaintContext context, PageIndex pageIndex)
重要代码如下:
//以上为画背景色
ZLTextPage page; switch (pageIndex) { default: case current: page = myCurrentPage; break; case previous: page = myPreviousPage; if (myPreviousPage.PaintState == PaintStateEnum.NOTHING_TO_PAINT) { //如果下一页没东西画,则画当前页 preparePaintInfo(myCurrentPage); myPreviousPage.EndCursor.setCursor(myCurrentPage.StartCursor); myPreviousPage.PaintState = PaintStateEnum.END_IS_KNOWN; } break; case next: page = myNextPage; if (myNextPage.PaintState == PaintStateEnum.NOTHING_TO_PAINT) { //如果下一页没东西画,则画当前页 preparePaintInfo(myCurrentPage); myNextPage.StartCursor.setCursor(myCurrentPage.EndCursor); myNextPage.PaintState = PaintStateEnum.START_IS_KNOWN; } }
page.TextElementMap.clear(); //准备要画的页面 preparePaintInfo(page);
//以下为页面显示 |
请见preparePaintInfo(page);方法,该方法传入的是ZLTextPage对象,该对象具有开始和结束cursor,以及每一列文字的信息。即每页的记录类。即为该page绑定要显示的数据开头和结尾信息,以便显示。
以上就是图书显示的过程。
总结如下:
ZLAndroidActivity.onCreate() --> FBReaderApp生成,ZLAndroidWidget获取,ZLAndroidWindow创建,FBReaderAPP.initWindow()调用--->ZLAndroidWidget重画
所以如果我们想在一开始就加载自己的书籍,就可以在FBReaderApp的initWindow中下功夫。
wait("loadingBook", new Runnable() { public void run() { Book book = createBookForFile(ZLFile.createFileByPath(myArg0)); if (book == null) { book = Library.getRecentBook(); } if ((book == null) || !book.File.exists()) { book = Book.getByFile(Library.getHelpFile()); //book = Book.getByFile(ZLResourceFile.createResourceFile("data/help/Noname1.txt")); } openBookInternal(book, null); } }); |
只需要把,此处的myArg0更换成自己的书籍路径即可!!
从阅读器开始(FBReader类开启),到页面显示完成的过程
首先在FBReader中实现了fileFromIntent()方法,从Intent对象中读取要打开的文本路径,因为是直接打开所以此处获得的路径为null,用这个去实例化一个
@Override protected ZLFile fileFromIntent(Intent intent) { //如果是从文本打开的方式进入,则此处能够获取到文件路径 String filePath = intent.getStringExtra(BOOK_PATH_KEY); if (filePath == null) { final Uri data = intent.getData(); if (data != null) { filePath = data.getPath(); } } return filePath != null ? ZLFile.createFileByPath(filePath) : null; } |
@Override protected FBReaderApp createApplication(ZLFile file) { if (SQLiteBooksDatabase.Instance() == null) { new SQLiteBooksDatabase(this, "READER"); } return new FBReaderApp(file != null ? file.getPath() : null); }
|
ZLAndroidActivity的OnCreate中调用
final ZLFile fileToOpen = fileFromIntent(getIntent()); if (((ZLAndroidApplication) getApplication()).myMainWindow == null) { ZLApplication application = createApplication(fileToOpen); ((ZLAndroidApplication) getApplication()).myMainWindow = new ZLAndroidApplicationWindow( application); application.initWindow(); } else { ZLApplication.Instance().openFile(fileToOpen); } |
详解:
由于fileToOpen为null,且并未为myMainWindow初始化,则
首先创建FBReaderApp对象,之后再通过instance()方法就能够得到该对象,
由于ZLApplication和其子类FBReaderApp都实现了initWindow(),则会调用子类的(因为此处的application为FBReaderApp对象),如下:
/** * 该方法在ZLAndroidActivity的OnCreate中有调用 */ public void initWindow() { super.initWindow(); wait("loadingBook", new Runnable() { public void run() { Book book = createBookForFile(ZLFile.createFileByPath(myArg0)); if (book == null) { book = Library.getRecentBook(); } if ((book == null) || !book.File.exists()) { book = Book.getByFile(Library.getHelpFile()); //book = Book.getByFile(ZLResourceFile.createResourceFile("data/help/Noname1.txt")); } openBookInternal(book, null); } }); } |
其wait()方法即打开书籍并显示等待进度条,等待进度条的信息根据当前语言而有所不同。
运行到这里,preparePaintInfo执行前,会呈现图书加载中,请等待...的进度框。
在该构造方法中调用了setView方法,如下:
protected final void setView(ZLView view) { if (view != null) { myView = view; final ZLViewWidget widget = getViewWidget(); if (widget != null) { widget.reset(); widget.repaint(); } //隐藏活动popup onViewChanged(); } } |
此时getViewWidget()为null,则里面的if不执行。
接着,初始化myMainWindow,并初始化,初始化时在ZLAndroidApplicationWindow父类构造方法中,把该window加载到FBReaderApp上了
接着调用FBReaderApp.initWindow()方法,其内部同样调用了setView方法,不同之处在于此时getViewWidget()不为null。
public final ZLViewWidget getViewWidget() { return myWindow != null ? myWindow.getViewWidget() : null; } |
如上,因为此时myWindow不为null,则调用 myWindow.getViewWidget()方法
此方法最终执行如下:
public ZLAndroidWidget getWidget() { if (myWidget == null) { myWidget = (ZLAndroidWidget)myActivity.findViewById(R.id.main_view); } return myWidget; } |
即能够获得当前Activity中的id为main_view的Widget。
所以调用如下:
widget.reset();
widget.repaint();
上面的对BitmapManager进行初始化其页码。
下面的调用重画Widget。
该Widget就是小说画面的承载类,提供了如点击左右方翻页,点击下方出菜单等功能。其具有onDrawInScrolling(),onDrawStatic()方法,分别是翻页中画,和静止中画。
我们主要关注静止中(分页中主要就是一些计算和动画)。
/** * 静止中画 * * @param canvas */ private void onDrawStatic(Canvas canvas) { //System.out.println("ondrawstatic invoke"); // 重新设置小说部分的宽高(主题部分的高是要减去底部 myBitmapManager.setSize(getWidth(), getMainAreaHeight()); // 画出小说部分 canvas.drawBitmap(myBitmapManager.getBitmap(ZLView.PageIndex.current), 0, 0, myPaint); drawFooter(canvas); } |
我们看到其调用了:
myBitmapManager.getBitmap(ZLView.PageIndex.current);
该方法如下:
/** * 通过传入的页面参数决定返回的位图 * @param index * @return */ Bitmap getBitmap(ZLView.PageIndex index) { for (int i = 0; i < SIZE; ++i) { if (index == myIndexes[i]) { return myBitmaps[i]; } } final int iIndex = getInternalIndex(index); myIndexes[iIndex] = index; if (myBitmaps[iIndex] == null) { myBitmaps[iIndex] = Bitmap.createBitmap(myWidth, myHeight, Bitmap.Config.RGB_565); } //向myBitmaps[index]上画bitmap myWidget.drawOnBitmap(myBitmaps[iIndex], index); return myBitmaps[iIndex]; } |
最主要的就是: myWidget.drawOnBitmap(myBitmaps[iIndex], index);
如下:
/** * 在位图上画 * 该方法在BitmapManager中调用。 * * @param bitmap * @param index */ void drawOnBitmap(Bitmap bitmap, ZLView.PageIndex index) { final ZLView view = ZLApplication.Instance().getCurrentView(); if (view == null) { return; } //把ZLAndroidWigetPaintContext与Bitmap进行绑定 final ZLAndroidPaintContext context = new ZLAndroidPaintContext( new Canvas(bitmap), getWidth(), getMainAreaHeight(), view.isScrollbarShown() ? getVerticalScrollbarWidth() : 0); view.paint(context, index); } |
最主要的仍是最后一句: view.paint(context,index);
此方法代码较长,详情参见ZLTextView. paint(ZLPaintContext context, PageIndex pageIndex)
重要代码如下:
//以上为画背景色
ZLTextPage page; switch (pageIndex) { default: case current: page = myCurrentPage; break; case previous: page = myPreviousPage; if (myPreviousPage.PaintState == PaintStateEnum.NOTHING_TO_PAINT) { //如果下一页没东西画,则画当前页 preparePaintInfo(myCurrentPage); myPreviousPage.EndCursor.setCursor(myCurrentPage.StartCursor); myPreviousPage.PaintState = PaintStateEnum.END_IS_KNOWN; } break; case next: page = myNextPage; if (myNextPage.PaintState == PaintStateEnum.NOTHING_TO_PAINT) { //如果下一页没东西画,则画当前页 preparePaintInfo(myCurrentPage); myNextPage.StartCursor.setCursor(myCurrentPage.EndCursor); myNextPage.PaintState = PaintStateEnum.START_IS_KNOWN; } }
page.TextElementMap.clear(); //准备要画的页面 preparePaintInfo(page);
//以下为页面显示 |
请见preparePaintInfo(page);方法,该方法传入的是ZLTextPage对象,该对象具有开始和结束cursor,以及每一列文字的信息。即每页的记录类。即为该page绑定要显示的数据开头和结尾信息,以便显示。
以上就是图书显示的过程。
总结如下:
ZLAndroidActivity.onCreate() --> FBReaderApp生成,ZLAndroidWidget获取,ZLAndroidWindow创建,FBReaderAPP.initWindow()调用--->ZLAndroidWidget重画
所以如果我们想在一开始就加载自己的书籍,就可以在FBReaderApp的initWindow中下功夫。
wait("loadingBook", new Runnable() { public void run() { Book book = createBookForFile(ZLFile.createFileByPath(myArg0)); if (book == null) { book = Library.getRecentBook(); } if ((book == null) || !book.File.exists()) { book = Book.getByFile(Library.getHelpFile()); //book = Book.getByFile(ZLResourceFile.createResourceFile("data/help/Noname1.txt")); } openBookInternal(book, null); } }); |
只需要把,此处的myArg0更换成自己的书籍路径即可!!