自定义TextView
1 package com.sun.mytext; 2 3 import android.annotation.SuppressLint; 4 import android.content.Context; 5 import android.os.Build; 6 import android.text.Layout; 7 import android.text.SpannableString; 8 import android.text.Spanned; 9 import android.text.StaticLayout; 10 import android.text.method.LinkMovementMethod; 11 import android.util.AttributeSet; 12 import android.view.View; 13 import android.widget.TextView; 14 15 /** 16 * @author Sigal 17 * on 2018/12/18 18 * note: 可展开的TextView 19 */ 20 @SuppressLint("AppCompatCustomView") 21 public class ExpandTextView extends TextView { 22 // 原始内容文本 23 private String originText; 24 // TextView可展示宽度 25 private int initWidth = 0; 26 // TextView最大行数 27 private int mMaxLines = 3; 28 // 收起的文案(颜色处理) 29 private SpannableString SPAN_CLOSE = null; 30 // 展开的文案(颜色处理) 31 private SpannableString SPAN_EXPAND = null; 32 private String TEXT_EXPAND = " 查看更多>"; 33 private String TEXT_CLOSE = " <收起"; 34 35 public expandtextview(context context) { 36 super(context); 37 initcloseend(); 38 } 39 40 context, attributeset attrs) 41 super(context, attrs); 42 43 44 45 attrs, int defstyleattr) 46 defstyleattr); 47 48 49 50 ** 51 * 设置textview可显示的最大行数 52 @param maxlines 最大行数 53 54 @override 55 void setmaxlines(int maxlines) 56 this.mmaxlines="maxLines;" 57 super.setmaxlines(maxlines); 58 59 60 61 初始化textview的可展示宽度 62 width 63 64 initwidth(int width){ 65 initwidth="width;" 66 67 68 69 收起的文案(颜色处理)初始化 70 71 private initcloseend(){ 72 string content="TEXT_CLOSE;" 73 span_close="new" spannablestring(content); 74 buttonspan span="new" buttonspan(getcontext(), new view.onclicklistener(){ 75 76 onclick(view v) 77 expandtextview.super.setmaxlines(integer.max_value); 78 setexpandtext(origintext); 79 80 }, r.color.coloraccent); 81 span_close.setspan(span, 0, content.length(), spanned.span_inclusive_exclusive); 82 83 84 85 展开的文案(颜色处理)初始化 86 87 initexpandend(){ 88 89 span_expand="new" 90 91 92 93 expandtextview.super.setmaxlines(mmaxlines); 94 setclosetext(origintext); 95 96 97 span_expand.setspan(span, 98 99 100 setclosetext(charsequence text) {101 102 if(span_close="=" null){103 initcloseend();104 }105 boolean appendshowall="false;//" true 不需要展开收起功能, false 需要展开收起功能106 origintext="text.toString();107" 108 sdk> = 16 可以直接从xml属性获取最大行数109 int maxLines = 0;110 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {111 maxLines = getMaxLines();112 } else{113 maxLines = mMaxLines;114 }115 String workingText = new StringBuilder(originText).toString();116 if (maxLines != -1) {117 Layout layout = createWorkingLayout(workingText);118 if (layout.getLineCount() > maxLines) {119 //获取一行显示字符个数,然后截取字符串数120 workingText = originText.substring(0, layout.getLineEnd(maxLines - 1)).trim();// 收起状态原始文本截取展示的部分121 String showText = originText.substring(0, layout.getLineEnd(maxLines - 1)).trim() + "..." + SPAN_CLOSE;122 Layout layout2 = createWorkingLayout(showText);123 // 对workingText进行-1截取,直到展示行数==最大行数,并且添加 SPAN_CLOSE 后刚好占满最后一行124 while (layout2.getLineCount() > maxLines) {125 int lastSpace = workingText.length()-1;126 if (lastSpace == -1) {127 break;128 }129 workingText = workingText.substring(0, lastSpace);130 layout2 = createWorkingLayout(workingText + "..." + SPAN_CLOSE);131 }132 appendShowAll = true;133 workingText = workingText + "...";134 }135 }136 137 setText(workingText);138 if (appendShowAll) {139 // 必须使用append,不能在上面使用+连接,否则spannable会无效140 append(SPAN_CLOSE);141 setMovementMethod(LinkMovementMethod.getInstance());142 }143 }144 145 public void setExpandText(String text) {146 if(SPAN_EXPAND == null){147 initExpandEnd();148 }149 Layout layout1 = createWorkingLayout(text);150 Layout layout2 = createWorkingLayout(text + TEXT_CLOSE);151 // 展示全部原始内容时 如果 TEXT_CLOSE 需要换行才能显示完整,则直接将TEXT_CLOSE展示在下一行152 if(layout2.getLineCount() > layout1.getLineCount()){153 setText(originText + "\n");154 }else{155 setText(originText);156 }157 append(SPAN_EXPAND);158 setMovementMethod(LinkMovementMethod.getInstance());159 }160 161 //返回textview的显示区域的layout,该textview的layout并不会显示出来,只是用其宽度来比较要显示的文字是否过长162 private Layout createWorkingLayout(String workingText) {163 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {164 return new StaticLayout(workingText, getPaint(), initWidth - getPaddingLeft() - getPaddingRight(),165 Layout.Alignment.ALIGN_NORMAL, getLineSpacingMultiplier(), getLineSpacingExtra(), false);166 } else{167 return new StaticLayout(workingText, getPaint(), initWidth - getPaddingLeft() - getPaddingRight(),168 Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);169 }170 }171 } 收起";>
自定义ClickableSpan
1 package com.sun.mytext; 2 3 import android.content.Context; 4 import android.text.TextPaint; 5 import android.text.style.ClickableSpan; 6 import android.view.View; 7 8 /** 9 * @author Sigal10 * on 2018/12/1811 * note:12 */13 public class ButtonSpan extends ClickableSpan {14 View.OnClickListener onClickListener;15 private Context context;16 private int colorId;17 18 public ButtonSpan(Context context, View.OnClickListener onClickListener) {19 this(context, onClickListener, R.color.textColor);20 }21 22 public ButtonSpan(Context context, View.OnClickListener onClickListener, int colorId){23 this.onClickListener = onClickListener;24 this.context = context;25 this.colorId = colorId;26 }27 28 @Override29 public void updateDrawState(TextPaint ds) {30 ds.setColor(context.getResources().getColor(colorId));31 ds.setUnderlineText(false);32 }33 34 @Override35 public void onClick(View widget) {36 if (onClickListener != null) {37 onClickListener.onClick(widget);38 }39 }40 41 }
PxUtils 和 ScreenUtils
1 package com.sun.mytext; 2 3 import android.content.Context; 4 5 /** 6 * @author Sigal 7 * on 2018/12/18 8 * note: px与dp、sp的转换 9 */10 public class PxUtils {11 /**12 * 将px转换为与之相等的dp13 */14 public static int px2dp(Context context, float pxValue) {15 final float scale = context.getResources().getDisplayMetrics().density;16 return (int) (pxValue / scale + 0.5f);17 }18 19 /**20 * 将dp转换为与之相等的px21 */22 public static int dp2px(Context context, float dipValue) {23 final float scale = context.getResources().getDisplayMetrics().density;24 return (int) (dipValue * scale + 0.5f);25 }26 27 /**28 * 将px转换为sp29 */30 public static int px2sp(Context context, float pxValue) {31 final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;32 return (int) (pxValue / fontScale + 0.5f);33 }34 35 /**36 * 将sp转换为px37 */38 public static int sp2px(Context context, float spValue) {39 final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;40 return (int) (spValue * fontScale + 0.5f);41 }42 43 }
1 package com.sun.mytext; 2 3 import android.app.Activity; 4 import android.content.Context; 5 import android.graphics.Bitmap; 6 import android.graphics.Rect; 7 import android.util.DisplayMetrics; 8 import android.view.View; 9 import android.view.WindowManager; 10 11 /** 12 * @author Sigal 13 * on 2018/12/18 14 * note: 15 */ 16 public class ScreenUtils { 17 private ScreenUtils() 18 { 19 /* cannot be instantiated */ 20 throw new UnsupportedOperationException("cannot be instantiated"); 21 } 22 23 /** 24 * 获得屏幕高度 25 * @param context 26 * @return 27 */ 28 public static int getScreenWidth(Context context) 29 { 30 WindowManager wm = (WindowManager) context 31 .getSystemService(Context.WINDOW_SERVICE); 32 DisplayMetrics outMetrics = new DisplayMetrics(); 33 wm.getDefaultDisplay().getMetrics(outMetrics); 34 return outMetrics.widthPixels; 35 } 36 37 /** 38 * 获得屏幕宽度 39 * @param context 40 * @return 41 */ 42 public static int getScreenHeight(Context context) 43 { 44 WindowManager wm = (WindowManager) context 45 .getSystemService(Context.WINDOW_SERVICE); 46 DisplayMetrics outMetrics = new DisplayMetrics(); 47 wm.getDefaultDisplay().getMetrics(outMetrics); 48 return outMetrics.heightPixels; 49 } 50 51 /** 52 * 获得状态栏的高度 53 * @param context 54 * @return 55 */ 56 public static int getStatusHeight(Context context) 57 { 58 59 int statusHeight = -1; 60 try 61 { 62 Class clazz = Class.forName("com.android.internal.R$dimen"); 63 Object object = clazz.newInstance(); 64 int height = Integer.parseInt(clazz.getField("status_bar_height") 65 .get(object).toString()); 66 statusHeight = context.getResources().getDimensionPixelSize(height); 67 } catch (Exception e) 68 { 69 e.printStackTrace(); 70 } 71 return statusHeight; 72 } 73 74 /** 75 * 获取当前屏幕截图,包含状态栏 76 * @param activity 77 * @return 78 */ 79 public static Bitmap snapShotWithStatusBar(Activity activity) 80 { 81 View view = activity.getWindow().getDecorView(); 82 view.setDrawingCacheEnabled(true); 83 view.buildDrawingCache(); 84 Bitmap bmp = view.getDrawingCache(); 85 int width = getScreenWidth(activity); 86 int height = getScreenHeight(activity); 87 Bitmap bp = null; 88 bp = Bitmap.createBitmap(bmp, 0, 0, width, height); 89 view.destroyDrawingCache(); 90 return bp; 91 92 } 93 94 /** 95 * 获取当前屏幕截图,不包含状态栏 96 * @param activity 97 * @return 98 */ 99 public static Bitmap snapShotWithoutStatusBar(Activity activity) {100 View view = activity.getWindow().getDecorView();101 view.setDrawingCacheEnabled(true);102 view.buildDrawingCache();103 Bitmap bmp = view.getDrawingCache();104 Rect frame = new Rect();105 activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);106 int statusBarHeight = frame.top;107 108 int width = getScreenWidth(activity);109 int height = getScreenHeight(activity);110 Bitmap bp = null;111 bp = Bitmap.createBitmap(bmp, 0, statusBarHeight, width, height112 - statusBarHeight);113 view.destroyDrawingCache();114 return bp;115 }116 }
代码引用
1 package com.sun.mytext; 2 3 import android.app.Activity; 4 import android.support.v7.app.AppCompatActivity; 5 import android.os.Bundle; 6 /** 7 * @author Sigal 8 * on 2018/12/18 9 * note:10 */11 public class MainActivity extends Activity {12 13 @Override14 protected void onCreate(Bundle savedInstanceState) {15 super.onCreate(savedInstanceState);16 setContentView(R.layout.activity_main);17 // 设置TextView可展示的宽度 ( 父控件宽度 - 左右margin - 左右padding)18 int whidth = ScreenUtils.getScreenWidth(this) - PxUtils.dp2px(this, 16 * 2);19 ExpandTextView mContentExpandTextView = findViewById(R.id.etv);20 mContentExpandTextView.initWidth(whidth);21 // 设置最大行数(如果SDK >= 16 也可以直接在xml里设置)22 mContentExpandTextView.setMaxLines(3);23 String content = "披绣闼,俯雕甍,山原旷其盈视,川泽纡其骇瞩。闾阎扑地,钟鸣鼎食之家;" +24 "舸舰弥津,青雀黄龙之舳。云销雨霁,彩彻区明。落霞与孤鹜齐飞,秋水共长天一色。渔舟唱晚,响穷彭蠡之滨;雁阵惊寒,声断衡阳之浦。\n" +25 "\n" +26 "遥襟甫畅,逸兴遄飞。爽籁发而清风生,纤歌凝而白云遏。睢园绿竹,气凌彭泽之樽;" +27 "邺水朱华,光照临川之笔。四美具,二难并。穷睇眄于中天,极娱游于暇日。天高地迥," +28 "觉宇宙之无穷;兴尽悲来,识盈虚之有数。望长安于日下,目吴会于云间。地势极而南溟深,天柱高而北辰远。关山难越," +29 "谁悲失路之人?萍水相逢,尽是他乡之客。怀帝阍而不见,奉宣室以何年?\n" +30 "\n" +31 "嗟乎!时运不齐,命途多舛。冯唐易老,李广难封。屈贾谊于长沙,非无圣主;窜梁鸿于海曲,岂乏明时?所赖君子见机,达人知命。" +32 "老当益壮,宁移白首之心?穷且益坚,不坠青云之志。酌贪泉而觉爽,处涸辙以犹欢。北海虽赊,扶摇可接;东隅已逝,桑榆非晚。" +33 "孟尝高洁,空余报国之情;阮籍猖狂,岂效穷途之哭!";34 mContentExpandTextView.setCloseText(content);35 }36 }
布局文件
1 28 9 13 14