博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
自定义Viewgroup实现流式布局(2):条件换行的自定义view
阅读量:4058 次
发布时间:2019-05-25

本文共 7197 字,大约阅读时间需要 23 分钟。

只是实例,因为Textview就带换行功能。

上一篇最后说canvas.darwText可以来绘制view,实现类似textview的显示,但是超出控件长度就会看不到,并不会换行;至于一段文字计算他在给定长度上如何换行等等很麻烦,这里只举例:如果文字一行可以容纳则一行显示,如果一行不能显示则每行只显示7个字符。

注:这里只处理wrap和match的情况,如果给指定宽度还需要去判断宽度能不能放下7个字符的问题。

这里只说自定义view的情况,自定义viewgroup的先放下:

(1).onMeasure必须做处理,当写wrap的时候必须手动计算宽高:

当一行可以容纳时,宽就是文字的宽,高就是文字的高;
//因为默认写wrap时候相当于match,所以上面测量的width其实就是match也就是屏幕的宽度,实际可以写字的区域是减去左右pading的区域
int canUseWidth = widthSize - getPaddingLeft() - getPaddingRight();
当一行不能容纳时,宽就是当前所有行文字宽度最大那行的宽度(同样7个字符,中英文不一样)+两个padding,高就是行数*行高+两个padding。
计算文字所占宽高的方法如下:
paint = new Paint();
paint.setTextSize(SizeUtil.sp2px(getContext(), 20.0f));
paint.setColor(Color.parseColor("#000000"));
paint.setAntiAlias(true);//抗锯齿
paint.setFilterBitmap(true);//位图过滤
;
rect = new Rect();
//得到写这些文字需要占据的方形区域
paint.getTextBounds(text, 0, text.length(), rect);
判断mode,如果是EXACTLY,那set的时候就是MeasureSpec.getSize的尺寸;
如果不是,那就是上面计算出来的宽高。
(2).draw文字
通过onMeasure已经计算出来warp时文字所占的区域,但是文字并没有被绘制上去。
onMeasure的时候已经知道能分几行,然后对文字也进行了分段,现在把这若干段文字分别画上去就可以了。
可能会想到onLayout,但是这是view,不是ViewGroup,所以只能多canvas.draw几次,只是每次draw的时候基线的坐标不同。

实例代码如下:

 

