您的位置:首页 > 其它

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

2017-10-16 11:28 615 查看
本文主要讲在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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: