大家都玩QQ空间客户端,对于每一个说说,我们都可以评论,那么,对于某一条评论:

白雪公主 回复 小矮人 : 你们好啊~

我们来分析一下:

、QQ空间允许我们 点击 回复人和被回复人的名字就可以进入对于用户的个人主页(即点击文字“白雪公主”/“小矮人”,就可以进入到这俩用户相应个人主页)
、点击 回复的文字,就可以对回复人进行回复(即点击评论中回复的内容“你们好啊~”,便对弹出一个编辑框对回复人“白雪公主”进行回复)
、回复人 和 被回复人 的名字是有颜色的

效果图:

作为一个android开发者,我们要实现对一个TextView :

、点击不同的文字部分(文字个数还不确定)有相应的响应操作(进入个人主页等等)

、一个TextView中某些文字有不同的颜色

下面学习如何实现-->

----------------------------------------------------------------------------------

首先介绍下QQ空间说说列表这一个界面(fragment来实现)的整体框架:

1、使用RecyclerView来展示说说列表   why?

、RecyclerView 自带实现复用机制,对于工作1--2年左右的,不建议使用自己写的复用ListView
、RecyclerView 方便对于某一个item 项的增删改操作 (大优势),比如控件删除该说说的功能的实现 RecyclerView实现更好

2、每一个item 内部 ,评论文字部分 用不可以滑动的ListView(RecyclerView理论上更棒,反正不可以滑动就行了)来展示 (博主一开始想的是用LinearLayout 内部 动态添加TextView来展示,经测试,太麻烦且易出错)

不可滑动的ListView 代码 --> 自定义不可滑动的ListView和GridView

-----------------------------------------------------------------------------------

下面用一个Demo来学习如何实现说说评论的效果:

首先布局文件,就一个不可滑动的ListView,我们Demo只展示评论列表

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
    
  <!-- 注意listview要去除分割线 -->
<com.xqx.com.qqhome.NoScrollListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@null" >
</com.xqx.com.qqhome.NoScrollListView> </RelativeLayout>

然后是Item项的布局文件(评论文字):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/txt_comment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="2dp"
/>
</LinearLayout>

-----------------------------------------------------------------------------------

看java文件部分:

MainActivity.java

很简单,自己创建了5条评论,添加到自己写的适配器中

注意:评论有的是没有被回复人的!

public class MainActivity extends Activity {

    private NoScrollListView noScrollListView;

    /* --------- 数据源----------- */
//记录回复说说用户的集合
private ArrayList<String> name;
//记录被回复说说用户的集合
private ArrayList<String> toName;
//记录评论内容的集合
private ArrayList<String> content; /* --------- 适配器------------*/
private CommentAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); noScrollListView = (NoScrollListView) findViewById(R.id.listview); name = new ArrayList<>();
toName = new ArrayList<>();
content = new ArrayList<>(); //添加数据 ,Demo只添加5条评论
name.add("白雪公主");
toName.add("小矮人");
content.add("你们好啊~"); name.add("小矮人");
toName.add("白雪公主");
content.add("白雪公主,早上好啊~"); name.add("王子");
toName.add("");
content.add("这条说说很有道理的样子啊~"); name.add("国王");
toName.add("");
content.add("我很喜欢这条说说~"); name.add("白雪公主");
toName.add("王子");
content.add("你也是XX的朋友啊?"); adapter = new CommentAdapter(name,toName,content,this);
noScrollListView.setAdapter(adapter); } }

-----------------------------------------------------------------------------------

布局文件有了,MainActivity有了,剩下最主要的适配器了

看下自定义适配器所需要的属性 和 写个必要方法:

public class CommentAdapter extends BaseAdapter {

