评论在很多app中都会用到,最常见的就是网易新闻中的评论了,所以今天就研究了一下这个,先看看网易的效果图吧。
这么一个评论的列表,想必都会做吧。一个ListView之类的控件,里面的item也是一个较简单的布局。不过当有人回复评论之后,那么效果图就变成了这样:
当有人回复评论之后,就出现了这样的楼层效果,今天主要就做做这个楼层的效果。
界面分析
通过效果图很容易看出,其实每一层楼的布局都是一样的,我们只需要把每一层每一层的布局拼接起来就好了。如果我们知道有多少个楼层,那么可以直接写一个LinearLayout,来包含多个楼层的布局就好了。但是在一开始不知道有多少楼层的情况下,我们只需要自定义一下LinearLayout,然后动态的添加楼层布局就好了。
代码编写
通过刚才的分析可以知道,每一个楼层里的布局是差不多的。所以,我们可以先写楼层的布局。楼层布局分为两种,一种是直接显示出了评论内容的,我这里的楼层只包含了人名,内容,楼层数,我们暂时叫它为ShowFloor;还有一种就是”展开隐藏楼层”,我们叫它为HideFloor。这两种布局都比较简单,所以就不贴出来了。
每一个楼层的布局写出来了,就可以开始自定义LinearLayout了,首先,我们创建一个FloorView类,让他继承LinearLayout,写出它的三个构造函数,然后初始化
this.setOrientation(LinearLayout.VERTICAL);
将它设置为垂直布局,然后就可以对数据进行显示布局了。
public void init() {
// 如果没有数据直接返回
if (null == datas.iterator())
return;
// 如果评论回复数小于7个,则直接显示完
if (datas.getFloorNum() < 7) {
for (Iterator<? extends Commentable> iterator = datas.iterator(); iterator
.hasNext(); ) {
View view = factory.buildSubFloor(iterator.next(), this);
addView(view);
}
} else {
// 如果评论回复数很多,则显示一个“显示隐藏楼层”的按钮
View view;
view = factory.buildSubFloor(datas.get(0), this);
addView(view);
view = factory.buildSubFloor(datas.get(1), this);
addView(view);
// 可以点击显示更多的楼层
view = factory.buildSubHideFloor(datas.get(2), this);
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
TextView hide_text = (TextView) v
.findViewById(R.id.hide_text);
hide_text.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0,
0);
v.findViewById(R.id.hide_pb).setVisibility(View.VISIBLE);
removeAllViews();
for (Iterator<? extends Commentable> iterator = datas.iterator(); iterator
.hasNext(); ) {
View view = factory.buildSubFloor(iterator.next(),
FloorView.this);
addView(view);
}
reLayoutChildren();
}
});
addView(view);
view = factory.buildSubFloor(datas.get(datas.size() - 1), this);
addView(view);
}
reLayoutChildren();
}
init()这个方法就是排列出评论楼层的,datas是对评论的再次封装,里面包含了所有的回复内容,以及有多少个回复楼层。我们先对评论里面有多少个楼层进行判断,如果小于9个,那么就显示出全部的楼层,如果大于9个,那么就显示出前两个楼层和最后一个楼层,中间用”显示隐藏楼层”布局来替代。
其中的:
factory.buildSubFloor(datas, this);
是生成ShowFloor用的,和平常的LayoutInflater.inflate生成View没有什么两样。
factory.buildSubHideFloor(datas, this)
是生成HideFloor用的。
这样思路就很清晰了,要直接显示所有楼层的时候,就循环遍历有多少个楼层,然后给每一个楼层(ShowFloor)设置数据,并add到FloorView中。如果楼层太多,就显示部分楼层,中间的用HideFloor替代,再加入到FloorView中,然后给HideFloor设置一个点击监听,点击后先移除FloorView中的所有子View,然后显示所有的楼层
public void reLayoutChildren() {
int count = getChildCount();
for (int i = 0; i < count; i++) {
View view = getChildAt(i);
LayoutParams layout = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT);
layout.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
int margin = Math.min((count - i - 1), 4) * density;
layout.leftMargin = margin;
layout.rightMargin = margin;
if (i == count - 1) {
layout.topMargin = 0;
} else {
layout.topMargin = Math.min((count - i), 4) * density;
}
view.setLayoutParams(layout);
}
}
reLayoutChildren()主要是对每一个楼层的位置进行一点小的改变,这样就可以呈现出楼层的效果了,不然就是竖着的一排。
@Override
protected void dispatchDraw(Canvas canvas) {
int i = getChildCount();
if (null != drawer && i > 0) {
for (int j = i - 1; j >= 0; j--) {
View view = getChildAt(j);
// drawable将在被绘制在canvas的哪个矩形区域内。
drawer.setBounds(view.getLeft(), view.getLeft(),
view.getRight(), view.getBottom());
drawer.draw(canvas);
}
}
super.dispatchDraw(canvas);
}
这个方法主要是给每一个楼层添加一个背景,就像一个框那样把楼层框起来。
最后再调用一下onLayout()和onMeasure()方法,对布局进行一下排版