当前位置: 首页 > news >正文

网站怎样做301微官网下载

网站怎样做301,微官网下载,郑州网络推广哪个好,wordpress中标签作用FBReader是如何读取缓存文件内容#xff0c;并生成每一页Bitmap内容的呢#xff1f; 经过上一篇的分析#xff0c;我们已经知道#xff0c;FBRreader在绘制时是获取每一页对应的bitmap#xff0c;然后再进行绘制的。同时#xff0c;在绘制完当前页之后#xff0c;会通过… FBReader是如何读取缓存文件内容并生成每一页Bitmap内容的呢 经过上一篇的分析我们已经知道FBRreader在绘制时是获取每一页对应的bitmap然后再进行绘制的。同时在绘制完当前页之后会通过Executors.newSingleThreadExecutor来准备下一页的bitmap。 上一篇提到了一个重要的角色——ZLZLTextPlainModel。里面记录了native生成的缓存文件路径以及缓存文件个数。并且其实例是在native解析BookModel时通过调用java方法创建并且set到BookModel中的。 一、数据注入——“瀑布”倾泻的开始 再次回到FBReaderApp这个类的openBookInternal继续探索数据解析之后内容的“瀑布”是怎么被开启的 private synchronized void openBookInternal(final Book book, Bookmark bookmark, boolean force) {//忽略部分代码...try {//忽略部分代码...//native解析BookModelModel BookModel.createModel(book, plugin);//保存bookCollection.saveBook(book);ZLTextHyphenator.Instance().load(book.getLanguage());//数据注入BookTextView.setModel(Model.getTextModel());//忽略部分代码...} catch (BookReadingException e) {processException(e);}getViewWidget().reset();getViewWidget().repaint();//忽略部分代码... } 复制代码 这里有一个核心的方法会将数据注入到view中 BookTextView.setModel(Model.getTextModel()); 复制代码 这里的BookTextView为FBView的实例追溯其setModel方法最终在ZLTextView中 public synchronized void setModel(ZLTextModel model) {myCursorManager model ! null ? new CursorManager(model, getExtensionManager()) : null;//忽略部分代码...myModel model;myCurrentPage.reset();myPreviousPage.reset();myNextPage.reset();if (myModel ! null) {final int paragraphsNumber myModel.getParagraphsNumber();if (paragraphsNumber 0) {myCurrentPage.moveStartCursor(myCursorManager.get(0));}}Application.getViewWidget().reset(); } 复制代码 这里有这么几件事需要注意一下 判空model生成CursorManager重置上一页、当前页、下一页判空myModel通过CursorManager获取第一自然段的cursor将当前currentpage内容起始位置指向第一自然段的cursor重置Application.getViewWidget 分别看一下这几步都是做了些什么工作 在model不为空的情况下会创建CusorManger那么这个CusorManger是什么呢 final class CursorManager extends LruCacheInteger,ZLTextParagraphCursor {private final ZLTextModel myModel;final ExtensionElementManager ExtensionManager;CursorManager(ZLTextModel model, ExtensionElementManager extManager) {super(200); // max 200 cursors in the cachemyModel model;ExtensionManager extManager;}Overrideprotected ZLTextParagraphCursor create(Integer index) {return new ZLTextParagraphCursor(this, myModel, index);}} 复制代码 原来CusorManger是继承自LruCacheInteger,ZLTextParagraphCursor而且其最大缓存200个cursor并且重写create方法在调用get(integer)时如果获取不到则会通过create创建integer对应的ZLTextParagraphCurosr对象。 再来看一下ZLTextParagraphCurosr该类是第index自然段的cursor public final class ZLTextParagraphCursor {//忽略部分代码...ZLTextParagraphCursor(CursorManager cManager, ZLTextModel model, int index) {CursorManager cManager;Model model;//段落角标Index Math.min(index, model.getParagraphsNumber() - 1);fill();}//忽略部分代码...} 复制代码 2.重置上一页、当前页、下一页ZLTextPage final class ZLTextPage {final ZLTextWordCursor StartCursor new ZLTextWordCursor();final ZLTextWordCursor EndCursor new ZLTextWordCursor();final ArrayListZLTextLineInfo LineInfos new ArrayListZLTextLineInfo();int PaintState PaintStateEnum.NOTHING_TO_PAINT;void reset() {StartCursor.reset();EndCursor.reset();LineInfos.clear();PaintState PaintStateEnum.NOTHING_TO_PAINT;} } 复制代码 看起来好像每一页的内容范围是由起始的starCurosr和终止的endCursor定位的来看看ZLTextWordCursor public final class ZLTextWordCursor extends ZLTextPosition {private ZLTextParagraphCursor myParagraphCursor;private int myElementIndex;private int myCharIndex;public void reset() {myParagraphCursor null;myElementIndex 0;myCharIndex 0;}} 复制代码 3.判空model不为空时获取cursormanager.get(0)我们知道在初始创建cursormanager时内部是没有缓存的内容的这时会通过create创建ZLTextParagraphCursor对象。 4.将当前页的起始curosr移动至上一步获取的curosr处并将endcuror重置 ZLTextPage.class final ArrayListZLTextLineInfo LineInfos new ArrayListZLTextLineInfo(); void moveStartCursor(ZLTextParagraphCursor cursor) {StartCursor.setCursor(cursor);EndCursor.reset();LineInfos.clear();PaintState PaintStateEnum.START_IS_KNOWN; }ZLTextWordCursor.class public void setCursor(ZLTextParagraphCursor paragraphCursor) {myParagraphCursor paragraphCursor;myElementIndex 0;myCharIndex 0; } 复制代码 5.重置Application.getViewWidget的重置最终在bitmapmanager中 void reset() {for (int i 0; i SIZE; i) {myIndexes[i] null;//置空缓存的bitmap} } 复制代码 二、ZLTextParagraphCursor开启数据仓库的“大门” 在ZLTextParagraphCursor初始化时调用fill方法 ZLTextParagraphCursor(CursorManager cManager, ZLTextModel model, int index) {CursorManager cManager;Model model;Index Math.min(index, model.getParagraphsNumber() - 1);fill(); }void fill() {ZLTextParagraph paragraph Model.getParagraph(Index);switch (paragraph.getKind()) {case ZLTextParagraph.Kind.TEXT_PARAGRAPH:new Processor(paragraph, CursorManager.ExtensionManager, new LineBreaker(Model.getLanguage()), Model.getMarks(), Index, myElements).fill();break;//忽略部分代码...} } 复制代码 发现会通过model获取index自然段对应的paragraph我们知道model为ZLTextPlainModel的实例 public final ZLTextParagraph getParagraph(int index) {//获取index自然段的kind数组myParagraphKinds数据由native解析得到final byte kind myParagraphKinds[index];return (kind ZLTextParagraph.Kind.TEXT_PARAGRAPH) ?new ZLTextParagraphImpl(this, index) :new ZLTextSpecialParagraphImpl(kind, this, index); } 复制代码 一般的情况下自然段均为TEXT_PARAGRAPH相应的就会生成ZLTextParagraphImpl class ZLTextParagraphImpl implements ZLTextParagraph {private final ZLTextPlainModel myModel;private final int myIndex;ZLTextParagraphImpl(ZLTextPlainModel model, int index) {myModel model;myIndex index;}public EntryIterator iterator() {return myModel.new EntryIteratorImpl(myIndex);}public byte getKind() {return Kind.TEXT_PARAGRAPH;} } 复制代码 这里有一个地方需要注意那就是iterator()方法返回的迭代器对象EntryIteratorImpl tips: EntryIteratorImpl为ZLTextPlainModel的非静态内部类EntryIteratorImpl(int index) {reset(index); }void reset(int index) {//计数器清0myCounter 0;//获取native读取后,index段落内容长度myLength myParagraphLengths[index];//获取native读取后,index段落内容在哪个ncache文件中myDataIndex myStartEntryIndices[index];//获取native读取后,index段落内容起始位置在ncache内容中的偏移myDataOffset myStartEntryOffsets[index]; } 复制代码 接下来由于段落类型为TEXT_PARAGRAPH那么就会执行new Processor....fill() void fill() {//忽略部分代码...final ArrayListZLTextElement elements myElements;for (ZLTextParagraph.EntryIterator it myParagraph.iterator(); it.next(); ) {switch (it.getType()) {case ZLTextParagraph.Entry.TEXT:processTextEntry(it.getTextData(), it.getTextOffset(), it.getTextLength(), hyperlink);break;case ZLTextParagraph.Entry.CONTROL://忽略部分代码...break;case ZLTextParagraph.Entry.HYPERLINK_CONTROL://忽略部分代码...break;case ZLTextParagraph.Entry.IMAGE:final ZLImageEntry imageEntry it.getImageEntry();final ZLImage image imageEntry.getImage();if (image ! null) {ZLImageData data ZLImageManager.Instance().getImageData(image);if (data ! null) {if (hyperlink ! null) {hyperlink.addElementIndex(elements.size());}elements.add(new ZLTextImageElement(imageEntry.Id, data, image.getURI(), imageEntry.IsCover));}}break;case ZLTextParagraph.Entry.AUDIO:break;case ZLTextParagraph.Entry.VIDEO:break;case ZLTextParagraph.Entry.EXTENSION://忽略部分代码...break;case ZLTextParagraph.Entry.STYLE_CSS:case ZLTextParagraph.Entry.STYLE_OTHER:elements.add(new ZLTextStyleElement(it.getStyleEntry()));break;case ZLTextParagraph.Entry.STYLE_CLOSE:elements.add(ZLTextElement.StyleClose);break;case ZLTextParagraph.Entry.FIXED_HSPACE:elements.add(ZLTextFixedHSpaceElement.getElement(it.getFixedHSpaceLength()));break;}} } 复制代码 这里会进入一个for循环循环的条件是it.next()而it是myParagraph.iterator()这个上一步我们已经分析过针对kind为TEXT_PARAGRAPH的自然段iterator返回的对象为EntryIteratorImpl那么就看一下EntryIteratorImpl的next方法 public boolean next() {if (myCounter myLength) {return false;}int dataOffset myDataOffset;//该段落起始游标char[] data myStorage.block(myDataIndex);if (data null) {return false;}if (dataOffset data.length) {data myStorage.block(myDataIndex);if (data null) {return false;}dataOffset 0;}short first (short)data[dataOffset];byte type (byte)first;if (type 0) {data myStorage.block(myDataIndex);if (data null) {return false;}dataOffset 0;first (short)data[0];type (byte)first;}myType type;dataOffset;switch (type) {case ZLTextParagraph.Entry.TEXT:{int textLength (int)data[dataOffset];textLength (((int)data[dataOffset]) 16);textLength Math.min(textLength, data.length - dataOffset);myTextLength textLength;myTextData data;myTextOffset dataOffset;dataOffset textLength;break;}case ZLTextParagraph.Entry.CONTROL:{//忽略部分代码...break;}case ZLTextParagraph.Entry.HYPERLINK_CONTROL:{//忽略部分代码...break;}case ZLTextParagraph.Entry.IMAGE:{final short vOffset (short)data[dataOffset];final short len (short)data[dataOffset];final String id new String(data, dataOffset, len);dataOffset len;final boolean isCover data[dataOffset] ! 0;myImageEntry new ZLImageEntry(myImageMap, id, vOffset, isCover);break;}case ZLTextParagraph.Entry.FIXED_HSPACE://忽略部分代码...break;case ZLTextParagraph.Entry.STYLE_CSS:case ZLTextParagraph.Entry.STYLE_OTHER:{//忽略部分代码...}case ZLTextParagraph.Entry.STYLE_CLOSE:// No databreak;case ZLTextParagraph.Entry.RESET_BIDI:// No databreak;case ZLTextParagraph.Entry.AUDIO:// No databreak;case ZLTextParagraph.Entry.VIDEO:{//忽略部分代码...break;}case ZLTextParagraph.Entry.EXTENSION:{//忽略部分代码...break;}}myCounter;myDataOffset dataOffset;return true; } 复制代码 在next方法中出现了之前分析到的一个角色CachedCharStorage首先会调用其block方法 protected final ArrayListWeakReferencechar[] myArray new ArrayListWeakReferencechar[]();public char[] block(int index) {if (index 0 || index myArray.size()) {return null;}char[] block myArray.get(index).get();if (block null) {try {File file new File(fileName(index));int size (int)file.length();if (size 0) {throw new CachedCharStorageException(exceptionMessage(index, size size));}block new char[size / 2];InputStreamReader reader new InputStreamReader(new FileInputStream(file),UTF-16LE);final int rd reader.read(block);if (rd ! block.length) {throw new CachedCharStorageException(exceptionMessage(index, ; rd ! block.length));}reader.close();} catch (IOException e) {throw new CachedCharStorageException(exceptionMessage(index, null), e);}myArray.set(index, new WeakReferencechar[](block));}return block; } 复制代码 在调用block方法时传入的参数为myDataIndex该参数指明了当前自然段的内容在哪个ncahce文件中。不难分析出next方法主要的作用 读取要获取的自然段所在ncache如果CachedCharStorage中已缓存则取缓存否则直接读取对应的ncache文件必要时读取下一个ncache文件当前段落内容起始在x.ncache中但终止在x1.ncahce中根据native读取的段落内容长度每次调用next读取一个内容元素并将读取到的元素类型可能是TEXT、IMAGE等格式、数据内容、offset、长度等记录下来 这里我们再次回到for循环。通过next方法我们已经知道该方法会读取一个元素并将读取到的元素类型等信息保存下来查看for循环内部代码发现后续会根据读取到的元素类型进行数据的原始组装并最终保存到ZLTextParagraphCursor的ArrayList集合中。即通过此fill方法最终将index自然段的每一个元素读取出来并存入了集合中。 三、通过数据仓库“大门”拉取所需内容数据绘制页面对应bitmap 在初始化ZLTextParagraphCursor时我们已经知道其通过fill方法已经将内容解析出来。这时我们再回看一下setModel方法 public synchronized void setModel(ZLTextModel model) {//忽略部分代码...if (myModel ! null) {final int paragraphsNumber myModel.getParagraphsNumber();if (paragraphsNumber 0) {myCurrentPage.moveStartCursor(myCursorManager.get(0));}}//忽略部分代码... } 复制代码 会将当前页面的startCursor移动到第一自然段并将当前页面的PaintState设置为START_IS_KNOWN。这个时候页面已经准备就绪等待“发令枪”响了那么“发令枪”是在什么时候打响的呢这就又要回顾一下之前的一个老朋友FBReader界面唯一的控件——ZLAndroidWidget。它的onDraw方法我们已经分析过在静止状态时会调用onDrawStatic ZLAndroidWidget.class private void onDrawStatic(final Canvas canvas) {canvas.drawBitmap(myBitmapManager.getBitmap(ZLView.PageIndex.current), 0, 0, myPaint);//忽略部分代码... }BitmapManagerImpl.class public Bitmap getBitmap(ZLView.PageIndex index) {//忽略部分代码...myWidget.drawOnBitmap(myBitmaps[iIndex], index);return myBitmaps[iIndex]; }ZLAndroidWidget.class void drawOnBitmap(Bitmap bitmap, ZLView.PageIndex index) {final ZLView view ZLApplication.Instance().getCurrentView();if (view null) {return;}final ZLAndroidPaintContext context new ZLAndroidPaintContext(mySystemInfo,new Canvas(bitmap),new ZLAndroidPaintContext.Geometry(getWidth(),getHeight(),getWidth(),getMainAreaHeight(),0,0),view.isScrollbarShown() ? getVerticalScrollbarWidth() : 0);view.paint(context, index); } 复制代码 ZLApplication.Instance().getCurrentView()返回的对象即为setModel时的BookTextView那么就会调用其paint方法 public synchronized void paint(ZLPaintContext context, PageIndex pageIndex) {setContext(context);final ZLFile wallpaper getWallpaperFile();if (wallpaper ! null) {context.clear(wallpaper, getFillMode());} else {context.clear(getBackgroundColor());}if (myModel null || myModel.getParagraphsNumber() 0) {return;}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);if (page.StartCursor.isNull() || page.EndCursor.isNull()) {return;}final ArrayListZLTextLineInfo lineInfos page.LineInfos;final int[] labels new int[lineInfos.size() 1];int x getLeftMargin();int y getTopMargin();int index 0;int columnIndex 0;ZLTextLineInfo previousInfo null;for (ZLTextLineInfo info : lineInfos) {info.adjust(previousInfo);prepareTextLine(page, info, x, y, columnIndex);y info.Height info.Descent info.VSpaceAfter;labels[index] page.TextElementMap.size();if (index page.Column0Height) {y getTopMargin();x page.getTextWidth() getSpaceBetweenColumns();columnIndex 1;}previousInfo info;}final ListZLTextHighlighting hilites findHilites(page);x getLeftMargin();y getTopMargin();index 0;for (ZLTextLineInfo info : lineInfos) {drawTextLine(page, hilites, info, labels[index], labels[index 1]);y info.Height info.Descent info.VSpaceAfter;index;if (index page.Column0Height) {y getTopMargin();x page.getTextWidth() getSpaceBetweenColumns();}}//忽略部分代码... } 复制代码 1.会获取当前设置的墙纸如果能获取到墙纸那么会再去获取墙纸的绘制方式根据不同的方式最终将墙纸绘制到bitmap上。 2.根据页面Index获取对应的page对象。 3.获取到当前要绘制的page对象后通过preparePaintInfo方法根据当前page的PaintState构建页面基础元素信息这里会给page设置size可绘制区域宽高以及是否是双列绘制等 private synchronized void preparePaintInfo(ZLTextPage page) {page.setSize(getTextColumnWidth(), getTextAreaHeight(), twoColumnView(), page myPreviousPage);//忽略部分代码...final int oldState page.PaintState;final HashMapZLTextLineInfo,ZLTextLineInfo cache myLineInfoCache;for (ZLTextLineInfo info : page.LineInfos) {cache.put(info, info);}switch (page.PaintState) {default:break;case PaintStateEnum.TO_SCROLL_FORWARD://忽略部分代码...break;case PaintStateEnum.TO_SCROLL_BACKWARD://忽略部分代码...break;case PaintStateEnum.START_IS_KNOWN:if (!page.StartCursor.isNull()) {buildInfos(page, page.StartCursor, page.EndCursor);}break;case PaintStateEnum.END_IS_KNOWN://忽略部分代码...break;}page.PaintState PaintStateEnum.READY;// TODO: cache?myLineInfoCache.clear();if (page myCurrentPage) {if (oldState ! PaintStateEnum.START_IS_KNOWN) {myPreviousPage.reset();}if (oldState ! PaintStateEnum.END_IS_KNOWN) {myNextPage.reset();}} } 复制代码 4.通过之前的分析当前页面的PaintState在moveStartCursor时被设置为了START_IS_KNOWN那么就会调用buildInfos方法去构建页面原始数据信息 private void buildInfos(ZLTextPage page, ZLTextWordCursor start, ZLTextWordCursor result) {result.setCursor(start);//将endcursor归位于startcursorint textAreaHeight page.getTextHeight();//获取当前页面可绘制内容区域高度page.LineInfos.clear();//清空之前构建信息page.Column0Height 0;//记录第一列已构建高度boolean nextParagraph;//是否是下一自然段ZLTextLineInfo info null;//构建的行内容信息do {final ZLTextLineInfo previousInfo info;resetTextStyle();final ZLTextParagraphCursor paragraphCursor result.getParagraphCursor();//获取所构建的段落对应的cursorfinal int wordIndex result.getElementIndex();//开始的indexapplyStyleChanges(paragraphCursor, 0, wordIndex);info new ZLTextLineInfo(paragraphCursor, wordIndex, result.getCharIndex(), getTextStyle());//构建一个行信息final int endIndex info.ParagraphCursorLength;//结束index段落内容长度while (info.EndElementIndex ! endIndex) {info processTextLine(page, paragraphCursor, info.EndElementIndex, info.EndCharIndex, endIndex, previousInfo);textAreaHeight - info.Height info.Descent;if (textAreaHeight 0 page.LineInfos.size() page.Column0Height) {if (page.Column0Height 0 page.twoColumnView()) {textAreaHeight page.getTextHeight();textAreaHeight - info.Height info.Descent;page.Column0Height page.LineInfos.size();} else {break;}}textAreaHeight - info.VSpaceAfter;result.moveTo(info.EndElementIndex, info.EndCharIndex);page.LineInfos.add(info);if (textAreaHeight 0) {if (page.Column0Height 0 page.twoColumnView()) {textAreaHeight page.getTextHeight();page.Column0Height page.LineInfos.size();} else {break;}}}//如果当前已经读取到了该段落最后位置则获取下一段落nextParagraph result.isEndOfParagraph() result.nextParagraph();if (nextParagraph result.getParagraphCursor().isEndOfSection()) {if (page.Column0Height 0 page.twoColumnView() !page.LineInfos.isEmpty()) {textAreaHeight page.getTextHeight();page.Column0Height page.LineInfos.size();}}} while (nextParagraph textAreaHeight 0 (!result.getParagraphCursor().isEndOfSection() ||page.LineInfos.size() page.Column0Height));resetTextStyle(); }private ZLTextLineInfo processTextLine(ZLTextPage page,ZLTextParagraphCursor paragraphCursor,final int startIndex,final int startCharIndex,final int endIndex,ZLTextLineInfo previousInfo ) {final ZLTextLineInfo info processTextLineInternal(page, paragraphCursor, startIndex, startCharIndex, endIndex, previousInfo);if (info.EndElementIndex startIndex info.EndCharIndex startCharIndex) {info.EndElementIndex paragraphCursor.getParagraphLength();info.EndCharIndex 0;// TODO: add error element}return info; }private ZLTextLineInfo processTextLineInternal(ZLTextPage page,ZLTextParagraphCursor paragraphCursor,final int startIndex,final int startCharIndex,final int endIndex,ZLTextLineInfo previousInfo ){//忽略部分代码... } 复制代码 已第一次阅读时的构建场景为例通过buildInfos方法针对要构建内容的page会做如下几件事 page的startCusor在之前被移动到了第一自然段并且第一自然段在创建时已读取出来。在此方法中会遍历已读取出的自然段内容元素遍历元素过程中会根据可绘制区域宽度一行一行的构建出行元素信息且每一行的高度为行内元素中高度最高元素的高度生产出的每一行元素再根据可绘制区域高度判断该行是否能够添加到页面中。如果能则加入并继续构建下一行如果不能则退出构建当前页面元素构建完毕如果针对于第一自然段遍历完每一个元素切构建完每一行的行元素后当前仍有可用绘制高度则获取下一自然段继续重复上述步骤构建行信息直至构建结束 到此已经根据实际的可用空间构建出了当前page的内容数据并且是一行一行的内容数据。每一行中包含着之前读取出的数据元素。 5.包装元素将元素转变为可以被cavas绘制的元素“区域” 经过上面的页面数据构建已经将page当前情况下的数据内容一行行的构建出来了。但是目前构建出来的数据还是只是数据而我们最终的目的是生成page对应的bitmap。那么就需要对每一行的每一个元素进行位置描述转变为页面上一个一个的具有真实位置和数据信息的内容。而这一步的转变是通过for遍历每一行完成的 x、y为元素绘制坐标 for (ZLTextLineInfo info : lineInfos) {info.adjust(previousInfo);//将每一行中的每一个元素包装为元素“区域”带有元素数据和绘制坐标prepareTextLine(page, info, x, y, columnIndex);y info.Height info.Descent info.VSpaceAfter;labels[index] page.TextElementMap.size();if (index page.Column0Height) {y getTopMargin();x page.getTextWidth() getSpaceBetweenColumns();columnIndex 1;}previousInfo info; } 复制代码 6.绘制每一行的每一行元素“区域” 元素“区域”包装完成可以进行绘制了 for (ZLTextLineInfo info : lineInfos) {drawTextLine(page, hilites, info, labels[index], labels[index 1]);y info.Height info.Descent info.VSpaceAfter;index;if (index page.Column0Height) {y getTopMargin();x page.getTextWidth() getSpaceBetweenColumns();} }private void drawTextLine(ZLTextPage page, ListZLTextHighlighting hilites, ZLTextLineInfo info, int from, int to) {final ZLPaintContext context getContext();final ZLTextParagraphCursor paragraph info.ParagraphCursor;int index from;final int endElementIndex info.EndElementIndex;int charIndex info.RealStartCharIndex;final ListZLTextElementArea pageAreas page.TextElementMap.areas();if (to pageAreas.size()) {return;}for (int wordIndex info.RealStartElementIndex; wordIndex ! endElementIndex index to; wordIndex, charIndex 0) {final ZLTextElement element paragraph.getElement(wordIndex);final ZLTextElementArea area pageAreas.get(index);if (element area.Element) {index;if (area.ChangeStyle) {setTextStyle(area.Style);}final int areaX area.XStart;final int areaY area.YEnd - getElementDescent(element) - getTextStyle().getVerticalAlign(metrics());if (element instanceof ZLTextWord) {final ZLTextPosition pos new ZLTextFixedPosition(info.ParagraphCursor.Index, wordIndex, 0);final ZLTextHighlighting hl getWordHilite(pos, hilites);final ZLColor hlColor hl ! null ? hl.getForegroundColor() : null;drawWord(areaX, areaY, (ZLTextWord)element, charIndex, -1, false,hlColor ! null ? hlColor : getTextColor(getTextStyle().Hyperlink));} else if (element instanceof ZLTextImageElement) {final ZLTextImageElement imageElement (ZLTextImageElement)element;context.drawImage(areaX, areaY,imageElement.ImageData,getTextAreaSize(),getScalingType(imageElement),getAdjustingModeForImages());} else if (element instanceof ZLTextVideoElement) {//忽略部分代码...} else if (element instanceof ExtensionElement) {//忽略部分代码...} else if (element ZLTextElement.HSpace || element ZLTextElement.NBSpace) {//忽略部分代码...}}}//忽略部分代码... } 复制代码 7.绘制执行者——ZLAndroidPaintContext 最终的绘制是有此类对象来执行查看其主要的两个方法 public void drawString(int x, int y, char[] string, int offset, int length) {boolean containsSoftHyphen false;for (int i offset; i offset length; i) {if (string[i] (char)0xAD) {containsSoftHyphen true;break;}}if (!containsSoftHyphen) {myCanvas.drawText(string, offset, length, x, y, myTextPaint);} else {final char[] corrected new char[length];int len 0;for (int o offset; o offset length; o) {final char chr string[o];if (chr ! (char)0xAD) {corrected[len] chr;}}myCanvas.drawText(corrected, 0, len, x, y, myTextPaint);} }public void drawImage(int x, int y, ZLImageData imageData, Size maxSize, ScalingType scaling, ColorAdjustingMode adjustingMode) {final Bitmap bitmap ((ZLAndroidImageData)imageData).getBitmap(maxSize, scaling);if (bitmap ! null !bitmap.isRecycled()) {switch (adjustingMode) {case LIGHTEN_TO_BACKGROUND:myFillPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN));break;case DARKEN_TO_BACKGROUND:myFillPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DARKEN));break;case NONE:break;}myCanvas.drawBitmap(bitmap, x, y - bitmap.getHeight(), myFillPaint);myFillPaint.setXfermode(null);} } 复制代码 8.paint方法前后bitmap内容对比 起初bitmap paint方法执行结束后bitmap 至此当前page对应的bitmap就准备完成。通过bitmapmanager传递给ZLAndroidWidget最终绘制此bitmap到控件上。 当然由于本人接触此项目时间有限而且书写技术文章的经验实在欠缺过程中难免会有存在错误或描述不清或语言累赘等等一些问题还望大家能够谅解同时也希望大家继续给予指正。最后感谢大家对我的支持让我有了强大的动力坚持下去。 转载于:https://juejin.im/post/5c0f50dd518825170d1aed66
http://www.hkea.cn/news/14378815/

