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


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

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

    我们来分析一下:

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

    效果图:

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

    1、点击不同的文字部分(文字个数还不确定)有相应的响应操作(进入个人主页等等)
    
    2、一个TextView中某些文字有不同的颜色

    下面学习如何实现-->

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

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

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

    1、RecyclerView 自带实现复用机制,对于工作1--2年左右的,不建议使用自己写的复用ListView
    2、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 = 0;
            if (name != null&&name.size()!=0)
                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()!=0) {
                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()>0) {
                    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(0, 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 = 0;
            if (name != null&&name.size()!=0)
                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()!=0) {
                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()>0) {
                    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(0, 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开发,有同领域的朋友欢迎关注交流~~~                                                                                        

             

  • 相关阅读:
    理解maven命令package、install、deploy的联系与区别
    Mybatis中传参包There is no getter for property named 'XXX' in 'class java.lang.String'
    Spring boot 外部资源配置
    配置spring boot 内置tomcat的accessLog日志
    spring使用@Value标签读取.properties文件的中文乱码问题的解决
    使用Lombok简化你的代码
    统计mysql数据库中数据表/字段等数量的sql
    Kettle
    Python内置函数(39)——locals
    Python内置函数(38)——list
  • 原文地址:https://www.cnblogs.com/xqxacm/p/5231600.html
Copyright © 2020-2023  润新知