pbootcms网站模板|日韩1区2区|织梦模板||网站源码|日韩1区2区|jquery建站特效-html5模板网

Android中的分頁文本

Paginating text in Android(Android中的分頁文本)
本文介紹了Android中的分頁文本的處理方法,對大家解決問題具有一定的參考價值,需要的朋友們下面隨著小編來一起學習吧!

問題描述

我正在為 Android 編寫一個簡單的文本/電子書查看器,所以我使用 TextView 向用戶顯示 HTML 格式的文本,以便他們可以通過返回和瀏覽頁面中的文本向前.但我的問題是我無法在 Android 中對文本進行分頁.

我不能(或者我不知道如何)從 TextView 用來將文本分成行和頁的換行和分頁算法中獲得適當的反饋.因此,我無法理解內容在實際顯示中的結束位置,因此我從下一頁的剩余部分繼續.我想找到解決這個問題的方法.

如果我知道屏幕上最后繪制的字符是什么,我可以輕松地放置足夠的字符來填滿屏幕,并且知道實際繪制完成的位置,我可以繼續下一頁.這可能嗎?怎么樣?

<小時>

在 StackOverflow 上已多次提出類似問題,但未提供令人滿意的答案.這些只是其中的一部分:

  • <小時>

    PS:我不僅限于 TextView,而且我知道換行和分頁算法可能非常復雜(

    實施

    公共類分頁{私有最終布爾值 mIncludePad;私人最終 int mWidth;私人最終 int mHeight;私人最終浮動 mSpacingMult;私人最終浮動 mSpacingAdd;私有最終 CharSequence mText;私有最終 TextPaint mPaint;私有最終列表<CharSequence>mPages;公共分頁(CharSequence 文本,int pageW,int pageH,TextPaint 油漆,浮動間距Mult,浮動間距添加,布爾 inclidePad){this.mText = 文本;this.mWidth = pageW;this.mHeight = pageH;this.mPaint = 油漆;this.mSpacingMult = spacingMult;this.mSpacingAdd = 間距添加;this.mIncludePad = inclidePad;this.mPages = new ArrayList<>();布局();}私人無效布局(){final StaticLayout layout = new StaticLayout(mText, mPaint, mWidth, Layout.Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, mIncludePad);最終 int 行 = layout.getLineCount();final CharSequence text = layout.getText();int startOffset = 0;int 高度 = mHeight;for (int i = 0; i < 行; i++) {如果(高度 < layout.getLineBottom(i)) {//當超出布局高度時addPage(text.subSequence(startOffset, layout.getLineStart(i)));startOffset = layout.getLineStart(i);高度 = layout.getLineTop(i) + mHeight;}如果(我 == 行 - 1){//把剩下的文本放到最后一頁addPage(text.subSequence(startOffset, layout.getLineEnd(i)));返回;}}}私人無效添加頁(字符序列文本){mPages.add(文本);}公共整數大小(){返回 mPages.size();}公共 CharSequence get(int index) {return (index >= 0 && index < mPages.size()) ?mPages.get(index) : null;}}

    注 1

    該算法不僅適用于 TextView(Pagination 類在上面的實現中使用 TextView 的 參數).您可以傳遞 StaticLayout 接受的任何參數集,然后使用分頁布局在

    I am writing a simple text/eBook viewer for Android, so I have used a TextView to show the HTML formatted text to the users, so they can browse the text in pages by going back and forth. But my problem is that I can not paginate the text in Android.

    I can not (or I don't know how to) get appropriate feedback from the line-breaking and page-breaking algorithms in which TextView uses to break text into lines and pages. Thus, I can not understand where the content ends in the actual display, so that I continue from the remaining in the next page. I want to find way to overcome this problem.

    If I know what is the last character painted on the screen, I can easily put enough characters to fill a screen, and knowing where tha actual painting was finished, I can continue at the next page. Is this possible? How?


    Similar questions have been asked several times on StackOverflow, but no satisfactory answer was provided. These are just a few of them:

    • How to paginate long text into pages in Android?
    • Ebook reader pagination issue in android
    • Paginate text based on rendered text size

    There was a single answer, which seems to work, but it is slow. It adds characters and lines until the page is filled. I don't think this is a good way to do page breaking:

    • How to break styled text into pages in Android?

    Rather than this question, it happens that PageTurner eBook reader does it mostly right, although it is somehow slow.

    • https://github.com/nightwhistler/pageturner


    PS: I am not confined to TextView, and I know line breaking and page breaking algorithms can be quite complex (as in TeX), so I am not looking for an optimal answer, but rather a reasonably fast solution that can be usable by the users.


    Update: This seems to be a good start for getting the right answer:

    Is there a way of retrieving a TextView's visible line count or range?

    Answer: After completing text layout, it is possible to find out the visible text:

    ViewTreeObserver vto = txtViewEx.getViewTreeObserver();
            vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    ViewTreeObserver obs = txtViewEx.getViewTreeObserver();
                    obs.removeOnGlobalLayoutListener(this);
                    height = txtViewEx.getHeight();
                    scrollY = txtViewEx.getScrollY();
                    Layout layout = txtViewEx.getLayout();
    
                    firstVisibleLineNumber = layout.getLineForVertical(scrollY);
                    lastVisibleLineNumber = layout.getLineForVertical(height+scrollY);
    
                }
            });
    

    解決方案

    NEW ANSWER

    PagedTextView library (in Kotlin) summarises the below lying algorithm by extending Android TextView. The sample app demonstrates the usage of the library.

    Setup

    dependencies {
        implementation 'com.github.onikx:pagedtextview:0.1.3'
    }
    

    Usage

    <com.onik.pagedtextview.PagedTextView
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    


    OLD ANSWER

    The algorithm below implements text pagination in separation of TextView itself lacking simultaneous dynamic change of both the TextView attributes and algorithm configuration parameters.

    Background

    What we know about text processing within TextView is that it properly breaks a text by lines according to the width of a view. Looking at the TextView's sources we can see that the text processing is done by the Layout class. So we can make use of the work the Layout class does for us and utilizing its methods do pagination.

    Problem

    The problem with TextView is that the visible part of text might be cut vertically somewhere at the middle of the last visible line. Regarding said, we should break a new page when the last line that fully fits into a view's height is met.

    Algorithm

    • We iterate through the lines of text and check if the line's bottom exceeds the view's height;
    • If so, we break a new page and calculate a new value for the cumulative height to compare the following lines' bottom with (see the implementation). The new value is defined as top value (red line in the picture below) of the line that hasn't fit into the previous page + TextView's height.

    Implementation

    public class Pagination {
        private final boolean mIncludePad;
        private final int mWidth;
        private final int mHeight;
        private final float mSpacingMult;
        private final float mSpacingAdd;
        private final CharSequence mText;
        private final TextPaint mPaint;
        private final List<CharSequence> mPages;
    
        public Pagination(CharSequence text, int pageW, int pageH, TextPaint paint, float spacingMult, float spacingAdd, boolean inclidePad) {
            this.mText = text;
            this.mWidth = pageW;
            this.mHeight = pageH;
            this.mPaint = paint;
            this.mSpacingMult = spacingMult;
            this.mSpacingAdd = spacingAdd;
            this.mIncludePad = inclidePad;
            this.mPages = new ArrayList<>();
    
            layout();
        }
    
        private void layout() {
            final StaticLayout layout = new StaticLayout(mText, mPaint, mWidth, Layout.Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, mIncludePad);
    
            final int lines = layout.getLineCount();
            final CharSequence text = layout.getText();
            int startOffset = 0;
            int height = mHeight;
    
            for (int i = 0; i < lines; i++) {
                if (height < layout.getLineBottom(i)) {
                    // When the layout height has been exceeded
                    addPage(text.subSequence(startOffset, layout.getLineStart(i)));
                    startOffset = layout.getLineStart(i);
                    height = layout.getLineTop(i) + mHeight;
                }
    
                if (i == lines - 1) {
                    // Put the rest of the text into the last page
                    addPage(text.subSequence(startOffset, layout.getLineEnd(i)));
                    return;
                }
            }
        }
    
        private void addPage(CharSequence text) {
            mPages.add(text);
        }
    
        public int size() {
            return mPages.size();
        }
    
        public CharSequence get(int index) {
            return (index >= 0 && index < mPages.size()) ? mPages.get(index) : null;
        }
    }
    

    Note 1

    The algorithm works not just for TextView (Pagination class uses TextView's parameters in the implementation above). You may pass any set of parameters StaticLayout accepts and later use the paginated layouts to draw text on Canvas/Bitmap/PdfDocument.

    You can also use Spannable as yourText parameter for different fonts as well as Html-formatted strings (like in the sample below).

    Note 2

    When all text has the same font size, all lines have equal height. In that case you might want to consider further optimization of the algorithm by calculating an amount of lines that fits into a single page and jumping to the proper line at each loop iteration.


    Sample

    The sample below paginates a string containing both html and Spanned text.

    public class PaginationActivity extends Activity {
        private TextView mTextView;
        private Pagination mPagination;
        private CharSequence mText;
        private int mCurrentIndex = 0;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_pagination);
    
            mTextView = (TextView) findViewById(R.id.tv);
    
            Spanned htmlString = Html.fromHtml(getString(R.string.html_string));
    
            Spannable spanString = new SpannableString(getString(R.string.long_string));
            spanString.setSpan(new ForegroundColorSpan(Color.BLUE), 0, 24, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            spanString.setSpan(new RelativeSizeSpan(2f), 0, 24, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            spanString.setSpan(new StyleSpan(Typeface.MONOSPACE.getStyle()), 0, 24, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            spanString.setSpan(new ForegroundColorSpan(Color.BLUE), 700, spanString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            spanString.setSpan(new RelativeSizeSpan(2f), 700, spanString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            spanString.setSpan(new StyleSpan(Typeface.MONOSPACE.getStyle()), 700, spanString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    
            mText = TextUtils.concat(htmlString, spanString);
    
            mTextView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    // Removing layout listener to avoid multiple calls
                    mTextView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                    mPagination = new Pagination(mText,
                            mTextView.getWidth(),
                            mTextView.getHeight(),
                            mTextView.getPaint(),
                            mTextView.getLineSpacingMultiplier(),
                            mTextView.getLineSpacingExtra(),
                            mTextView.getIncludeFontPadding());
                    update();
                }
            });
    
            findViewById(R.id.btn_back).setOnClickListener(v -> {
                mCurrentIndex = (mCurrentIndex > 0) ? mCurrentIndex - 1 : 0;
                update();
            });
            findViewById(R.id.btn_forward).setOnClickListener(v -> {
                mCurrentIndex = (mCurrentIndex < mPagination.size() - 1) ? mCurrentIndex + 1 : mPagination.size() - 1;
                update();
            });
        }
    
        private void update() {
            final CharSequence text = mPagination.get(mCurrentIndex);
            if(text != null) mTextView.setText(text);
        }
    }
    

    Activity's layout:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:paddingBottom="@dimen/activity_vertical_margin" >
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <Button
                android:id="@+id/btn_back"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:background="@android:color/transparent"/>
    
            <Button
                android:id="@+id/btn_forward"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:background="@android:color/transparent"/>
    
        </LinearLayout>
    
        <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    
    </RelativeLayout>
    

    Screenshot:

    這篇關于Android中的分頁文本的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!

    【網站聲明】本站部分內容來源于互聯網,旨在幫助大家更快的解決問題,如果有圖片或者內容侵犯了您的權益,請聯系我們刪除處理,感謝您的支持!

相關文檔推薦

How to wrap text around components in a JTextPane?(如何在 JTextPane 中的組件周圍環繞文本?)
MyBatis, how to get the auto generated key of an insert? [MySql](MyBatis,如何獲取插入的自動生成密鑰?[MySql])
Inserting to Oracle Nested Table in Java(在 Java 中插入 Oracle 嵌套表)
Java: How to insert CLOB into oracle database(Java:如何將 CLOB 插入 oracle 數據庫)
Why does Spring-data-jdbc not save my Car object?(為什么 Spring-data-jdbc 不保存我的 Car 對象?)
Use threading to process file chunk by chunk(使用線程逐塊處理文件)
主站蜘蛛池模板: 石英砂矿石色选机_履带辣椒色选机_X光异物检测机-合肥幼狮光电科技 | 清洁设备_洗地机/扫地机厂家_全自动洗地机_橙犀清洁设备官网 | 正压密封性测试仪-静态发色仪-导丝头柔软性测试仪-济南恒品机电技术有限公司 | 中细软知识产权_专业知识产权解决方案提供商 | 无硅导热垫片-碳纤维导热垫片-导热相变材料厂家-东莞市盛元新材料科技有限公司 | 空气能采暖,热泵烘干机,空气源热水机组|设备|厂家,东莞高温热泵_正旭新能源 | YJLV22铝芯铠装电缆-MYPTJ矿用高压橡套电缆-天津市电缆总厂 | 培训中心-翰香原香酥板栗饼加盟店总部-正宗板栗酥饼技术 | 污水处理设备维修_污水处理工程改造_机械格栅_过滤设备_气浮设备_刮吸泥机_污泥浓缩罐_污水处理设备_污水处理工程-北京龙泉新禹科技有限公司 | 挤塑板-XPS挤塑板-挤塑板设备厂家[襄阳欧格]| 工程管道/塑料管材/pvc排水管/ppr给水管/pe双壁波纹管等品牌管材批发厂家-河南洁尔康建材 | 石膏基自流平砂浆厂家-高强石膏基保温隔声自流平-轻质抹灰石膏粉砂浆批发-永康市汇利建设有限公司 | 铝镁锰板_铝镁锰合金板_铝镁锰板厂家_铝镁锰金属屋面板_安徽建科 | 耐酸泵,耐酸泵厂家-淄博华舜耐腐蚀真空泵 | 广州番禺搬家公司_天河黄埔搬家公司_企业工厂搬迁_日式搬家_广州搬家公司_厚道搬迁搬家公司 | 济南品牌设计-济南品牌策划-即合品牌策划设计-山东即合官网 | 德国GMN轴承,GMN角接触球轴承,GMN单向轴承,GMN油封,GMN非接触式密封 | 东莞办公家具厂家直销-美鑫【免费3D效果图】全国办公桌/会议桌定制 | X光检测仪_食品金属异物检测机_X射线检测设备_微现检测 | 设定时间记录电子秤-自动累计储存电子秤-昆山巨天仪器设备有限公司 | 煤矿人员精确定位系统_矿用无线通信系统_煤矿广播系统 | 首页-恒温恒湿试验箱_恒温恒湿箱_高低温试验箱_高低温交变湿热试验箱_苏州正合 | 搪玻璃冷凝器_厂家-越宏化工设备| 手持气象站_便携式气象站_农业气象站_负氧离子监测站-山东万象环境 | 胜为光纤光缆_光纤跳线_单模尾纤_光纤收发器_ODF光纤配线架厂家直销_北京睿创胜为科技有限公司 - 北京睿创胜为科技有限公司 | 北京发电车出租-发电机租赁公司-柴油发电机厂家 - 北京明旺盛安机电设备有限公司 | 澳门精准正版免费大全,2025新澳门全年免费,新澳天天开奖免费资料大全最新,新澳2025今晚开奖资料,新澳马今天最快最新图库-首页-东莞市傲马网络科技有限公司 | LZ-373测厚仪-华瑞VOC气体检测仪-个人有毒气体检测仪-厂家-深圳市深博瑞仪器仪表有限公司 | 一体化污水处理设备_生活污水处理设备_全自动加药装置厂家-明基环保 | 山西3A认证|太原AAA信用认证|投标AAA信用证书-山西AAA企业信用评级网 | 紫外可见光分光度计-紫外分光度计-分光光度仪-屹谱仪器制造(上海)有限公司 | 国产离子色谱仪,红外分光测油仪,自动烟尘烟气测试仪-青岛埃仑通用科技有限公司 | 重庆网站建设,重庆网站设计,重庆网站制作,重庆seo,重庆做网站,重庆seo,重庆公众号运营,重庆小程序开发 | 定制/定做冲锋衣厂家/公司-订做/订制冲锋衣价格/费用-北京圣达信 | Trimos测长机_测高仪_TESA_mahr,WYLER水平仪,PWB对刀仪-德瑞华测量技术(苏州)有限公司 | 合肥展厅设计-安徽展台设计-合肥展览公司-安徽奥美展览工程有限公司 | 微型气泵-真空-蠕动-水泵-厂家-深圳市品亚科技有限公司 | 布袋除尘器|除尘器设备|除尘布袋|除尘设备_诺和环保设备 | 复合肥,化肥厂,复合肥批发,化肥代理,复合肥品牌-红四方 | 油罐车_加油机_加油卷盘_加油机卷盘_罐车人孔盖_各类球阀_海底阀等车用配件厂家-湖北华特专用设备有限公司 | 橡胶接头_橡胶软接头_可曲挠橡胶接头-巩义市创伟机械制造有限公司 |