在Textview中获取指定文字位置(兼顾网址链接和emoji表情),并在其附近展示图片

本文主要讲在Textview中获取指定文字的位置,最后,附带一点文字宽高的测量。
下面,我会给出全部源码。自己建个demo,复制进去就能跑起来。

先强调一下,不建议在ListView中使用。最好是在一个单独展示Textview的界面中使用。

先上效果图:
这里写图片描述

需求及说明:给出一段文字,里面可能包含链接或表情。同时,也有要匹配的关键字,最后,在关键字处展示一个图片,用于强调这个关键字。对应关键字,也可能有多种情况,如:@人名、股票代码等。这里ABC和000001代替。最后,我要在第一个出现的000001位置展示图片。

在源码开始前,先看看项目结构:
这里写图片描述

说明:
CHEN是工具类
DataBean是listView中,list里面要放的数据的对象
KeyBean是关键字对象
Mainactivity_1是单纯展示一个Textview展示效果图中的内容
Mainactivity_2是把1中的功能放到ListView中(有问题,到现在为止,我没解决,下面会详细说)
Mainactivity_3是文字宽高的测量
MyTextView是可以匹配链接和表情的自定义Textview
PicImageView是自定义的ImageView,是用于自动展示帧动画的ImageView
ViewHolder是ListView中用的

代码:
先上工具相关的准备东西:
CHEN

package com.chen.demo;

import android.content.Context;

public class CHEN {

    /**
     * 网址要被替换成的文字
     */
    public static String REPLACEMENT_STRING = "*点击链接";

    //    /**
    //     * 匹配网址的正则表达式。以http://为例
    //     */
    //    public static String urlRegex = "(http://[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>,]*)?)|(www.[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>,]*)?)|([a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>,]*)?)";

    /**
     * 匹配网址的正则表达式。有http://、https://、ftp://这3中开头的
     */
    public static String urlRegex = "((http[s]{0,1}|ftp)://[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>,]*)?)|(www.[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>,]*)?)|([a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>,]*)?)";

    public static int sp2px(float spValue, Context context) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * fontScale + 0.5f);
    }

}

DataBean

package com.chen.demo;

public class DataBean {

    //关键字的内容
    private String content;
    //类型,用于区分是否需要在关键字位置显示图片
    private String type;

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }
}

KeyBean

package com.chen.demo;


public class KeyBean {

    //关键字的内容
    private String content;

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

}

ViewHolder

package com.chen.demo;

import android.util.SparseArray;
import android.view.View;


public class ViewHolder {

    private ViewHolder(){

    }


    public static <T extends View> T get(View convertView, int id){

        SparseArray<View> viewHolder=(SparseArray<View>) convertView.getTag();
        if(viewHolder==null){
            viewHolder=new SparseArray<View>();
            convertView.setTag(viewHolder);
        }
        View childView=viewHolder.get(id);
        if(childView==null){
            childView=convertView.findViewById(id);
            viewHolder.put(id, childView);
        }
        return (T)childView;
    }

}

自定义View
MyTextView
这里面定义了2种方法,一种是直接展示内容,关于关键字的位置,需要通过另外的方法获取;一种是展示完后直接返回关键字的位置

package com.chen.demo;


