android开发游记:textview超过长度点击展开自动滚动(在一个TextView中实现,不增加布局复杂度)

来源:互联网 发布:android ndk r10d mac 编辑:程序博客网 时间:2024/06/05 07:39

最近有需求需要在一个有限高度的页面上显示超过其高度的文字,当文字超过最大行之后显示省略号和查看更多,然后点击查看更多显示完整的信息,并且可以滚动。

先看效果图:

这里写图片描述

功能很简单,网上搜的有人使用了scrollview来滚动,再添加一个按钮”查看更多”,然后点击后把消息全部展示,再把按钮隐藏。

但是这样做不是觉得很复杂,其实所有的工作在一个textView里就可以完成了,包括滚动效果,包括查看更多的按钮。这些都可以在同一个textView里处理完,先上代码,后面再解释其原理。

我封装成了一个工具类,使用该工具类来处理TextView的显示逻辑

使用方法:

导入下面的工具类MoreTextUtil,在你需要设置TextView拥有展开效果的时候添加下面代码:

textView.setText("你要显示的内容");MoreTextUtil.setMore(textView);
  • 1
  • 2

你也可以这样,使用重载的方法:

MoreTextUtil.setMore(textView,"你要显示的内容");
  • 1

默认最大可显示行数为5行,如果需要自定义就给自己的TextView的布局加上下面的属性:

android:maxLines="9"    //最多显示9行
  • 1

用法很简单吧,下面上源码:

package com.shelwee.update.utils;import android.annotation.SuppressLint;import android.text.Layout;import android.text.SpannableString;import android.text.Spanned;import android.text.TextPaint;import android.text.TextUtils.TruncateAt;import android.text.method.LinkMovementMethod;import android.text.method.ScrollingMovementMethod;import android.text.style.ClickableSpan;import android.view.View;import android.view.ViewTreeObserver;import android.view.ViewTreeObserver.OnGlobalLayoutListener;import android.widget.TextView;import com.shelwee.updater.R;public class MoreTextUtil {    public static boolean hasMesure = false;    //是否已经执行过一次    public static void setMore(TextView textV,String content){        textV.setText(content);        setMore(textV,"...","查看更多");    }    public static void setMore(TextView textV){        setMore(textV,"...","查看更多");    }    @SuppressLint("NewApi")    public static void setMore(final TextView textV, final String ellipsis, final String strmore) {        if (textV == null) {            return;        }        if (2147483647 == textV.getMaxLines()) textV.setMaxLines(5);        textV.setEllipsize(TruncateAt.END);        textV.setVerticalScrollBarEnabled(true);        hasMesure = false;        //添加布局变化监听器,view 布局完成时调用,每次view改变时都会调用        ViewTreeObserver vto = textV.getViewTreeObserver();        vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {            @Override            public void onGlobalLayout() {                if (!hasMesure) {                    int maxLines = textV.getMaxLines();                    int lines = textV.getLineCount();                    //如果文字的行数超过最大行数,展示缩略的textview                    if (lines >= maxLines) {                        Layout layout=textV.getLayout();                        String str=layout.getText().toString();                        int end = layout.getLineEnd(maxLines-2);                        str = str.substring(0, end);                    //缩略的文字                        String strall = textV.getText().toString();     //完整的文字                        hasMesure = true;                        SpannableString spanstr;                        //如果以换行符结尾,则不再换行                        if (str.endsWith("\n")) {                            spanstr = new SpannableString(str + ellipsis + strmore);                        }else {                            spanstr = new SpannableString(str + "\n" + ellipsis + strmore);                        }                        //设置“查看更多”的点击事件                        spanstr.setSpan(new MyClickableSpan(strall,textV.getResources().getColor(android.R.color.holo_green_dark)), spanstr.length() - strmore.length(),                                spanstr.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);                        textV.setText(spanstr);                        //移除默认背景色                        textV.setHighlightColor(textV.getResources().getColor(android.R.color.transparent));                        textV.setMovementMethod(LinkMovementMethod.getInstance());                    }                }            }        });    }    static class MyClickableSpan extends ClickableSpan{        private String str;        private int color;        public MyClickableSpan(String str,int color) {            this.str = str;            this.color = color;        }        @Override        public void onClick(View view) {            ((TextView)view).setMovementMethod(new ScrollingMovementMethod());            ((TextView)view).setText(str);        }        @Override        public void updateDrawState(TextPaint ds) {            super.updateDrawState(ds);            ds.setColor(color);         //设置“查看更多”字体颜色            ds.setUnderlineText(false); //设置“查看更多”无下划线,默认有            ds.clearShadowLayer();        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 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
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107

代码中的注释很清楚,说下重点: 
代码中为什么要使用布局变化监听器呢,如下:

ViewTreeObserver vto = textV.getViewTreeObserver();vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener(){    ....});
  • 1
  • 2
  • 3
  • 4