//伪换行的textviewpublic class MyTextview extends View {    public void setText(String text) {        this.text = text;        initView();        invalidate();    }    public MyTextview(Context context) {        super(context);        initView();    }    public MyTextview(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);        initAtters(context, attrs);        initView();    }    public MyTextview(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        initAtters(context, attrs);        initView();    }    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)    public MyTextview(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {        super(context, attrs, defStyleAttr, defStyleRes);        initAtters(context, attrs);        initView();    }    private void initAtters(Context context, @Nullable AttributeSet attrs) {        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyTextview);        text = array.getString(R.styleable.MyTextview_texcontent);    }    //画笔    Paint paint;    //文字所占的区域,用来确定文字的起始坐标    Rect rect;    String text = "需要显示的文字";    private void initView() {        Log.e("init里面放的数字是", text);        if (paint == null) {            paint = new Paint();            paint.setTextSize(SizeUtil.sp2px(getContext(), 20.0f));            paint.setColor(Color.parseColor("#000000"));            paint.setAntiAlias(true);            paint.setFilterBitmap(true);//位图过滤            ;        }        if (rect == null) {            rect = new Rect();        }        paint.getTextBounds(text, 0, text.length(), rect);    }    String TAG = "MyTextview";    @Override    protected void onDraw(Canvas canvas) {        //super.onDraw(canvas);        Log.e(TAG, "onDraw: ");        //为了让文字纵向居中,计算正确的Y值        Paint.FontMetrics fontMetrics = paint.getFontMetrics();        //ascent的绝对值=descent+2*(文字纵向中线与基准线的高度差)        //float baseline = 0;        //Math.abs(fontMetrics.ascent) =fontMetrics.descent+2*(baseline-height/2);        if (lineNum > 1) {            //绘制文字            int startY = getPaddingTop();            for (int i = 0; i < rowStrings.size(); i++) {                Rect rect_T = new Rect();                //得到写这些文字需要占据的方形区域                paint.getTextBounds(rowStrings.get(i), 0, rowStrings.get(i).length(), rect_T);                int height = rect_T.height();                float y = height / 2 + (Math.abs(fontMetrics.ascent) - fontMetrics.descent) / 2;                canvas.drawText(rowStrings.get(i), 0 + getPaddingLeft(), startY + y, paint);                startY = startY + height;            }        } else {            float y = rect.height() / 2 + (Math.abs(fontMetrics.ascent) - fontMetrics.descent) / 2;            canvas.drawText(text, 0 + getPaddingLeft(), y + getPaddingTop(), paint);        }    }    List
rowStrings; int lineNum; //模拟一行可以放下就不换行,如果大于1行每行只容纳五个字符(只是模拟,实际操作还需要判断当前位置一行还能不能放下5个字符) @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //得到写这些文字需要占据的方形区域 Log.e(TAG, "onMeasure: "); rowStrings = new ArrayList<>(); lineNum = 0; int widthMode = MeasureSpec.getMode(widthMeasureSpec); //获取宽的模式 int heightMode = MeasureSpec.getMode(heightMeasureSpec); //获取高的模式 int widthSize = MeasureSpec.getSize(widthMeasureSpec); //获取宽的尺寸 int heightSize = MeasureSpec.getSize(heightMeasureSpec); //获取高的尺寸 //判断,如果文字的长度大于宽度就换行 //因为默认写wrap时候相当于match,所以上面测量的width其实就是match也就是屏幕的宽度 int canUseWidth = widthSize - getPaddingLeft() - getPaddingRight(); if (rect.width() > canUseWidth) {//表示一行放不下 //一行只容纳7个字符 double rows = text.length() / 7.0; lineNum = (int) Math.ceil(rows); //一行文字所包含的文字数 for (int i = 0; i < lineNum; i++) { if (i == lineNum - 1) { rowStrings.add(text.substring(i * 7, text.length())); } else { rowStrings.add(text.substring(i * 7, (i + 1) * 7)); } } } else { lineNum = 1; } int width; int height; if (widthMode == MeasureSpec.EXACTLY) { //如果match_parent或者具体的值,直接赋值 width = widthSize; } else { //如果是wrap_content,我们要得到控件需要多大的尺寸 float textWidth = rect.width(); //文本的宽度 //控件的宽度就是文本的宽度加上两边的内边距。内边距就是padding值,在构造方法执行完就被赋值 //根据行数来计算宽高 if (lineNum > 1) { //计算分割的文字里面的最大宽度 width = 0; for (String s : rowStrings) { Rect rect_T = new Rect(); //得到写这些文字需要占据的方形区域 paint.getTextBounds(s, 0, s.length(), rect_T); int width_T = rect_T.width(); if (width_T > width) { width = width_T; } } width = (int) (getPaddingLeft() + width + getPaddingRight()); } else { width = (int) (getPaddingLeft() + textWidth + getPaddingRight()); } } //高度跟宽度处理方式一样 if (heightMode == MeasureSpec.EXACTLY) { height = heightSize; } else { float textHeight = rect.height(); if (lineNum > 1) { //计算分割的文字里面的最大宽度 height = 0; for (String s : rowStrings) { Rect rect_T = new Rect(); //得到写这些文字需要占据的方形区域 paint.getTextBounds(s, 0, s.length(), rect_T); int height_T = rect_T.height(); height = height + height_T; } height = (int) (getPaddingTop() + height + getPaddingBottom()); } else { height = (int) (getPaddingTop() + textHeight + getPaddingBottom()); } Log.e("计算出来textview高度", height + ""); } //保存测量宽度和测量高度 setMeasuredDimension(width, height); }}

 

转载地址:http://icrci.baihongyu.com/

你可能感兴趣的文章
JSP中文乱码总结
查看>>
Java-IO-File类
查看>>
Java-IO-java的IO流
查看>>
Java-IO-输入/输出流体系
查看>>
Java实现DES加密解密
查看>>
HTML基础
查看>>
Java IO
查看>>
Java NIO
查看>>
Java大数据:Hbase分布式存储入门
查看>>
Java大数据:全文搜索引擎Elasticsearch入门
查看>>
大数据学习:Hadoop入门学习书单
查看>>
大数据学习:Spark SQL入门简介
查看>>
大数据学习:Spark RDD操作入门
查看>>
大数据框架:Spark 生态实时流计算
查看>>
大数据入门:Hive和Hbase区别对比
查看>>
大数据入门:ZooKeeper工作原理
查看>>
大数据入门:Zookeeper结构体系
查看>>
大数据入门:Spark RDD基础概念
查看>>
大数据入门:SparkCore开发调优原则
查看>>
大数据入门:Java和Scala编程对比
查看>>