import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.text.style.ImageSpan;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MyTextView extends TextView {

    private Context context = null;

    private int firstKeyWordPosition = -1;

    public MyTextView(Context context) {
        super(context);
        this.context = context;
    }

    public MyTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
    }

    public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
    }


    public void showContent_1(String str, ArrayList<KeyBean> keyBeanList) {

        String content = str;

        //处理匹配的url
        Pattern p = Pattern.compile(CHEN.urlRegex);
        Matcher m = p.matcher(content);
        ArrayList<String> urlList = new ArrayList<String>();
        while (m.find()) {
            String urlStr = m.group();
            if (urlStr.contains("http://") || urlStr.contains("ftp://")) {
                //如果末尾有英文逗号或者中文逗号等,就去掉
                while (urlStr.endsWith(",") || urlStr.endsWith(",") || urlStr.endsWith(".") || urlStr.endsWith("。") || urlStr.endsWith(";") || urlStr.endsWith(";") || urlStr.endsWith("!") || urlStr.endsWith("!") || urlStr.endsWith("?") || urlStr.endsWith("?")) {
                    urlStr = urlStr.substring(0, urlStr.length() - 1);
                }
                urlList.add(urlStr);
                content = content.replace(urlStr, CHEN.REPLACEMENT_STRING);
            }
        }

        SpannableString spannableString = new SpannableString(content);

        //处理表情相关
        String emoji_string = "\\[(.+?)\\]";
        Pattern emoji_patten = Pattern.compile(emoji_string);
        Matcher matcher = emoji_patten.matcher(content);

        while (matcher.find()) {

            Drawable drawable = context.getResources().getDrawable(R.mipmap.emoji_weixiao);
            drawable.setBounds(0, 0, CHEN.sp2px(25, context), CHEN.sp2px(25, context));
            ImageSpan imgSpan = new ImageSpan(drawable);
            spannableString.setSpan(imgSpan, matcher.start(),
                    matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

        }

        //表情相关处理结束

        content = spannableString.toString();
        //处理链接
        if (urlList.size() > 0) {

            int urlStartNew = 0;
            int urlStartOld = 0;

            String urlTemp = content;

            for (int i = 0; i < urlList.size(); i++) {

                final String regexUrl = urlList.get(i);

                spannableString.setSpan(new ClickableSpan() {

                                            @Override
                                            public void updateDrawState(TextPaint ds) {
                                                // TODO Auto-generated method stub
                                                super.updateDrawState(ds);
                                                ds.setColor(0xff2097D9);
                                                ds.setUnderlineText(false);
                                            }

                                            @Override
                                            public void onClick(View widget) {
                                                Toast.makeText(context, regexUrl, Toast.LENGTH_SHORT).show();
                                            }

                                        }, urlStartOld + urlTemp.indexOf(CHEN.REPLACEMENT_STRING), urlStartOld + urlTemp.indexOf(CHEN.REPLACEMENT_STRING) + CHEN.REPLACEMENT_STRING.length(),
                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                try {
                    //“点击链接”前面的回形针图片。大小可自己调整
                    Drawable drawable = context.getResources().getDrawable(R.mipmap.web_link);
                    drawable.setBounds(0, 0, CHEN.sp2px(25, context), CHEN.sp2px(25, context));
                    //                    ImageSpan imgSpan = new ImageSpan(drawable);
                    spannableString.setSpan(new ImageSpan(drawable), urlStartOld + urlTemp.indexOf(CHEN.REPLACEMENT_STRING), urlStartOld + urlTemp.indexOf(CHEN.REPLACEMENT_STRING) + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

                } catch (Exception e) {
                    //异常以后,就不加小图片了
                }

                setMovementMethod(LinkMovementMethod.getInstance());

                urlStartNew = urlTemp.indexOf(CHEN.REPLACEMENT_STRING) + CHEN.REPLACEMENT_STRING.length();
                urlStartOld += urlStartNew;
                urlTemp = urlTemp.substring(urlStartNew);
            }
        }

        //处理关键字
        if (keyBeanList != null) {
            for (int i = 0; i < keyBeanList.size(); i++) {
                final String data = keyBeanList.get(i).getContent();

                String temp = content;
                int startNew = 0;
                int startOld = 0;
                if (temp.contains(data)) {
                    while (temp.contains(data)) {
                        spannableString.setSpan(new ClickableSpan() {

                                                    @Override
                                                    public void updateDrawState(TextPaint ds) {
                                                        super.updateDrawState(ds);
                                                        ds.setColor(0xff2097D9);
                                                        ds.setUnderlineText(false);
                                                    }

                                                    @Override
                                                    public void onClick(View widget) {
                                                        Toast.makeText(context, data, Toast.LENGTH_SHORT).show();
                                                    }
                                                }, startOld + temp.indexOf(data), startOld + temp.indexOf(data) + data.length(),
                                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

                        setMovementMethod(LinkMovementMethod.getInstance());
                        startNew = temp.indexOf(data) + data.length();
                        startOld += startNew;
                        temp = temp.substring(startNew);
                    }
                }
            }
        }

        setText(spannableString);

        if (keyBeanList != null) {

            try {
                firstKeyWordPosition = content.indexOf(keyBeanList.get(1).getContent());
            } catch (Exception e) {
                firstKeyWordPosition = -1;
            }

        }

    }

    public int getFirstKeyWordPosition() {
        return firstKeyWordPosition;
    }

    public int showContent_2(String str, ArrayList<KeyBean> keyBeanList) {

        String content = str;

        //处理匹配的url
        Pattern p = Pattern.compile(CHEN.urlRegex);
        Matcher m = p.matcher(content);
        ArrayList<String> urlList = new ArrayList<String>();
        while (m.find()) {
            String urlStr = m.group();
            if (urlStr.contains("http://") || urlStr.contains("ftp://")) {
                //如果末尾有英文逗号或者中文逗号等,就去掉
                while (urlStr.endsWith(",") || urlStr.endsWith(",") || urlStr.endsWith(".") || urlStr.endsWith("。") || urlStr.endsWith(";") || urlStr.endsWith(";") || urlStr.endsWith("!") || urlStr.endsWith("!") || urlStr.endsWith("?") || urlStr.endsWith("?")) {
                    urlStr = urlStr.substring(0, urlStr.length() - 1);
                }
                urlList.add(urlStr);
                content = content.replace(urlStr, CHEN.REPLACEMENT_STRING);
            }
        }

        SpannableString spannableString = new SpannableString(content);

        //处理表情相关
        String emoji_string = "\\[(.+?)\\]";
        Pattern emoji_patten = Pattern.compile(emoji_string);
        Matcher matcher = emoji_patten.matcher(content);

        while (matcher.find()) {

            Drawable drawable = context.getResources().getDrawable(R.mipmap.emoji_weixiao);
            drawable.setBounds(0, 0, CHEN.sp2px(25, context), CHEN.sp2px(25, context));
            ImageSpan imgSpan = new ImageSpan(drawable);
            spannableString.setSpan(imgSpan, matcher.start(),
                    matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

        }

        //表情相关处理结束

        content = spannableString.toString();
        //处理链接
        if (urlList.size() > 0) {

            int urlStartNew = 0;
            int urlStartOld = 0;

            String urlTemp = content;

            for (int i = 0; i < urlList.size(); i++) {

                final String regexUrl = urlList.get(i);

                spannableString.setSpan(new ClickableSpan() {

                                            @Override
                                            public void updateDrawState(TextPaint ds) {
                                                // TODO Auto-generated method stub
                                                super.updateDrawState(ds);
                                                ds.setColor(0xff2097D9);
                                                ds.setUnderlineText(false);
                                            }

                                            @Override
                                            public void onClick(View widget) {
                                                Toast.makeText(context, regexUrl, Toast.LENGTH_SHORT).show();
                                            }

                                        }, urlStartOld + urlTemp.indexOf(CHEN.REPLACEMENT_STRING), urlStartOld + urlTemp.indexOf(CHEN.REPLACEMENT_STRING) + CHEN.REPLACEMENT_STRING.length(),
                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                try {
                    //“点击链接”前面的回形针图片。大小可自己调整
                    Drawable drawable = context.getResources().getDrawable(R.mipmap.web_link);
                    drawable.setBounds(0, 0, CHEN.sp2px(25, context), CHEN.sp2px(25, context));
                    //                    ImageSpan imgSpan = new ImageSpan(drawable);
                    spannableString.setSpan(new ImageSpan(drawable), urlStartOld + urlTemp.indexOf(CHEN.REPLACEMENT_STRING), urlStartOld + urlTemp.indexOf(CHEN.REPLACEMENT_STRING) + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

                } catch (Exception e) {
                    //异常以后,就不加小图片了
                }

                setMovementMethod(LinkMovementMethod.getInstance());

                urlStartNew = urlTemp.indexOf(CHEN.REPLACEMENT_STRING) + CHEN.REPLACEMENT_STRING.length();
                urlStartOld += urlStartNew;
                urlTemp = urlTemp.substring(urlStartNew);
            }
        }

        //处理关键字
        if (keyBeanList != null) {
            for (int i = 0; i < keyBeanList.size(); i++) {
                final String data = keyBeanList.get(i).getContent();

                String temp = content;
                int startNew = 0;
                int startOld = 0;
                if (temp.contains(data)) {
                    while (temp.contains(data)) {
                        spannableString.setSpan(new ClickableSpan() {

                                                    @Override
                                                    public void updateDrawState(TextPaint ds) {
                                                        super.updateDrawState(ds);
                                                        ds.setColor(0xff2097D9);
                                                        ds.setUnderlineText(false);
                                                    }

                                                    @Override
                                                    public void onClick(View widget) {
                                                        Toast.makeText(context, data, Toast.LENGTH_SHORT).show();
                                                    }
                                                }, startOld + temp.indexOf(data), startOld + temp.indexOf(data) + data.length(),
                                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

                        setMovementMethod(LinkMovementMethod.getInstance());
                        startNew = temp.indexOf(data) + data.length();
                        startOld += startNew;
                        temp = temp.substring(startNew);
                    }
                }
            }
        }

        setText(spannableString);

        if (keyBeanList != null) {

            try {
                firstKeyWordPosition = content.indexOf(keyBeanList.get(1).getContent());
            } catch (Exception e) {
                firstKeyWordPosition = -1;
            }

        }
        return firstKeyWordPosition;

    }


}

PicImageView

package com.chen.demo;

import android.content.Context;
import android.graphics.drawable.Animatable;
import android.util.AttributeSet;
import android.widget.ImageView;

public class PicImageView extends ImageView {

    public PicImageView(Context context) {
        this(context, null);
    }

    public PicImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public PicImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        init();
    }

    /**
     * 初始化设置
     */
    private void init() {

        //不可以使用这个
        //setBackgroundResource(R.drawable.refresh_loading);

        //需要使用这个
        setImageResource(R.drawable.pic_anim);
        ((Animatable) getDrawable()).start();
    }

}

帧动画:pic_anim
随便找3个图,每个图展示1秒。这个随意

<?xml version="1.0" encoding="utf-8"?>
<animation-list
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="true">
    <!--设置循环播放-->
    <!--android:oneshot="false"-->
    <!--true 表示单次播放-->
    <item
        android:drawable="@mipmap/ch_1"
        android:duration="1000"/>
    <item
        android:drawable="@mipmap/ch_2"
        android:duration="1000"/>
    <item
        android:drawable="@mipmap/ch_3"
        android:duration="1000"/>

</animation-list>

至此,通过上面的代码,准备工作基本做完了。下面,开始功能实现。

activity_main_1
有人会对辅助线的295px有疑问,下面会有说明

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <!--辅助线-->
    <View
        android:id="@+id/auxiliary_line"
        android:layout_width="1dp"
        android:layout_height="match_parent"
        android:layout_marginStart="295px"
        android:background="#ff0000"
        />

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true">

        <com.chen.demo.MyTextView
            android:id="@+id/my_tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:background="#55ff0000"
            android:padding="5dp"
            android:textSize="25sp"
            />

        <com.chen.demo.PicImageView
            android:id="@+id/pic_img"
            android:layout_width="20dp"
            android:layout_height="20dp"
            android:visibility="gone"/>

    </RelativeLayout>

</RelativeLayout>

MainActivity_1

package com.chen.demo;

import android.app.Activity;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.Layout;
import android.util.Log;
import android.view.View;
import android.widget.RelativeLayout;

import java.util.ArrayList;

public class MainActivity_1 extends Activity {

    private MyTextView my_tv;
    private PicImageView pic_img;

    private ArrayList<KeyBean> keyBeanList;

    //第二种方法需要,记录关键字位置
    int position = -1;

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {

            switch (msg.what) {
                case 100:

                    //第一种方法,通过textview的方法拿到需要关键字的位置
                    //int position = my_tv.getFirstKeyWordPosition();
                    Log.e("position", position + "");
                    Layout layout = my_tv.getLayout();
                    if (position != -1 && layout != null) {
                        int line = layout.getLineForOffset(position);

                        Log.e("所在行数", line + "");

                        Rect bound = new Rect();

                        //拿到关键字所在行的矩形区域
                        layout.getLineBounds(line, bound);

                        int left = bound.left;
                        int top = bound.top;
                        int right = bound.right;
                        int bottom = bound.bottom;
                        int width = bound.width();
                        int height = bound.height();

                        Log.e("left", left + "");
                        Log.e("top", top + "");
                        Log.e("right", right + "");
                        Log.e("bottom", bottom + "");
                        Log.e("width", width + "");
                        Log.e("height", height + "");


                        float primaryHorizontal = layout.getPrimaryHorizontal(position);//字符左边x坐标
                        float secondaryHorizontal = layout.getSecondaryHorizontal(position);

                        Log.e("primaryHorizontal", primaryHorizontal + "");
                        Log.e("secondaryHorizontal", secondaryHorizontal + "");

                        RelativeLayout.LayoutParams pic_img_lp = (RelativeLayout.LayoutParams) pic_img.getLayoutParams();
                        pic_img_lp.setMargins((int) primaryHorizontal, top, 0, 0);
                        pic_img.setLayoutParams(pic_img_lp);

                        pic_img.setVisibility(View.VISIBLE);
                        mHandler.sendEmptyMessageDelayed(200, 3000);

                    }

                    break;

                case 200:

                    pic_img.setVisibility(View.GONE);

                    break;
            }


        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_1);

        my_tv = findViewById(R.id.my_tv);
        pic_img = findViewById(R.id.pic_img);

        String s = "哈http://www.baidu.com哈123哈哈[\\微笑]哈哈ABC哈哈000001http://www.baidu.com哈[\\微笑]哈哈000001哈哈哈000001";

        keyBeanList = new ArrayList<>();

        KeyBean keyBean_1 = new KeyBean();
        keyBean_1.setContent("ABC");

        keyBeanList.add(keyBean_1);

        KeyBean keyBean_2 = new KeyBean();
        keyBean_2.setContent("000001");

        keyBeanList.add(keyBean_2);

        //第一种方法,展示内容
        //my_tv.showContent_1(s, keyBeanList);

        //第二种方法,展示内容后直接拿到需要关键字的位置的返回
        position = my_tv.showContent_2(s, keyBeanList);

        mHandler.sendEmptyMessageDelayed(100, 3000);

    }

    @Override
    protected void onDestroy() {
        if (mHandler != null && mHandler.hasMessages(100)) {
            mHandler.removeMessages(100);
        }
        super.onDestroy();

    }
}

说明:
1、在这种单独展示Textview,然后在指定关键字位置展示图片的情况,用MyTextView中的第一种或第二种方法,都没问题,可以任选一种
2、关于handler的使用。如果直接上来就getLayout(),因为视图还没加载完(系统还没准备好),会导致getLayout()得到空。也可以用视图树监听,加载完了再获取,或者用handler延迟获取,这个3秒是我随便写的,不一定是3秒。
3、之所以3秒后再发一个handler,是因为每张图片展示1秒,3张就是3秒
4、日志
在Activity中,我放了2个关键字:ABC和000001,同时,在MyTextView中,我取的是

if (keyBeanList != null) {

            try {
                firstKeyWordPosition = content.indexOf(keyBeanList.get(1).getContent());
            } catch (Exception e) {
                firstKeyWordPosition = -1;
            }

        }

即:第一个000001出现的位置。在Activity中,打印出了日志:

com.chen.demo E/position: 24
com.chen.demo E/所在行数: 1
com.chen.demo E/left: 0
com.chen.demo E/top: 98
com.chen.demo E/right: 1050
com.chen.demo E/bottom: 191
com.chen.demo E/width: 1050
com.chen.demo E/height: 93
com.chen.demo E/primaryHorizontal: 295.0
com.chen.demo E/secondaryHorizontal: 295.0

我为了看295对应的位置,才定义了辅助线

有细心的朋友应该发现了,从0开始计数,如果按字数数一遍,回形针图片长度算1,第一个微笑算1,会数出来,000001出现的位置是20,不是24,这是因为,你把表情算成了1,但是系统数的时候,微笑是[/微笑],这个长度是5,你少数了4,。20+4=24

附 getPrimaryHorizontal和getSecondaryHorizontal说明:

/**
* Get the primary(主要的;初级的;基本的) horizontal水平 position for the specified特定的 text offset偏移、位移.
* This is the location位置 where a new character特性 would be inserted插入、附着 in
* the paragraph's(paragraph:段落) primary direction方向.
*/
public float getPrimaryHorizontal(int offset) {
    return getPrimaryHorizontal(offset, false /* not clamped */);
}   


/**
* Get the secondary中等的、次要的 horizontal position for the specified text offset.
* This is the location where a new character would be inserted in
* the direction other than the paragraph's primary direction.
*/
public float getSecondaryHorizontal(int offset) {
    return getSecondaryHorizontal(offset, false /* not clamped */);
}

以上,就是单独展示的情况,位置不准的话,可以在setMargins中自己调整。

考虑到有人会有疑问,在ListView中可以这样用吗?下面,我说一下ListView中的使用情况
(不建议在ListView中使用,如果不用,下面的内容忽略)
提前说明:
1、在ListView中使用的话,建议用MyTextView中的第二种方法,即:加载完内容后直接返回关键字位置。如果是加载完内容,再通过其他方法获取,容易出问题,有些手机甚至会出现“停止运行”的问题
2、会出现复用问题。即:有的不显示,有的显示过了,但是还会显示。
3、源码如下,有兴趣的朋友可以自己看看,我这里就不多说了
效果图:
这里写图片描述

说明:
每次加载10条。点击重新加载,会清空之前的所有数据,重新生成10条展示;点击加载更过,会生成10条新的,加再现有数据后面。

activity_main_2

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/reLoad_tv"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="#5500ff00"
            android:gravity="center"
            android:padding="5dp"
            android:text="重新加载数据"
            android:textSize="20sp"/>

        <TextView
            android:id="@+id/load_more_tv"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="#550000ff"
            android:gravity="center"
            android:padding="5dp"
            android:text="加载更多数据"
            android:textSize="20sp"/>

    </LinearLayout>

    <ListView
        android:id="@+id/listview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>


</LinearLayout>

MainActivity_2

package com.chen.demo;

import android.app.Activity;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.Layout;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;

import java.util.ArrayList;

public class MainActivity_2 extends Activity implements View.OnClickListener {

    private ListView listview;

    private ArrayList<DataBean> dataList;
    private ArrayList<KeyBean> keyBeanList;

    private MyAdapter myAdapter;

    //重新加载数据
    private TextView reLoad_tv;
    //加载更多数据
    private TextView load_more_tv;

    private int baseCount = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_2);

        reLoad_tv = findViewById(R.id.reLoad_tv);
        load_more_tv = findViewById(R.id.load_more_tv);
        reLoad_tv.setOnClickListener(this);
        load_more_tv.setOnClickListener(this);

        listview = findViewById(R.id.listview);

        myAdapter = new MyAdapter();

        dataList = new ArrayList<>();
        keyBeanList = new ArrayList<>();

        KeyBean keyBean_1 = new KeyBean();
        keyBean_1.setContent("ABC");

        keyBeanList.add(keyBean_1);

        KeyBean keyBean_2 = new KeyBean();
        keyBean_2.setContent("000001");

        keyBeanList.add(keyBean_2);

        listview.setAdapter(myAdapter);

        baseCount = 0;
        setShowData();

    }

    private void setShowData() {
        if (baseCount == 0) {
            dataList.clear();
        }
        String baseStr = "哈http://www.baidu.com哈123哈哈[\\微笑]哈哈ABC哈哈000001http://www.baidu.com哈[\\微笑]哈哈000001哈哈哈000001";
        DataBean dataBean;
        for (int i = 10 * baseCount; i < 10 * (baseCount + 1); i++) {
            dataBean = new DataBean();
            dataBean.setContent("=" + i + "=" + baseStr);
            dataBean.setType("0");
            dataList.add(dataBean);
        }
        myAdapter.notifyDataSetChanged();
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.reLoad_tv:
                baseCount = 0;
                setShowData();
                break;
            case R.id.load_more_tv:
                baseCount++;
                setShowData();
                break;
        }
    }


    private class MyAdapter extends BaseAdapter {

        private View auxiliary_line;
        private MyTextView my_tv;
        private PicImageView pic_img;

        private DataBean bean;

        int keyposition=-1;

        private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {

                switch (msg.what) {
                    case 100:

                        if (TextUtils.equals("0", bean.getType())) {
                            //没有展示过的,才展示

//                            int keyposition = my_tv.getFirstKeyWordPosition();
                            Layout layout = my_tv.getLayout();
                            if (keyposition != -1 && layout != null) {
                                int line = layout.getLineForOffset(keyposition);

                                Log.e("所在行数", line + "");

                                Rect bound = new Rect();

                                layout.getLineBounds(line, bound);

                                int left = bound.left;
                                int top = bound.top;
                                int right = bound.right;
                                int bottom = bound.bottom;
                                int width = bound.width();
                                int height = bound.height();

                                float primaryHorizontal = layout.getPrimaryHorizontal(keyposition);
                                float secondaryHorizontal = layout.getSecondaryHorizontal(keyposition);

                                RelativeLayout.LayoutParams pic_img_lp = (RelativeLayout.LayoutParams) pic_img.getLayoutParams();
                                pic_img_lp.setMargins((int) primaryHorizontal, top, 0, 0);
                                pic_img.setLayoutParams(pic_img_lp);

                                pic_img.setVisibility(View.VISIBLE);

                            }

                            mHandler.sendEmptyMessageDelayed(200, 3000);

                        }


                        break;

                    case 200:
                        if (pic_img != null) {
                            pic_img.setVisibility(View.GONE);
                        }
                        bean.setType("1");
                        break;

                }


            }
        };

        @Override
        public int getCount() {
            return dataList.size();
        }

        @Override
        public Object getItem(int i) {
            return null;
        }

        @Override
        public long getItemId(int i) {
            return 0;
        }

        @Override
        public View getView(int i, View view, ViewGroup viewGroup) {

            bean = dataList.get(i);

            if (view == null) {
                view = LayoutInflater.from(MainActivity_2.this).inflate(R.layout.activity_main_1, null);
            }

            auxiliary_line = ViewHolder.get(view, R.id.auxiliary_line);
            my_tv = ViewHolder.get(view, R.id.my_tv);
            pic_img = ViewHolder.get(view, R.id.pic_img);

            //这里就不用辅助线了
            auxiliary_line.setVisibility(View.GONE);
            pic_img.setVisibility(View.GONE);
//            my_tv.showContent_1(bean.getContent(), keyBeanList);

            keyposition= my_tv.showContent_2(bean.getContent(), keyBeanList);

            if (TextUtils.equals("0", bean.getType())) {
                mHandler.sendEmptyMessageDelayed(100, 3000);
            }

            return view;
        }

    }

}

最后,说一下文字的测量:
源码及日志如下,我不多做解释了,想深入的朋友,请看一位大神的博客:

http://blog.csdn.net/aigestudio/article/details/41447349

activity_main_3

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <TextView
        android:id="@+id/textview"
        android:layout_width="300px"
        android:layout_height="200px"
        android:layout_centerInParent="true"
        android:background="#55ff0000"
        android:textSize="50px"
        />

</RelativeLayout>

MainActivity_3

package com.chen.demo;

import android.app.Activity;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextPaint;
import android.util.Log;
import android.widget.TextView;

public class MainActivity_3 extends Activity {

    private TextView textview;

    String s = "";

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {

            switch (msg.what) {
                case 100:

                    TextPaint textPaint = textview.getPaint();
                    Rect mRect = new Rect();

                    textPaint.getTextBounds(s, 0, s.length(), mRect);

                    int text_rect_left = mRect.left;
                    int text_rect_top = mRect.top;
                    int text_rect_right = mRect.right;
                    int text_rect_bottom = mRect.bottom;

                    int text_rect_width = mRect.width();
                    int text_rect_height = mRect.height();

                    float textWidth = textPaint.measureText(s);

                    Log.e("text_rect_left", text_rect_left + "");
                    Log.e("text_rect_top", text_rect_top + "");
                    Log.e("text_rect_right", text_rect_right + "");
                    Log.e("text_rect_bottom", text_rect_bottom + "");

                    Log.e("text_rect_width", text_rect_width + "");
                    Log.e("text_rect_height", text_rect_height + "");

                    Log.e("textWidth", textWidth + "");

                    Log.e("right-left", text_rect_right - text_rect_left + "");
                    Log.e("bottom-top", text_rect_bottom - text_rect_top + "");


                    Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
                    Log.e("ascent", fontMetrics.ascent + "");
                    Log.e("top", fontMetrics.top + "");
                    Log.e("leading", fontMetrics.leading + "");
                    Log.e("descent", fontMetrics.descent + "");
                    Log.e("bottom", fontMetrics.bottom + "");

                    Log.e("bottom-top", fontMetrics.bottom-fontMetrics.top + "");
                    Log.e("descent-ascent", fontMetrics.descent-fontMetrics.ascent + "");

                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_3);

        textview = findViewById(R.id.textview);

        s = "123";

        textview.setText(s);

        mHandler.sendEmptyMessageDelayed(100, 3000);

    }

}

日志:

com.chen.demo E/text_rect_left: 4
com.chen.demo E/text_rect_top: -37
com.chen.demo E/text_rect_right: 81
com.chen.demo E/text_rect_bottom: 1

com.chen.demo E/text_rect_width: 77
com.chen.demo E/text_rect_height: 38

com.chen.demo E/textWidth: 84.0

com.chen.demo E/right-left: 77
com.chen.demo E/bottom-top: 38

com.chen.demo E/ascent: -46.38672
com.chen.demo E/top: -52.807617
com.chen.demo E/leading: 0.0
com.chen.demo E/descent: 12.207031
com.chen.demo E/bottom: 13.549805

com.chen.demo E/bottom-top: 66.35742
com.chen.demo E/descent-ascent: 58.59375

猜你喜欢

转载自blog.csdn.net/u014620028/article/details/78247920