Android布局之View.measure()动态量取高度并设置布局--(例:动态计算评论高度并显示)

 需求是这样的

在应用程序的详情介绍时,有评论的版块,该页评论最多显示5条,而每条最大字数是140个字符,每条评论可能根据字数不同,所占据的高度也不一样,如有的是1行,有的是2、3行,且评论可以翻页。

图片效果如下:

技术分享



如何解决这样的问题呢?

首先必须知道的是评论控件不要固定不变,而是需要动态计算并动态添加到显示面板中的。

下面通过实例来说一下。

1.定义布局

定义布局的时候,可以用AbsoluteLayout,因为高度是动态量取的,所以具体坐标是可以求得的。参考如下:

 <AbsoluteLayout
        android:layout_width="1386px"
        android:layout_height="wrap_content"
        android:layout_marginTop="60px"
        android:id="@+id/comment_content">
        
 </AbsoluteLayout>
该布局文件宽度设置不变,高度根据内容填充。只需将这个布局方在需要显示评论内容的地方。

2.显示数据


显示数据,需要根据数据来计算高度,首先把数据设置到控件中,然后通过View.measure(int widthMeasureSpec, int heightMeasureSpec)量取高度,并为该View设置坐标。参考代码:

<span style="font-size:14px;">/**
	 * 初始化、动态计算高度
	 */
	public void initCommentView() {
		record_temp = new CommentRecord();
		mPositionRecord = new ArrayList<Integer>();
		mHeightRecord = new ArrayList<Integer>();
		int currentHeight = 0;

		int maxNum = cachedComments.size();
		int sum = 0;
		for (int i = comment_begin_index; i < maxNum; i++) {
			if (null != mCommentCache && !mCommentCache.empty()) {
				comment_temp = mCommentCache.pop();
			} else {
				comment_temp = new CommentSimpleView(mContext);
			}
			mCommentUI.add(comment_temp);

			comment_temp.setData(cachedComments.get(i));
			comment_temp.measure(width, height);
			if (MAX_COMMENT_HEIGHT > currentHeight) {
				comment_content.addView(
						comment_temp,
						new AbsoluteLayout.LayoutParams(1386, comment_temp
								.getMeasuredHeight(), 0, FIRST_COMMENT_INTERVAL
								+ currentHeight));
				mPositionRecord.add(FIRST_COMMENT_INTERVAL + currentHeight);
				mHeightRecord.add(comment_temp.getMeasuredHeight());
				currentHeight = currentHeight
						+ comment_temp.getMeasuredHeight();
				comment_end_index++;
				sum++;
				if (sum < 5) {
					comment_temp.show_Divider();
				} else if (sum == 5) {
					comment_temp.hide_Divider();
				}
			}
			if (MAX_COMMENT_HEIGHT < currentHeight) {
				compareHeight = 1;
				isEnd = false;
				RelativeLayout.LayoutParams rl = (LayoutParams) comment_content
						.getLayoutParams();
				rl.setMargins(0, 60, 0, 0);
				rl.width = 1386;
				rl.height = MAX_COMMENT_HEIGHT+20;
				comment_content.setLayoutParams(rl);
				break;
			}
			if (MAX_COMMENT_HEIGHT == currentHeight) {
				compareHeight = 0;
				if (maxNum == comment_end_index) {
					isEnd = true;
					comment_pagedown.setFocusStatus(false);
				} else {
					isEnd = false;
				}
			}

		}
		record_temp.setHeightRecord(mHeightRecord);
		record_temp.setPositionRecord(mPositionRecord);
		record_temp.setBegin_index(comment_begin_index);
		record_temp.setEnd_index(comment_end_index);
		if (MAX_COMMENT_HEIGHT > currentHeight) {
			isEnd = true;
			comment_pagedown.setFocusStatus(false);
		}

	}</span>
其中全局的宽、高定义如下:

int width = MeasureSpec.makeMeasureSpec(1386, MeasureSpec.EXACTLY);
int height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);

3.翻页动态计算实现

 