    /* --------- 数据源----------- */
//记录回复说说用户的集合
private ArrayList<String> name;
//记录被回复说说用户的集合
private ArrayList<String> toName;
//记录评论内容的集合
private ArrayList<String> content; private Context context; public CommentAdapter(ArrayList<String> name, ArrayList<String> toName, ArrayList<String> content, Context context) {
this.name = name;
this.toName = toName;
this.content = content;
this.context = context;
} @Override
public int getCount() {
int ret = ;
if (name != null&&name.size()!=)
ret = name.size();
return ret;
} @Override
public Object getItem(int position) {
return position;
} @Override
public long getItemId(int position) {
return position;
}
  class ViewHolder{
   TextView txt_comment;
  }

重点来了 getView() ~~

首先 建议大家要看下这几篇文章

(转) SpannableString与SpannableStringBuilder

TextView显示html样式的文字

浅谈ClickableSpan , 实现TextView文本某一部分文字的点击响应

然后~~

注释都在代码中:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
//其实评论一般都是文字,高级点的带有图片评论,光文字的话复用不复用就没什么大区别了
View view = null;
if(convertView!=null)
{
view = convertView;
}
else
{
view = LayoutInflater.from(context).inflate(R.layout.item_comment, parent,false);
}
ViewHolder holder = (ViewHolder) view.getTag();
if(holder==null)
{
holder = new ViewHolder();
holder.txt_comment = (TextView) view.findViewById(R.id.txt_comment); view.setTag(holder);
}
//给相应位置的文字赋内容
if (name != null && name.size()!=) {
StringBuilder actionText = new StringBuilder(); //谁回复
actionText.append("<a style=\"text-decoration:none;\" href='name' ><font color='#1468a3'>"
+ name.get(position) + "</font> </a>"); // 回复谁,被回复的人可能不存在。
if(toName.get(position)!=null&&toName.get(position).length()>) {
actionText.append("回复");
actionText.append("<font color='#1468a3'><a style=\"text-decoration:none;\" href='toName'>"
+ toName.get(position) + " " + " </a></font>");
}
// 内容
actionText.append("<font color='#484848'><a style=\"text-decoration:none;\" href='content'>"
+ ":" + content.get(position) + " " + " </a></font>"); holder.txt_comment.setText(Html.fromHtml(actionText.toString()));
holder.txt_comment.setMovementMethod(LinkMovementMethod
.getInstance());
CharSequence text = holder.txt_comment.getText();
int ends = text.length();
Spannable spannable = (Spannable) holder.txt_comment.getText();
URLSpan[] urlspan = spannable.getSpans(, ends, URLSpan.class);
SpannableStringBuilder stylesBuilder = new SpannableStringBuilder(text);
stylesBuilder.clearSpans(); for (URLSpan url : urlspan) {
FeedTextViewURLSpan myURLSpan = new FeedTextViewURLSpan(url.getURL(),
context,name.get(position),toName.get(position),content.get(position));
stylesBuilder.setSpan(myURLSpan, spannable.getSpanStart(url),
spannable.getSpanEnd(url), spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
holder.txt_comment.setText(stylesBuilder);
holder.txt_comment.setFocusable(false);
holder.txt_comment.setClickable(false);
holder.txt_comment.setLongClickable(false); } return view;
} static class FeedTextViewURLSpan extends ClickableSpan {
private String clickString;
private Context context;
// 回复人的名字
private String name;
// 被回复人的名字
private String toName;
// 评论内容
private String content; public FeedTextViewURLSpan(String clickString, Context context, String name, String toName, String content) {
this.clickString = clickString;
this.context = context;
this.name = name;
this.toName = toName;
this.content = content;
} @Override
public void updateDrawState(TextPaint ds) {
ds.setUnderlineText(false);
//给标记的部分 的文字 添加颜色
if(clickString.equals("toName")){
ds.setColor(context.getResources().getColor(R.color.blue));
}else if(clickString.equals("name")){
ds.setColor(context.getResources().getColor(R.color.blue));
}
} @Override
public void onClick(View widget) {
// 根据文字的标记 来进行相应的 响应事件
if (clickString.equals("toName")) {
//可以再次进行跳转activity的操作
Toast.makeText(context,"点击了"+toName,Toast.LENGTH_SHORT).show();
} else if (clickString.equals("name")) {
//可以再次进行跳转activity的操作
Toast.makeText(context,"点击了"+name,Toast.LENGTH_SHORT).show();
} else if(clickString.equals("content")){
//可以再次进去回复评论的操作
Toast.makeText(context,"点击了"+content,Toast.LENGTH_SHORT).show();
}
}
}

适配器完整代码:

import android.content.Context;
import android.text.Html;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.TextPaint;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.text.style.URLSpan;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Adapter;
import android.widget.BaseAdapter;
import android.widget.TextView;
import android.widget.Toast; import java.util.ArrayList; public class CommentAdapter extends BaseAdapter { /* --------- 数据源----------- */
//记录回复说说用户的集合
private ArrayList<String> name;
//记录被回复说说用户的集合
private ArrayList<String> toName;
//记录评论内容的集合
private ArrayList<String> content; private Context context; public CommentAdapter(ArrayList<String> name, ArrayList<String> toName, ArrayList<String> content, Context context) {
this.name = name;
this.toName = toName;
this.content = content;
this.context = context;
} @Override
public int getCount() {
int ret = ;
if (name != null&&name.size()!=)
ret = name.size();
return ret;
} @Override
public Object getItem(int position) {
return position;
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
//其实评论一般都是文字,高级点的带有图片评论,光文字的话复用不复用就没什么大区别了
View view = null;
if(convertView!=null)
{
view = convertView;
}
else
{
view = LayoutInflater.from(context).inflate(R.layout.item_comment, parent,false);
}
ViewHolder holder = (ViewHolder) view.getTag();
if(holder==null)
{
holder = new ViewHolder();
holder.txt_comment = (TextView) view.findViewById(R.id.txt_comment); view.setTag(holder);
}
//给相应位置的文字赋内容
if (name != null && name.size()!=) {
StringBuilder actionText = new StringBuilder(); //谁回复
actionText.append("<a style=\"text-decoration:none;\" href='name' ><font color='#1468a3'>"
+ name.get(position) + "</font> </a>"); // 回复谁,被回复的人可能不存在。
if(toName.get(position)!=null&&toName.get(position).length()>) {
actionText.append("回复");
actionText.append("<font color='#1468a3'><a style=\"text-decoration:none;\" href='toName'>"
+ toName.get(position) + " " + " </a></font>");
}
// 内容
actionText.append("<font color='#484848'><a style=\"text-decoration:none;\" href='content'>"
+ ":" + content.get(position) + " " + " </a></font>"); holder.txt_comment.setText(Html.fromHtml(actionText.toString()));
holder.txt_comment.setMovementMethod(LinkMovementMethod
.getInstance());
CharSequence text = holder.txt_comment.getText();
int ends = text.length();
Spannable spannable = (Spannable) holder.txt_comment.getText();
URLSpan[] urlspan = spannable.getSpans(, ends, URLSpan.class);
SpannableStringBuilder stylesBuilder = new SpannableStringBuilder(text);
stylesBuilder.clearSpans(); for (URLSpan url : urlspan) {
FeedTextViewURLSpan myURLSpan = new FeedTextViewURLSpan(url.getURL(),
context,name.get(position),toName.get(position),content.get(position));
stylesBuilder.setSpan(myURLSpan, spannable.getSpanStart(url),
spannable.getSpanEnd(url), spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
holder.txt_comment.setText(stylesBuilder);
holder.txt_comment.setFocusable(false);
holder.txt_comment.setClickable(false);
holder.txt_comment.setLongClickable(false); } return view;
} static class FeedTextViewURLSpan extends ClickableSpan {
private String clickString;
private Context context;
// 回复人的名字
private String name;
// 被回复人的名字
private String toName;
// 评论内容
private String content; public FeedTextViewURLSpan(String clickString, Context context, String name, String toName, String content) {
this.clickString = clickString;
this.context = context;
this.name = name;
this.toName = toName;
this.content = content;
} @Override
public void updateDrawState(TextPaint ds) {
ds.setUnderlineText(false);
//给标记的部分 的文字 添加颜色
if(clickString.equals("toName")){
ds.setColor(context.getResources().getColor(R.color.blue));
}else if(clickString.equals("name")){
ds.setColor(context.getResources().getColor(R.color.blue));
}
} @Override
public void onClick(View widget) {
// 根据文字的标记 来进行相应的 响应事件
if (clickString.equals("toName")) {
//可以再次进行跳转activity的操作
Toast.makeText(context,"点击了"+toName,Toast.LENGTH_SHORT).show();
} else if (clickString.equals("name")) {
//可以再次进行跳转activity的操作
Toast.makeText(context,"点击了"+name,Toast.LENGTH_SHORT).show();
} else if(clickString.equals("content")){
//可以再次进去回复评论的操作
Toast.makeText(context,"点击了"+content,Toast.LENGTH_SHORT).show();
} }
} class ViewHolder{
TextView txt_comment;
} }

CommentAdapter.java

-----------------------------------------------------------------------------------

如何实现QQ空间说说列表评论的展示介绍完了~~

那么如何 回复评论呢?

如何将新评论的评论及时的显示在当前列表呢?

之后的博客继续讨论~~~

相关知识:

QQ空间实现(二)—— 分享功能 / 弹出PopupWindow

博主现在从事社交类社区类APP开发,有同领域的朋友欢迎关注交流~~~

Android项目实战(十六):QQ空间实现(一)—— 展示说说中的评论内容并有相应点击事件的更多相关文章

  1. Android项目实战(六):JazzyGridView和JazzyListView的使用

    GridView和ListView控件划动的动画效果 ------------------------------------------------------------------------- ...

  2. (转载)Android项目实战(十七):QQ空间实现(二)—— 分享功能 / 弹出PopupWindow

    Android项目实战(十七):QQ空间实现(二)—— 分享功能 / 弹出PopupWindow   这是一张QQ空间说说详情的截图. 分析: 1.点击右上角三个点的图标,在界面底部弹出一个区域,这个 ...

  3. Android项目实战(十七):QQ空间实现(二)—— 分享功能 / 弹出PopupWindow

    这是一张QQ空间说说详情的截图. 分析: .点击右上角三个点的图标,在界面底部弹出一个区域,这个区域有一些按钮提供给我们操作 .当该区域出现的时候,详情界面便灰了,也说成透明度变化了 .当任意选了一个 ...

  4. Android项目实战(四十九):Andoird 7.0+相机适配

    解决方案类似: Android项目实战(四十):Andoird 7.0+ 安装APK适配 解决方法: 一.在AndroidManifest.xml 文件中添加 四大组件之一的 <provider ...

  5. (转载)Android项目实战(三十二):圆角对话框Dialog

    Android项目实战(三十二):圆角对话框Dialog   前言: 项目中多处用到对话框,用系统对话框太难看,就自己写一个自定义对话框. 对话框包括:1.圆角 2.app图标 , 提示文本,关闭对话 ...

  6. (转载)Android项目实战(二十八):Zxing二维码实现及优化

    Android项目实战(二十八):Zxing二维码实现及优化   前言: 多年之前接触过zxing实现二维码,没想到今日项目中再此使用竟然使用的还是zxing,百度之,竟是如此牛的玩意. 当然,项目中 ...

  7. (转载)Android项目实战(二十八):使用Zxing实现二维码及优化实例

    Android项目实战(二十八):使用Zxing实现二维码及优化实例 作者:听着music睡 字体:[增加 减小] 类型:转载 时间:2016-11-21我要评论 这篇文章主要介绍了Android项目 ...

  8. Android项目实战(四十四):Zxing二维码切换横屏扫描

    原文:Android项目实战(四十四):Zxing二维码切换横屏扫描 Demo链接 默认是竖屏扫描,但是当我们在清单文件中配置横屏显示的时候: <activity android:name=&q ...

  9. Android项目实战(四十):在线生成按钮Shape的网站

    原文:Android项目实战(四十):在线生成按钮Shape的网站 AndroidButton Make  右侧设置按钮的属性,可以即时看到效果,并即时生成对应的.xml 代码,非常高效(当然熟练的话 ...

随机推荐

  1. bower的使用

    一.bower的安装 安装nodejs的最新版本: 安装npm. 由于npm是nodejs的包管理器,所以在将nodejs安装完成后,npm也就自动安装完成. 安装git. 安装bower. 使用 n ...

  2. Tips3:通过Layer下拉菜单来锁定游戏物体和控制物体的可视化

    通过把不同的游戏物体放在不同的Layer里面能对不同类的游戏物体进行很方便的控制,如果某些游戏物体创建后你不想再改动,如地面 装饰 什么的, 你可以通过点击Layer下拉菜单把它们锁定了 也可以通过控 ...

  3. Sprint回顾

    1.回顾组织 主题:“我们下次怎么样才能更加认真对待?” 时间:设定为1至2个小时. 参与者:整个团队. 场所:能够在不受干扰的情况下讨论. 秘书:指定某人当秘书,筹备.记录.整理.  2.回顾流程 ...

  4. .NET 配置文件简单使用

    当我们开发系统的时候要把一部分设置提取到外部的时候,那么就要用到.NET的配置文件了.比如我的框架中使用哪个IOC容器需要可以灵活的选择,那我就需要把IOC容器的设置提取到配置文件中去配置.实现有几种 ...

  5. 字符串 - 近似回文词 --- csu 1328

    近似回文词 Problem's Link:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1328 analyse: 直接暴力枚举每一个终点,然后枚举 ...

  6. Unity3D-terrain brush地形画刷无法出现在Scene中,无法刷地图2

    原因大概是 画刷brush 太小了,地图也太小了,没出出现. 如图,非正常状态: 解决方法: tag: terrain brush not working unity

  7. Google判断广告点击作弊的几种方式和数据

     Google判断广告点击作弊的几种方式和数据. 作弊广告点击的CTR数据太高网上有研究说如果CTR值大于了10%的站被干掉的可能性很高,他们会被单独拿出来分析.一般来说低于6-7%的CTR是安全红线 ...

  8. Python入门笔记(14):Python的字符编码

    一.字符编码中ASCII.Unicode和UTF-8的区别 点击阅读:http://www.cnblogs.com/kingstarspe/p/ASCII.html 再推荐一篇相关博文:http:// ...

  9. 404 Not Found错误页面的解决方法和注意事项

    最近这段时间一直忙于整理网站的错误页面,期间整理了很多关于404 Not Found错误页面的知识,加之最近也在帮团队新来的人员培训seo优化知识,所以在此借助马海祥博客的平台就拿出来跟大家一起分享一 ...

  10. USE “schema_name” in PostgreSQL

    http://timmurphy.org/tag/mysql/ http://timmurphy.org/2009/11/17/use-schema_name-in-postgresql/ ===== ...