前言
很多时候因业务需要,必须在ScrollView
中嵌套ListView
、RecyclerView
的item中包含ListView
、ListView
的item中包含高度不定的TextView
。那么这时候就需要动态的计算ListView
或TextView
的高度。
如下图:RecyclerView的item中包含ListView,ListView的item又有换行的TextView

具体实现代码如下:
一、动态计算ListView高度
public static int setListViewHeightBasedOnChildren(ListView listView) {
// 获取ListView对应的Adapter
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
return 0;
}
int totalHeight = 0;
for (int i = 0, len = listAdapter.getCount(); i < len; i++) {
// listAdapter.getCount()返回数据项的数目
View listItem = listAdapter.getView(i, null, listView);
// 计算子项View 的宽高
listItem.measure(0, 0);
// 统计所有子项的总高度
totalHeight += listItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
// listView.getDividerHeight()获取子项间分隔符占用的高度
// params.height最后得到整个ListView完整显示需要的高度
listView.setLayoutParams(params);
return params.height;
}
二、动态计算换行TextView高度
- 创建MTextView类继承
TextView
- Layout布局中添加MTextView
layout_width:wrap_content/未指定宽度(MeasureSpec.UNSPECIFIED),则用屏幕宽度计算,否则就使用View自身宽度计算,并且无需计算Parent的Padding
public class MTextView extends TextView {
private Context context;
public MTextView(Context context) {
super(context);
this.context = context;
}
public MTextView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
}
public MTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int mode = MeasureSpec.getMode(widthMeasureSpec);
Layout layout = getLayout();
if (layout != null) {
int height = (int) Math.ceil(getMaxLineHeight(ToDBC(this.getText().toString()), mode))
+ getCompoundPaddingTop() + getCompoundPaddingBottom();
int width = getMeasuredWidth();
setMeasuredDimension(DisplayUtil.px2px(context, width), DisplayUtil.px2px(context, height));
}
}
private float getMaxLineHeight(String str, int mode) {
float height = 0.0f;
float width = getMeasuredWidth();
float widthPixels = context.getResources().getDisplayMetrics().widthPixels;
//这里具体this.getPaint()要注意使用,要看你的TextView在什么位置,
// 这个是拿TextView父控件的Padding的,为了更准确的算出换行
float pLeft;
float pRight;
try {
pLeft = ((LinearLayout) getParent()).getPaddingLeft();
pRight = ((LinearLayout) getParent()).getPaddingRight();
} catch (Exception e) {
pLeft = 0;
pRight = 0;
}
//检测字符串中是否包含换行符,获得换行的次数,在之后计算高度时加上
int br = 0;
if (str.contains("\n"))
br = str.split("\n").length - 1;
/**
* wrap_content/未指定宽度(MeasureSpec.UNSPECIFIED),则用屏幕宽度计算
* 否则就使用View自身宽度计算,并且无需计算Parent的Padding
*/
int line;
if (mode == MeasureSpec.UNSPECIFIED) {
line = (int) Math.ceil((this.getPaint().measureText(str) /
(widthPixels - getPaddingLeft() - pLeft - pRight - getPaddingRight())));
} else {
line = (int)
Math.ceil((this.getPaint().measureText(str) /
(width - getPaddingLeft() - getPaddingRight())));
}
height = (this.getPaint().getFontMetrics().descent -
this.getPaint().getFontMetrics().ascent) * (line + br);
return height;
}
public static String ToDBC(String input) {
char c[] = input.toCharArray();
for (int i = 0; i < c.length; i++) {
if (c[i] == '\u3000') { //空格
c[i] = ' ';
} else if (c[i] > '\uFF00' && c[i] < '\uFF5F') {//半角与全角相差 65248
c[i] = (char) (c[i] - 65248);
}
}
return new String(c);
}
}
<com.lin.widget.MTextView
android:layout_width="276dp"
android:layout_height="wrap_content"
android:text="TextView" />
网友评论