/**
	 * 点击下一页时,判断是否有空间剩余,若有剩余则用上一页数据补充,false:无剩余
	 * @return
	 */
	private boolean hasExtraSpace() {
		if(null != cachedComments && cachedComments.size()-comment_end_index >=5){ //剩下的评论大于5条
			return false;
		}
		int beginIndex = 0;
		if(1 == compareHeight){
			beginIndex = comment_end_index;
		}else if(0 == compareHeight){
			beginIndex = comment_end_index+1;
		}
		int maxSize = cachedComments.size();
		int HeightSum = 0;
		for(int i = beginIndex;i<maxSize;i++){
			comment_temp = new CommentSimpleView(mContext);
			comment_temp.setData(cachedComments.get(i));
			comment_temp.measure(width, height);
			HeightSum += comment_temp.getMeasuredHeight();
			if(MAX_COMMENT_HEIGHT <= HeightSum){
				return false;
			}
			
		}
		lastPageHeight = HeightSum;
		return true;
	}
	/**
	 * 最后一页不满一屏,为倒数第一页,从最后一条倒序排列,
	 */
	public void showLastPage(){
		int lastCommentNum = cachedComments.size() - comment_end_index;
		int copy_last_index = comment_end_index;
		while(128 <= MAX_COMMENT_HEIGHT - lastPageHeight){
			lastCommentNum ++;
			comment_temp = new CommentSimpleView(mContext);
			comment_temp.setData(cachedComments.get(--copy_last_index));
			comment_temp.measure(width, height);
			lastPageHeight+=comment_temp.getMeasuredHeight();
		}
		if(MAX_COMMENT_HEIGHT < lastPageHeight){
			lastCommentNum -- ;
			lastPageHeight -= comment_temp.getMeasuredHeight();
			copy_last_index ++;
		}
		int sum = cachedComments.size();
		int current_H = FIRST_COMMENT_INTERVAL + MAX_COMMENT_HEIGHT - lastPageHeight;
		for(int i= copy_last_index;i<sum;i++){
			
			if(null != mCommentCache && !mCommentCache.empty()){
				comment_temp = mCommentCache.pop();
			}else{
				comment_temp = new CommentSimpleView(mContext);
			}	
			mCommentUI.add(comment_temp);
			comment_temp.setData(cachedComments.get(i));
			comment_temp.measure(width, height);
			comment_content.addView(comment_temp, new AbsoluteLayout.LayoutParams(1386, comment_temp.getMeasuredHeight(), 0, current_H));
			current_H = current_H + comment_temp.getMeasuredHeight();
			if(i == sum -1){
				comment_temp.hide_Divider();
			}else{
				comment_temp.show_Divider();
			}
		}
		isEnd = true;
		comment_pagedown.setFocusStatus(false);
	}
	/**
	 * 点击上一页,出栈,恢复数据并显示
	 */
	public void showPageUp(){
		if(mCommentRecord.empty()){
			Toast.makeText(mContext, "已是第一页", Toast.LENGTH_SHORT).show();
			return;
		}
		record_temp = mCommentRecord.pop();
		int begin = record_temp.getBegin_index();
		int end = record_temp.getEnd_index();
		List<Integer> position = record_temp.getPositionRecord();
		List<Integer> height = record_temp.getHeightRecord();
		if(null == position || null == height){
			return;
		}
		int m = 0;
		for(int i= begin;i<=end;i++){
			if(null != mCommentCache && !mCommentCache.empty()){
				comment_temp = mCommentCache.pop();
			}else{
				comment_temp = new CommentSimpleView(mContext);
			}	
			mCommentUI.add(comment_temp);
			comment_temp.setData(cachedComments.get(i));
			comment_content.addView(comment_temp, new AbsoluteLayout.LayoutParams(1386,height.get(m), 0, position.get(m)));
			m++;
			if(5 == m){
				comment_temp.hide_Divider();
			}else{
				comment_temp.show_Divider();
			}
		}
		isEnd = false;
		comment_begin_index = begin;
		comment_end_index = end;
	}

4.点击上一页、下一页事件处理

 

public class comment_pageup_click implements OkButtonViewClick {

		@Override
		public void onOkButtonViewClick() {
			if(1 == currentPage){
//				Toast.makeText(mContext, "已是第一页", Toast.LENGTH_SHORT).show();
				comment_pageup.setFocusStatus(false);
				return;
			}else{
				currentPage = currentPage - 1;
				comment_content.removeAllViews();
				while (!mCommentUI.isEmpty()) {
					mCommentCache.push(mCommentUI.remove(0));
				}
				showPageUp();
				comment_pagedown.setFocusStatus(true);
			}
		
		}
	}

	class comment_pagedown_click implements OkButtonViewClick {

		@Override
		public void onOkButtonViewClick() {
			if(isEnd){
//				Toast.makeText(mContext, "已是最后一页", Toast.LENGTH_SHORT).show();
				comment_pagedown.setFocusStatus(false);
				return;
			}
			else if(!isEnd){ //不到最后一页
				if(!hasExtraSpace()){ //下一页无剩余空间,即该页不是倒数第二页
					mCommentRecord.push(record_temp);
					currentPage = currentPage + 1;
					if(1 == compareHeight){
						comment_begin_index = comment_end_index;
						comment_end_index --;
					}else{
						comment_begin_index = comment_end_index+1;
					}
					if(currentPage%4 ==0){ //预加载数据
						ParserHelper.getParserHelper().requestComment("1", "30", "9", callback);
					}
					comment_content.removeAllViews();
					while (!mCommentUI.isEmpty()) {
						mCommentCache.push(mCommentUI.remove(0));
					}
					initCommentView();
				}else {    //下一页有剩余空间,即该页为倒数第二页
					mCommentRecord.push(record_temp);
					currentPage = currentPage + 1;
					comment_content.removeAllViews();
					while (!mCommentUI.isEmpty()) {
						mCommentCache.push(mCommentUI.remove(0));
					}
					showLastPage();
				}
				comment_pageup.setFocusStatus(true);
			}
		}
	}

5.下一页点击时保存状态,用作恢复。(用栈保存,入栈出栈


package com.helios.module.commentData;

import java.util.List;

public class CommentRecord {
		int begin_index;
		int end_index;
		List <Integer> positionRecord;
		List <Integer> heightRecord;
		
		public CommentRecord() {
			super();
		}
		public int getBegin_index() {
			return begin_index;
		}
		public void setBegin_index(int begin_index) {
			this.begin_index = begin_index;
		}
		public int getEnd_index() {
			return end_index;
		}
		public void setEnd_index(int end_index) {
			this.end_index = end_index;
		}
		public List<Integer> getPositionRecord() {
			return positionRecord;
		}
		public void setPositionRecord(List<Integer> positionRecord) {
			this.positionRecord = positionRecord;
		}
		public List<Integer> getHeightRecord() {
			return heightRecord;
		}
		public void setHeightRecord(List<Integer> heightRecord) {
			this.heightRecord = heightRecord;
		}		   
}


6.总结语

 动态计算,动态设置布局还是挺常用的。
其中的关键就是,measure()方法的使用,设置好数据就可以measure(),量好高度后,再设置到显示面板中,即调用:
ViewGroup.addView(View child, LayoutParams params)
第一次写动态布局,写的不好,希望大家勿喷,有疑问欢迎留言讨论,大家互相学习,慢慢进步。技术分享

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。