相关文章:

  • 东城网站设计织梦cms视频网站建设
  • wps免费模板网站公司logo需要注册商标吗
  • 仿模板电影网站嘉兴网站关键词
  • 多玩游戏网 wordpress东营市做网站优化
  • 衡阳市网站建设人才招聘网站开发 源代码
  • 擦边球网站怎么做招标网站建设方案
  • 山东建设信息网站网络营销培训课件
  • 物流公司网站建设有什么要点阿里云做电脑网站
  • 建设网站的网站免费下载ppt模板的网站有哪些
  • 常用的建站软件有哪些iis 网站权限
  • 专门做旅游的网站公司备案 网站主办者名称
  • 个人快速建站域名批量查询工具
  • 怎么修改网站后台权限百数低代码开发平台
  • 如何免费建网站网络营销推广的目标与策略
  • 购物网站可行性分析报告深圳建网站开发费用
  • 可以做mv的视频网站网站防止机器注册
  • 国内全屏网站欣赏学设计多少钱
  • 宿州做网站请简述网站建设流程图
  • 网站建设征求意见分析报告永康网站建设的公司
  • ui设计网站模板四川手机网站设计
  • 找个网站世界工厂采购网登录
  • 网站开发经理岗位职责软件制作小程序开发
  • 网站设计标注图怎么做做竞价的网站做优化有效果吗
  • 楚雄网站制作推广引流吸引人的文案
  • 石材公司网站高端大气网站源码
  • 大企业网站建设做什么网站赚钱最快
  • 网站开发工程师任职要求长春市人才网
  • 怎样看一个网站是不是织梦做的做it行业招标网站
  • 公司注销后 网站备案吗网站微信推广怎么做
  • 怎么用html建网站国内平面设计公司