原因是TextView会根据字符的排版规则自动换行,这种换行受字体大小的影响,而且英文字母符号中文等不同字符占的宽并不同,而且TextView还会自动匹配单词选择性换行,所以我们几乎无法通过一个字符串计算出TextView会在什么时候换行,我们也无法计算出在字符串的哪个位置会因为显示不下而出现截断。

那么我们就换一种方式,直接把字符串绘制到TextView中,在TextView生成布局渲染完成后把字符设置到页面前一刻再回调我们的方法,这样我们就可以得到TextView具体绘制了多少行,每行的字符是什么,这也就是为什么要使用ViewTreeObserver 添加 OnGlobalLayoutListener 的原因。在onGlobalLayout回调方法中完成我们的业务逻辑。

但是值得注意的是,OnGlobalLayoutListener 会在绘制过程中被调用多少,而我们的业务逻辑只需要处理一次就可以了,为了避免不必要的性能损失,我们得加以控制,通过hasMesure 这个布尔变量来控制代码只执行一次。

另外一个重要的地方就是,我们没有添加按钮,那就以为着我们的TextView必须要支持局部点击事件,就是说,点击“查看更多”的时候要有响应,而点其他地方没有。还好TextView的SpannableString (复合文本) 支持多事件和样式的处理,如下:

//设置“查看更多”的点击事件spanstr.setSpan(new MyClickableSpan(strall,textV.getResources().getColor(R.color.cc_orage)),                spanstr.length() - strmore.length(),                spanstr.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);textV.setText(spanstr);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这里就使用了复合文本格式来设置局部的点击事件,方法原型如下:

spanstr.setSpan(Object what,int start,int end,int flags)
  • 1

what 参数可以设置一个监听器 
start 指定了设置的字符的开始位置 
end 是指定字符的结束位置 
flags 是间隔模式,SPAN_EXCLUSIVE_EXCLUSIVE表示不影响前后相邻的字符

接着看复合文本事件监听器:

static class MyClickableSpan extends ClickableSpan{        private String str;        private int color;        public MyClickableSpan(String str,int color) {            this.str = str;            this.color = color;        }        @Override        public void onClick(View view) {            ((TextView)view).setMovementMethod(new ScrollingMovementMethod());            ((TextView)view).setText(str);        }        @Override        public void updateDrawState(TextPaint ds) {            super.updateDrawState(ds);            ds.setColor(color);         //设置“查看更多”字体颜色            ds.setUnderlineText(false); //设置“查看更多”无下划线,默认有            ds.clearShadowLayer();        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

构造方法我传入了完整的字符串和一个颜色值,当点击后把显示的字符串换成完整的字符,并设置可以滚动就ok,至于颜色值,我希望“查看更多”这4个字拥有不同的颜色,当然你不设置也没有什么关系,丑一点而已。 
在点击事件执行之前,会先调用updateDrawState(TextPaint ds)方法设置绘制格式,在这里设置颜色和取消下划线,如果需要其他特殊效果的话也在这里处理。

觉得不错的下面有个顶,可以点一下 :)

阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 泡温泉不想素颜怎么办 身份证过期1个月怎么办 工作时被同事骂怎么办 骗了感情骗了钱怎么办 别人老骂我胸小怎么办 别人说你没素质怎么办 我的12306忘了怎么办 电工证6年过期怎么办 电工证复审过2年怎么办 电工证过期2个月怎么办 如果报到证丢了怎么办 考编报到证丢了怎么办 2017报到证丢了怎么办 携程机票不能退怎么办 暗黑3进度全没了怎么办 从飞机上掉下来怎么办? 白带多有酸臭味怎么办 私募基金跑路了怎么办 车险的保单丢了怎么办 做的3d有病毒怎么办 猫吐白沫没精神怎么办 光伏发电坏了怎么办 户户通违规移机怎么办 原岳阳恒立地块怎么办 发票打错一个字怎么办 香港自由行g签证怎么办 小米5铃声不响怎么办 德龙驾驶室爱跳怎么办 高低速的车换挡怎么办 挂车鞍座间隙大怎么办 自考本科证丢了怎么办 学籍号密码忘了怎么办 嘀嘀虎没流量了怎么办 被房产中介骗了怎么办 孕中期胎盘早剥怎么办 船用雷达不扫描怎么办? 招工年龄报小了怎么办 三茅hr app闪退怎么办 ipad开关键坏了怎么办 档案单位给丢了怎么办 单位把档案弄丢了怎么办