项目地址:https://github.com/razerdp/FriendCircle
因为最近忙于弄毕业设计,所以仿朋友圈这个项目暂时听了几天,今天花了一点时间把上一次的坑补充。
ps:因为是在太忙,服务器后台交互还没开始写,所以评论暂时来说是未能实现的哦,现在仅仅是UI上的操作
按照惯例,首先上图(图片大小:7M,为了大小问题,色彩调的比较低,所以看起来略渣):
preview.gif
本篇实现的原理跟上篇是一致的,不过也许上一篇写的有点乱,比较难理解,所以本篇将会一边概括上篇的内容一边讲述如何实现本篇的内容。
Step 1:概括
在上一篇中,写了那么多东东,其实核心思想只有一个:计算偏移量。
listview.smoothscrolltopositionfromtop这个方法执行起来其实是分两部的:
- 到达指定item,使item的顶部对齐listview的顶部
- 到达后,smoothScroll一定距离,也就是第二个参数
而我们的目标是为了让item的底部对齐输入框的顶部,而smoothscrolltopositionfromtop第一步已经帮我们定位好,使item的顶部对齐listview的顶部,所以我们着手的问题只是如何计算出偏移量。
又因为输入法弹出后,实际上我们看见的内容区域是变小的,所以我们需要计算的是item的底部与输入框顶部的差值,而这个差值就是偏移量了。
同时因为getBottom得到的是相对于父类而言的参数,所以我们只好通过item的height来动态计算(因为smoothscrolltopositionfromtop第一步会使item顶部对齐listview顶部,所以item的底部就相当于它的高度)
以上就是上一篇的概括。
总结起来就是:
- listview定位到指定的item
- 得到listview的height,此时的height相当于bottom
- 得到height与可见部分的差值,这个差值就是偏移量
- listview偏移上述步骤得到的偏移量
Step 2:评论控件对齐输入框顶部的实现原理
通过上面的概括,其实不难得出,我们只要得到当前点击的评论的底部再计算与可见区域底部的差值就可以了。
实际上也正是如此,但是在实现的时候我们则需要稍微处理一下。
首先看看我们的动态的底部布局:
底部布局
可以看到,我们的底部布局是RelativeLayout嵌套着LinearLayout,这第一个LinearLayout是存放点赞和评论的。然后这个LinearLayout里面又嵌套着一个LinearLayout,这个LinearLayout则是村发那个评论的。
看起来层次嵌套挺多,过度绘制妥妥的了。
不过打开微信看了看,咱就安心了。
左:微信朋友圈;右:本项目
毕竟那啥,看起来过度绘制都差不多哈哈。
上面说过,我们最终目的都是得到当前点击的评论的底部距离可视区域的底部,同时由于我们当初的设计是将commentWidget抛了出来,所以理论上来说,我们拿到commentWidget.getBottom()就行了。
但其实不然,getTop/Bottom/Left/Right这几个方法得到的是相对于其父布局的参数,而非在屏幕上的,所以我们需要处理一下:
比如上图,我们需要拿到四楼相对于整个item的位置,普通的getBottom是不可行的,所以我们需要计算一下。
在布局分析那里,我们可以看到我们嵌套了好几层,在图中我也用不同的颜色标出来了范围。
回到问题,我们要拿到该评论控件相对于item的位置,则需要通过其父布局的top,而父布局又因为嵌套在其他父布局里面,所以我们需要得到最外层的布局的top。在图中表现为蓝色框框的顶部。
所以我们取得当前点击评论控件相对于item的位置方法如下:
- 取得评论控件父布局(记为parent1)的top(图中绿色布局),记为top1
- 取得包含着点赞和评论布局(记为parent2)的top(即parent1.getParent(),图中红色布局),记为top2
- 取得整个bottom相对于item的top,即(parent2.getParent()),记为top3
- 取得被点击的commentWidget的bottom(该bottom是相对于parent1的bottom)
- 最后commentWidget的bottom相对于item来说,其y方向的值等于top1+top2+top3+bottom.
然后通过这个值减去可见区域的底部就得到了偏移值了
Step 3:代码实现
代码上十分简单,本次改动仅仅只有计算偏移值的地方:
首先补充上一篇的calculateListViewOffset方法
private int calculateListViewOffset(int currentDynamicPos, CommentWidget commentWidget, int keyBoardHeight) { int result; if (screenHeight == 0) screenHeight = UIHelper.getScreenPixHeight(this); if (statusBarHeight == 0) statusBarHeight = UIHelper.getStatusHeight(this); if (commentWidget == null) { // 评论控件为空,证明回复的是整个动态 result = getOffsetOfDynamic(currentDynamicPos, keyBoardHeight); } else { // 评论控件不空,证明回复的是评论 result = getOffsetOfComment(currentDynamicPos, commentWidget, keyBoardHeight); } return result; }
然后完善我们的getOffsetOfComment方法:
// 得到评论的偏移量 private int getOffsetOfComment(int currentDynamicPos, CommentWidget commentWidget, int keyBoardHeight) { int result=0; int contentHeight; contentHeight = screenHeight - keyBoardHeight - mInputLayout.getHeight(); try { //得到评论控件所在父控件(即评论的父控件) final LinearLayout parent = (LinearLayout) commentWidget.getParent(); //得到评论和点赞的父控件 final LinearLayout praiseAndCommentParent = (LinearLayout) parent.getParent(); //得到评论和点赞的父控件的父控件 final RelativeLayout originParent = (RelativeLayout) praiseAndCommentParent.getParent(); //得到评论和点赞的父控件的父控件的top final int originParentTop = originParent.getTop(); //得到评论和点赞的父控件的top final int praiseAndCommentParentTop = praiseAndCommentParent.getTop(); //得到评论所在父控件的top final int parentTop = parent.getTop(); //得到当前评论控件的底部(相对于LinearLayout) final int currentCommentBottom = commentWidget.getBottom(); //则当前评论控件相对于item来说,其底部=父类LinearLayout.top+当前控件的底部 final int currentCommentOffset = originParentTop + praiseAndCommentParentTop + parentTop + currentCommentBottom; //则偏移量等于offset-contentheight result = currentCommentOffset - contentHeight; } catch (Exception e) { e.printStackTrace(); Log.e("FriendCircleDemoAct","也许是空指针也许是cast错误哦"); } return -result; }
相关代码都写了注释了,因为忽然发现,本项目的注释稍微少了点。
本篇就到此结束了。
到现在为止,我们的朋友圈实现了以下几个功能:
- 展示数据
- 点赞/取消点赞(包括点赞的弹窗、点赞时的动画)
- 评论(点击动态的评论对齐/点击评论控件对齐)
理论上,一个初始的朋友圈功能就差不多了。
接下来我们将会实现朋友圈的图片查看,当然,包括动画-V-。