一起撸个朋友圈吧(step5) 控件篇【控件组装评论控件】
项目地址:https://github.com/razerdp/FriendCircle
上篇链接:http://www.jianshu.com/p/a2cdf81359fc
下篇链接:http://www.jianshu.com/p/ff9788581fb0
如您所见,在公司项目app提测后,在下终于闲下来继续去撸app了。花了一天时间,抱着服务器大哥的大腿狂问,终于初步弄出了一个服务器出来。
之前欠下的评论控件也得以展示了。
ps:在下非常欢迎PR,如果您有更好的想法,可以PR到dev分支哦-V-
预览图如下:(内容页还没开始,所以目前只有共有控件的拼装)
嗯。。。因为部署在本机,所以网速比较快←_←,而且目前在下只弄了4条数据,同时内容区(可变部分)还没开干,所以画面看起来怪怪的。
先不管那么多
本篇主要讲解评论控件的实现:
评论控件采取的依然是继承TextView,做法跟点赞列表控件差不多,但在ListView里面,我们的评论区实现方案大概有两种:
- ListView的item嵌套ListView?(不可取)
- ListView的item嵌套LinearLayout,动态添加我们的自定义控件。 很明显,我们采取方案二。
首先实现我们的控件,因为评论控件比较简单,所以我们就不需要attrs了。
/*** Created by 大灯泡 on 2016/2/23.* 评论控件*/
public class CommentWidget extends TextView {private static final String TAG = "CommentWidget";//用户名颜色private int textColor = 0xff517fae;private static final int textSize = 14;private int key;private SpannableStringBuilderAllVer mSpannableStringBuilderAllVer;...构造器public void setCommentText(CommentInfo info) {if (info == null) return;boolean hasContent = false;//根据hashCode判断内容是否一致if (key == 0) {key = info.hashCode();}else {hasContent = (key == info.hashCode());}if (!hasContent) {key = info.hashCode();setText("");setTag(info);createCommentStringBuilder(info);}else {try {setText(mSpannableStringBuilderAllVer);} catch (NullPointerException e) {e.printStackTrace();Log.e(TAG, "虽然在下觉得不可能会有这个情况,但还是捕捉下吧,万一被打脸呢。。。");}}}private void createCommentStringBuilder(@NonNull CommentInfo info) {String content = ": " + info.content + "\0";if (mSpannableStringBuilderAllVer == null) {mSpannableStringBuilderAllVer = new SpannableStringBuilderAllVer();boolean isApply = (info.userB == null);// 用户B为空,证明是一条原创评论if (info.userA != null && isApply) {CommentClick userA = new CommentClick.Builder(getContext(), info.userA).setTextSize(textSize).build();mSpannableStringBuilderAllVer.append(info.userA.nick, userA, 0);mSpannableStringBuilderAllVer.append(content);}else if (info.userA != null && !isApply) {//用户A,B不空,证明是回复评论CommentClick userA = new CommentClick.Builder(getContext(), info.userA).setTextSize(textSize).build();mSpannableStringBuilderAllVer.append(info.userA.nick, userA, 0);mSpannableStringBuilderAllVer.append("回复");CommentClick userB = new CommentClick.Builder(getContext(), info.userB).setTextSize(textSize).build();mSpannableStringBuilderAllVer.append(info.userB.nick, userB, 0);mSpannableStringBuilderAllVer.append(content);}}setText(mSpannableStringBuilderAllVer);}public CommentInfo getData() throws ClassCastException {return (CommentInfo) getTag();}
复制代码
代码不多,而且在下也写得比较清晰(命名二逼明了,咱们不故作深沉),我们主要观察createCommentStringBuilder()方法,这个方法我们主要判断userB,也就是被回复的用户是否为空,如果是空,则证明这是一条原创评论,也就是针对朋友的评论,不空则是回复别人。
然后CommentClick跟我们的点赞控件一样的ClickableSpan实现,这个没啥好说的。
另外需要注意的是记得在回复的内容后加上'\0',详情见点赞列表那篇。
评论控件大概就这样,但本篇文章重头戏在于控件的组装
还记得我们的一起撸个朋友圈吧(step3) - ListAdapter篇吗,我们的BaseItemDelegate留下了公共部分的初始化,这次我们就顺便的弄回。
我们公共的部分有如下几个(公共部分即无论哪种类型的朋友圈,都会存在的控件):
- 头像/昵称/用户心情文字,组成item_header
- 发布时间/评论按钮/点赞列表/评论区,组成item_bottom
这几个是无论如何都会存在的,所以我们的布局文件可以单独抽出来复用(xml就不贴了,又长又无聊):
值得注意的是,item_bootm里面有个地方嵌套布局比较多,原因如下: 我们的点赞&评论控件处于同一个LinearLayout里面,因为这两者之间存在着一条分割线,所以采用LinearLayout,其次,我们的评论列表则是单独使用LinearLayout动态添加控件的,所以这里嵌套了两层,这无法避免。。。(当然,也可以将点赞和评论控件封装在同一个LinearLayout里面,但个人觉得没必要)
布局弄好后,我们完善我们的baseitem代码
public abstract class BaseItemDelegateimplements BaseItemView<MomentsInfo>, View.OnClickListener, View.OnLongClickListener {protected Activity context;//顶部protected SuperImageView avatar;protected TextView nick;protected ClickShowMoreLayout textField;//底部protected TextView createTime;protected ImageView commentImage;protected FrameLayout commentButton;protected LinearLayout commentAndPraiseLayout;protected PraiseWidget praiseWidget;protected View line;protected LinearLayout commentLayout;//中间内容层protected RelativeLayout contentLayout;复制代码
常量大概就是这些,内容层里面也许是gridview(图片),也许是viewGroup(网页分享),这个是不可变甚至可能没有的,所以我们这里仅负责调整间距,不负责其可视性,其余的控件都是共有的。
关于SuperImageView,这个东东是继承ImageView封装了Glide的方法,关于这个在我的毕业论文备忘录中的Day4 - ImageView封装Glide方法有记载,这里就不详述了
在我们的item里面,所有view的操作都是在onBindData进行的,我们父类进行初始化共有控件主要以下几个方法:
@Overridepublic void onBindData(int position, @NonNull View v, @NonNull MomentsInfo data, final int dynamicType) {mInfo = data;//初始化共用部分bindView(v);bindShareData(data);bindData(position, v, data, dynamicType);}
复制代码
- bindView(v),这里进行共有控件的findViewById,此处不展示
- bindShareData(data),这里进行共有控件的数据展示
- bindData(position, v, data, dynamicType),这个是抽象方法,交由子类实现,确保子类执行到这里的时候父类的共有控件初始完成。
本篇我们关注bindShareData(data)方法。
这个方法内容如下:
/** 共有数据绑定 */private void bindShareData(MomentsInfo data) {avatar.loadImageDefault(data.userInfo.avatar);nick.setText(data.userInfo.nick);textField.setText(data.textField);if (TextUtils.isEmpty(data.textField) && contentLayout != null) {LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) contentLayout.getLayoutParams();params.topMargin = -UIHelper.dipToPx(context, 8);contentLayout.setLayoutParams(params);}else {LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) contentLayout.getLayoutParams();params.topMargin = 0;contentLayout.setLayoutParams(params);}createTime.setText(TimeUtil.getTimeString(data.dynamicInfo.createTime));setCommentPraiseLayoutVisibility(data);//点赞praiseWidget.setDatas(data.praiseList);//评论addCommentWidget(data.commentList);}
复制代码
我们重点关注addCommentWidget,在前面说过,我们的评论列表使用LinearLayout进行addView。
但这会导致一个问题:由于我们是一个listview,而我们的baseitem本质上是一个viewholder,这也就意味着假如我们划出屏幕,再滑回来,就会出现在原有的view基础上又重复add了一次。
也许有人说,那我们每次removeAllViews后再add不就可以了么,这的确可行,但假如量一大,比如连续10条朋友圈都包含着20~50条评论,也就意味着滑出去再滑回来就需要new 50个commentwidget,这造成的就是视觉上的卡顿,体验十分不好。
而我的解决方法目前想到两个:
- 维持一个池,从池里拿出可用的进行复用(期望,暂未实现)
- 动态添加/减去差额,多出remove,少了则new(目前采用,实际上这个方法跟上面的池结合最为妥善)
我目前采用方法2,具体操作如下:
获取当前评论区控件数量,记为childCount
chidCount与bean的评论数(n)比较
childCount>n,则remove掉childCount-n个view(期望维护一个池,将remove掉的放到复用池)
childCount<n,则new出n-childCount个view
childCount=n,则进行步骤3
所有view进行数据绑定(数据更新)
这样做的好处就是减少了new对象的操作,起码滑起来顺畅好多。
具体代码如下:
private void addCommentWidget(List<CommentInfo> commentList) {if (commentList == null || commentList.size() == 0) return;/*** 优化方案:* 因为是在listview里面,那么复用肯定有,意味着滑动的时候必须要removeView或者addView* 但为了性能提高,不可以直接removeAllViews* 于是采取以下方案:* 根据现有的view进行remove/add差额* 然后统一设置* */final int childCount = commentLayout.getChildCount();if (childCount < commentList.size()) {//当前的view少于list的长度,则补充相差的viewint subCount = commentList.size() - childCount;for (int i = 0; i < subCount; i++) {CommentWidget commentWidget = new CommentWidget(context);LinearLayout.LayoutParams params=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);params.topMargin=1;params.bottomMargin=1;commentWidget.setLayoutParams(params);commentWidget.setLineSpacing(4,1);commentWidget.setOnClickListener(this);commentWidget.setOnLongClickListener(this);commentLayout.addView(commentWidget);}}else if (childCount > commentList.size()) {//当前的view的数目比list的长度大,则减去对应的viewcommentLayout.removeViews(commentList.size(), childCount - commentList.size());}//绑定数据for (int n = 0; n < commentList.size(); n++) {CommentWidget commentWidget = (CommentWidget) commentLayout.getChildAt(n);if (commentWidget != null) commentWidget.setCommentText(commentList.get(n));}}
复制代码
最后是评论控件和点赞列表控件的分割线判定与layout可视性判定:
/** 是否有点赞或者评论 */private void setCommentPraiseLayoutVisibility(MomentsInfo data) {if ((data.commentList == null || data.commentList.size() == 0) &&(data.praiseList == null || data.praiseList.size() == 0)) {//全空,取消显示commentAndPraiseLayout.setVisibility(View.GONE);}else {//某项不空,则展示layoutcommentAndPraiseLayout.setVisibility(View.VISIBLE);//点赞或者评论某个为空,分割线不展示if (data.commentList == null || data.commentList.size() == 0 ||data.praiseList == null || data.praiseList.size() == 0) {line.setVisibility(View.GONE);}else {line.setVisibility(View.VISIBLE);}//点赞为空,取消点赞控件的可见性if (data.praiseList == null || data.praiseList.size() == 0) {praiseWidget.setVisibility(View.GONE);}else {praiseWidget.setVisibility(View.VISIBLE);}//评论if (data.commentList == null || data.commentList.size() == 0) {commentLayout.setVisibility(View.GONE);}else {commentLayout.setVisibility(View.VISIBLE);}}}
复制代码
其余的相关代码,如bean实体,volley初始化等请看源码,这里就不写出来了。
下一篇将会进行内容页的定制以及默默地精神分裂构造朋友圈虚拟数据。
【附:】本篇JSON数据:
{
data: {
hostInfo: {
hostid: 1001,
hostAvatar: "http://upload.jianshu.io/users/upload_avatars/684042/bd1b2f796e3a.jpg",
hostNick: "羽翼君",
hostWallPic: "http://www.pp3.cn/uploads/allimg/111118/10562Cb5-13.jpg"
},
moments: [
{
userInfo: {
nick: "羽翼君",
avatar: "http://upload.jianshu.io/users/upload_avatars/684042/bd1b2f796e3a.jpg",
userId: 1001
},
dynamicInfo: {
dynamicId: 10001,
createUserId: 1001,
dynamicType: 10,
praiseState: 1,
createTime: 1456296202,
candelete: 1
},
textField: "这是第一条朋友圈哦",
praiseList: [
{
nick: "羽翼君",
avatar: "http://upload.jianshu.io/users/upload_avatars/684042/bd1b2f796e3a.jpg",
userId: 1001
},
{
nick: "涵菱",
avatar: "http://img7.3wmm.cc/pic/c/f/d/cfd2c2291ba75df42efedfe4bc62ee39.jpg",
userId: 1004
},
{
nick: "短发美比我在这i",
avatar: "http://img0w.pconline.com.cn/pconline/1310/29/3719457_13667094527.jpg",
userId: 1044
},
{
nick: "丑化小丑不丑。",
avatar: "http://img1.touxiang.cn/uploads/20141128/28-021805_451.jpg",
userId: 1054
}
],
commentList: [
{
userA: {
nick: " 振然",
avatar: "http://cdn.duitang.com/uploads/item/201408/30/20140830175648_js4hP.png",
userId: 1014
},
commentId: 1,
content: "新年好",
candelete: 1,
createTime: 1454397315
},
{
userA: {
nick: "羽翼君",
avatar: "http://upload.jianshu.io/users/upload_avatars/684042/bd1b2f796e3a.jpg",
userId: 1001
},
commentId: 1,
content: "hello~",
candelete: 1,
createTime: 1454483655
},
{
userA: {
nick: "诗雁",
avatar: "http://img4.duitang.com/uploads/item/201601/11/20160111175420_ZmTzU.jpeg",
userId: 1006
},
userB: {
nick: "羽翼君",
avatar: "http://upload.jianshu.io/users/upload_avatars/684042/bd1b2f796e3a.jpg",
userId: 1001
},
commentId: 1,
content: "哇,好巧-V-",
candelete: 1,
createTime: 1454483715
}
]
},
{
userInfo: {
nick: "傲露",
avatar: "http://img1.hao661.com/uploads/allimg/c141030/141463I01W940-5IH0.jpg",
userId: 1010
},
dynamicInfo: {
dynamicId: 10003,
createUserId: 1010,
dynamicType: 11,
praiseState: 1,
createTime: 1454743095,
candelete: 0
},
textField: "咳咳。。。。测试一下",
praiseList: [
{
nick: "羽翼君",
avatar: "http://upload.jianshu.io/users/upload_avatars/684042/bd1b2f796e3a.jpg",
userId: 1001
},
{
nick: "凌之",
avatar: "http://img5.imgtn.bdimg.com/it/u=3341777813,2293496692&fm=11&gp=0.jpg",
userId: 1003
},
{
nick: "白雪",
avatar: "http://img5.duitang.com/uploads/item/201406/26/20140626190424_TCXuP.jpeg",
userId: 1009
},
{
nick: "柔胤",
avatar: "http://img5.imgtn.bdimg.com/it/u=660454163,590477124&fm=11&gp=0.jpg",
userId: 1011
},
{
nick: "琪家",
avatar: "http://img5.duitang.com/uploads/item/201502/01/20150201174019_A5LYU.png",
userId: 1015
},
{
nick: "暮色伊人。",
avatar: "http://b.hiphotos.baidu.com/zhidao/wh%3D600%2C800/sign=6a5d1183d358ccbf1be9bd3c29e89006/9213b07eca806538d5541c2295dda144ad348241.jpg",
userId: 1041
},
{
nick: "短发美比我在这i",
avatar: "http://img0w.pconline.com.cn/pconline/1310/29/3719457_13667094527.jpg",
userId: 1044
},
{
nick: "~花舞う街で~",
avatar: "http://c.hiphotos.baidu.com/zhidao/wh%3D450%2C600/sign=fa3f854c8618367aaddc77d91b43a7e2/bba1cd11728b4710f37cb5a9c3cec3fdfc032307.jpg",
userId: 1045
},
{
nick: "妖视觉〃",
avatar: "http://img1.touxiang.cn/uploads/20141128/28-021817_497.jpg",
userId: 1063
},
{
nick: "墨烟三色倾人城。",
avatar: "http://img1.touxiang.cn/uploads/20140815/15-072749_540.jpg",
userId: 1079
},
{
nick: "别嘲笑胖女孩!",
avatar: "http://img1.touxiang.cn/uploads/20140812/12-072839_61.jpg",
userId: 1087
},
{
nick: "默 ’_哀、",
avatar: "http://img1.touxiang.cn/uploads/20140812/12-072932_837.jpg",
userId: 1094
}
],
commentList: [
{
userA: {
nick: "透过骨z1里的傲 つ",
avatar: "http://img1.touxiang.cn/uploads/20141128/28-021810_437.jpg",
userId: 1058
},
commentId: 4,
content: "这是啥",
candelete: 0,
createTime: 1454746695
},
{
userA: {
nick: "傲露",
avatar: "http://img1.hao661.com/uploads/allimg/c141030/141463I01W940-5IH0.jpg",
userId: 1010
},
userB: {
nick: "透过骨z1里的傲 つ",
avatar: "http://img1.touxiang.cn/uploads/20141128/28-021810_437.jpg",
userId: 1058
},
commentId: 4,
content: "have a test",
candelete: 0,
createTime: 1454746698
},
{
userA: {
nick: "透过骨z1里的傲 つ",
avatar: "http://img1.touxiang.cn/uploads/20141128/28-021810_437.jpg",
userId: 1058
},
userB: {
nick: "傲露",
avatar: "http://img1.hao661.com/uploads/allimg/c141030/141463I01W940-5IH0.jpg",
userId: 1010
},
commentId: 4,
content: "噢~so ga",
candelete: 0,
createTime: 1454750298
}
],
content: {
imgurl: [ ],
dynamicid: 0
}
},
{
userInfo: {
nick: "~花舞う街で~",
avatar: "http://c.hiphotos.baidu.com/zhidao/wh%3D450%2C600/sign=fa3f854c8618367aaddc77d91b43a7e2/bba1cd11728b4710f37cb5a9c3cec3fdfc032307.jpg",
userId: 1045
},
dynamicInfo: {
dynamicId: 10002,
createUserId: 1045,
dynamicType: 11,
createTime: 1454656515,
candelete: 0
},
praiseList: [ ],
commentList: [
{
userA: {
nick: "皓博",
avatar: "http://t2.du114.com/uploads/160105/18-16010511202M47.jpg",
userId: 1012
},
commentId: 3,
content: "~",
candelete: 0,
createTime: 1454742975
}
],
content: {
imgurl: [ ],
dynamicid: 0
}
},
{
userInfo: {
nick: "白雪",
avatar: "http://img5.duitang.com/uploads/item/201406/26/20140626190424_TCXuP.jpeg",
userId: 1009
},
dynamicInfo: {
dynamicId: 10004,
createUserId: 1009,
dynamicType: 11,
praiseState: 1,
createTime: 1454483715,
candelete: 0
},
textField: "我发发图,我不说话。",
praiseList: [
{
nick: "羽翼君",
avatar: "http://upload.jianshu.io/users/upload_avatars/684042/bd1b2f796e3a.jpg",
userId: 1001
}
],
commentList: [
{
userA: {
nick: "~花舞う街で~",
avatar: "http://c.hiphotos.baidu.com/zhidao/wh%3D450%2C600/sign=fa3f854c8618367aaddc77d91b43a7e2/bba1cd11728b4710f37cb5a9c3cec3fdfc032307.jpg",
userId: 1045
},
commentId: 2,
content: "路过评论。。。",
candelete: 0,
createTime: 1454742855
}
],
content: {
imgurl: [
"http://img5.duitang.com/uploads/item/201206/06/20120606175201_WZ2F3.thumb.700_0.jpeg",
"http://img5.duitang.com/uploads/item/201206/06/20120606175201_WZ2F3.thumb.700_0.jpeg",
"http://img5.duitang.com/uploads/item/201206/06/20120606175201_WZ2F3.thumb.700_0.jpeg"
],
dynamicid: 10004
}
}
]
},
stateCode: 200,
requestTime: 1456418501691,
start: 4,
loadMore: 0
}
复制代码
一起撸个朋友圈吧(step5) 控件篇【控件组装评论控件】相关推荐
- 一起撸个朋友圈吧(step5) - 控件篇【控件组装评论控件】
项目地址:github.com/razerdp/Fri- 上篇链接:http://www.jianshu.com/p/a2cdf81359fc 下篇链接:http://www.jianshu.com/ ...
- 一起撸个朋友圈吧(step5) - 控件篇【评论控件优化】
项目地址:github.com/razerdp/Fri- 上篇链接:http://www.jianshu.com/p/4cc3f9c8a713 下篇链接:http://www.jianshu.com/ ...
- 一起撸个朋友圈吧 (Step6) 评论对齐(点击评论对齐)【下】
项目地址:github.com/razerdp/Fri- 上篇链接:一起撸个朋友圈吧 (Step6) 评论对齐(未完全版本)[上] 下篇链接:一起撸个朋友圈吧 - 图片浏览(上)[图片点击前景色] 因 ...
- 一起撸个朋友圈吧 图片浏览(上)【图片点击前景色】
项目地址:github.com/razerdp/Fri- (能弱弱的求个star或者fork么QAQ) 上篇链接:一起撸个朋友圈吧 (Step6)- 评论对齐(点击评论对齐)[下] 下篇链接:一起撸个 ...
- 类似QQ空间,微信朋友圈,微博主页等,展示图片的九宫格控件
类似QQ空间,微信朋友圈,微博主页等,展示图片的九宫格控件,自动根据图片的数量确定图片大小和控件大小,使用Adapter模式设置图片,对外提供接口回调,使用接口加载图片,支持任意的图片加载框架,如 G ...
- 一起撸个朋友圈吧(step2) 数据结构(JSON结构)【下】篇
项目地址:https://github.com/razerdp/FriendCircle 上篇链接:http://www.jianshu.com/p/94403e45fbef 下篇链接:http:// ...
- 微信朋友圈php主题,有关微信朋友圈的文章推荐10篇
这篇文章主要介绍了.Net语言Smobiler开发平台如何仿微信朋友圈的消息样式?本文为大家揭晓答案最前面的话:Smobiler是一个在VS环境中使用.Net语言来开发APP的开发平台,也许比Xama ...
- 朋友圈自动回复评论_微信新版,朋友圈可以表情包回复了!网友:评论区斗起来.jpg...
你们发现了吗? 朋友圈可以发表情包评论了! 微信iOS版7.0.9正式版今天迎来更新支持发消息时可以引用之前的内容更令人惊喜的是不少网友都发现新版本还新增朋友圈自定义表情评论功能可以用表情包评论别人的 ...
- python朋友圈自动点赞_基于AirTest+Python的ios自动化测试demo(微信朋友圈无限点赞)...
AirTest相比Appuim有个好处就是可以对GUI图片进行捕捉和最新版本支持WebView(目前Appuim不支持iOS12的WebView进行Xpath抓取) AirTest环境搭建可参考以下链 ...
最新文章
- Anaconda 和 JetBrains 联手推出 'Anaconda的PyCharm'
- 【Android 事件分发】ItemTouchHelper 事件分发源码分析 ( 绑定 RecyclerView )
- 用Red5搭建支持WEB播放的实时监控视频
- mysql 帮助命令_一篇文章帮你搞定所有MySQL命令!
- ALV 刷新实现(自动)
- Android开发之无bug滑动删除源码(非第三方库)
- Linux 命令之 ls -- 列出指定目录下的内容
- 设计微服务架构需要解决的问题
- html5 函数大全,5 个强大的HTML5 API 函数推荐
- igxe查询交易机器人_区块链数字货币交易所开发功能技术解决方案 | 拾里郎
- CIO众论:转型路径和新技术实践
- “暴风一号”学习日记(一)
- 2022最新u盘升级重装win10方法
- php当月1号怎么获取,php获取下月1号和月底最后一天的时间
- c语言编程能力风暴,学编程得从娃娃抓起:Abilix 能力风暴 发布 全新教育机器人伯牙...
- 美国依靠美元霸权, 是如何收割世界财富的?
- 51nod《拉勾专业算法能力测评》测试有感
- Activity 重启recreate() 与ViewPager一起使用出现的问题
- java实现登录验证机制的技术_基于token的登陆验证机制
- springboot项目添加了logback-spring.xml配置文件不生效