二级评论android_android 二级评论 - CSDN
  • 之前项目中产品需求带二级评论列表,首先想到是QQ空间评论列表。 先上效果图 下面我们来分析一下布局结构,首先一级列表是listview,然后二级列表也可以有多条,为了省事我只添加了一条,第一反应是listview...

    之前项目中产品需求带二级的评论列表,首先想到是QQ空间评论列表。
    先上效果图
    这里写图片描述
    下面我们来分析一下布局结构,首先一级列表是listview,然后二级列表也可以有多条,为了省事我只添加了一条,第一反应是listview嵌套listview,然而listview嵌套又会出现显示不全等问题,于是自定义listview解决高度问题,废话不多说直接上代码:
    布局文件最外层是竖直的LinearLayout

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >
    
        <com.world.compet.view.CircleImageView
            android:id="@+id/commentItemImg"
            android:layout_width="35dp"
            android:layout_height="35dp"
            android:layout_marginLeft="12dp"
            android:layout_marginTop="15dp"
            android:contentDescription="@string/app_name"
            android:src="@drawable/photo_pic" />
    
        <RelativeLayout
            android:id="@+id/rl"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="12dp"
            android:layout_marginTop="13dp" >
    
            <!-- 评论人昵称 -->
    
            <TextView
                android:id="@+id/commentNickname"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:text="游客1"
                android:textColor="#333333"
                android:textSize="14sp" />
    
            <ImageView
                android:id="@+id/male"
                android:layout_width="11dp"
                android:layout_height="11dp"
                android:layout_alignTop="@+id/commentNickname"
                android:layout_centerVertical="true"
                android:layout_marginLeft="7dp"
                android:layout_marginTop="4dp"
                android:visibility="invisible"
                android:layout_toRightOf="@+id/commentNickname"
                android:background="@drawable/nv" />
            <!-- 评论时间 -->
    
            <TextView
                android:id="@+id/commentItemTime"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_below="@id/commentNickname"
                android:layout_marginTop="3dp"
                android:text="6秒前"
                android:textColor="#b6b6b6"
                android:textSize="11sp" />
            <!-- 评论人内容 -->
    
            <LinearLayout
                android:id="@+id/description_layout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_below="@id/commentItemTime"
                android:orientation="vertical"
                android:paddingTop="15dip" >
    
                <TextView
                    android:id="@+id/commentItemContent"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_alignParentLeft="true"
                    android:layout_below="@id/commentItemTime"
                    android:ellipsize="end"
                    android:textColor="#666666"
                    android:lineSpacingExtra="2dp"
                    android:textSize="14sp" />
    
    
            </LinearLayout>
    
            <LinearLayout
                android:id="@+id/ll_comment"
                android:layout_width="wrap_content"
                android:layout_height="30dp"
                android:layout_alignParentRight="true"
                android:layout_marginTop="2dp"
                android:orientation="horizontal" >
    
                <LinearLayout
                    android:id="@+id/ll_dig"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginRight="20dp"
                    android:orientation="horizontal" >
    
                    <ImageView
                        android:id="@+id/like"
                        android:layout_width="15dp"
                        android:layout_height="15dp"
                        android:layout_marginRight="4dp"
                        android:background="@drawable/pinglunzan1" />
    
                    <TextView
                        android:id="@+id/like_num"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="0"
                        android:textColor="#b6b6b6"
                        android:textSize="12dp" />
                </LinearLayout>
    
                <LinearLayout
                    android:id="@+id/ll_comm"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:orientation="horizontal" >
    
                    <ImageView
                        android:id="@+id/com"
                        android:layout_width="15dp"
                        android:layout_height="15dp"
                        android:layout_marginRight="4dp"
                        android:background="@drawable/xiaoxiicon" />
    
                    <TextView
                        android:id="@+id/tv_commentnum"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="0"
                        android:textColor="#b6b6b6"
                        android:textSize="12dp" />
                </LinearLayout>
            </LinearLayout>
    
            <com.world.compet.view.NoScrollListView
                android:id="@+id/replyList"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_below="@+id/description_layout"
                android:layout_marginTop="15dp"
                android:divider="#00000000"
                android:focusable="false" />
    
            <View
                android:id="@+id/line"
                android:layout_width="match_parent"
                android:layout_height="15dp"
                android:layout_below="@+id/replyList"
                android:background="#00ffffff" />
        </RelativeLayout>
    </LinearLayout>
    
    
    
    <View
        android:id="@+id/nolast"
        android:layout_width="match_parent"
        android:layout_height="0.5dp"
        android:layout_marginLeft="57dp"
        android:layout_marginRight="12dp"
        android:background="#f3f3f3" />
    <View
        android:id="@+id/last"
        android:visibility="gone"
        android:layout_width="match_parent"
        android:layout_height="0.5dp"
        android:layout_marginRight="12dp"
        android:background="#f3f3f3" />
    

    自定义listview:
    package com.example.jing.commentdemo.view;

    import android.content.Context;
    import android.util.AttributeSet;
    import android.widget.ListView;

    public class NoScrollListView extends ListView {
    public NoScrollListView(Context context, AttributeSet attrs) {
    super(context,attrs);
    }
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
    int mExpandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
    super.onMeasure(widthMeasureSpec, mExpandSpec);
    }

    }
    下面主要是adapter
    public class CommentAdapter extends BaseAdapter {

    private Context context;
    private List<CommentBean> list;
    private LayoutInflater inflater;
    private replyClickListener rClickListener;
    private AnimateFirstDisplayListener animateFirstDisplayListener = new AnimateFirstDisplayListener();
    @SuppressWarnings("unused")
    private String nid;
    private Handler handler;
    int maxDescripLine = 5;
    
    // int isdigg=0;
    
    public CommentAdapter(String nid, Context context,
                          List<CommentBean> list, int resourceId, Handler handler) {
        this.nid = nid;
        this.list = list;
        this.context = context;
        inflater = LayoutInflater.from(context);
        this.handler = handler;
    }
    
    @Override
    public int getCount() {
        return list.size();
    }
    
    @Override
    public Object getItem(int position) {
        return list.get(position);
    }
    
    @Override
    public long getItemId(int position) {
        return position;
    }
    
    @SuppressLint("CutPasteId")
    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        final CommentBean bean = list.get(position);
        ViewHolder holder = null;
        if (convertView == null) {
            holder = new ViewHolder();
            convertView = inflater.inflate(R.layout.all_comment_item, null);
            holder.nolast=convertView.findViewById(R.id.nolast);
            holder.last=convertView.findViewById(R.id.last);
            holder.commentItemImg = (CircleImageView) convertView
                    .findViewById(R.id.commentItemImg);
            holder.commentNickname = (TextView) convertView
                    .findViewById(R.id.commentNickname);
            holder.likeNum = (TextView) convertView.findViewById(R.id.like_num);
            holder.commentItemTime = (TextView) convertView
                    .findViewById(R.id.commentItemTime);
            holder.commentItemContent = (TextView) convertView
                    .findViewById(R.id.commentItemContent);
            holder.replyList = (NoScrollListView) convertView
                    .findViewById(R.id.replyList);
    
            holder.ivLike = (ImageView) convertView.findViewById(R.id.like);
            holder.ivSex = (ImageView) convertView.findViewById(R.id.male);
            holder.replayNum = (TextView) convertView
                    .findViewById(R.id.tv_commentnum);
            holder.digLayout = (LinearLayout) convertView
                    .findViewById(R.id.ll_dig);
            holder.comLayout = (LinearLayout) convertView
                    .findViewById(R.id.ll_comm);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        if (position==list.size()-1) {
            holder.last.setVisibility(View.VISIBLE);
            holder.nolast.setVisibility(View.GONE);
        }else {
            holder.nolast.setVisibility(View.VISIBLE);
            holder.last.setVisibility(View.GONE);
    
        }
        final ComReplyAdapter adapter = new ComReplyAdapter(
                context, bean.getL2_comment(), R.layout.reply_item);
         holder.replyList.setAdapter(adapter);
        // Utility.setListViewHeightBasedOnChildren(holder.replyList);
        holder.comLayout
                .setOnClickListener(new TextviewClickListener(position) {
    
                    @Override
                    public void onClick(View v) {
                        // TODO Auto-generated method stub
                        super.onClick(v);
                    }
                });
        holder.digLayout
                .setOnClickListener(new TextviewClickListener(position) {
                    @Override
                    public void onClick(View v) {
                        // TODO Auto-generated method stub
                        super.onClick(v);
                    }
    
                });
        holder.commentItemContent.setOnClickListener(new TextviewClickListener(
                position) {
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                super.onClick(v);
            }
    
        });
        holder.replayNum.setText(bean.getComment_count());
        if (holder.commentItemImg.getTag() == null
                || !holder.commentItemImg.getTag().toString()
                        .equals(bean.getAvatar())) {
    
            App.getImageLoader().displayImage(bean.getAvatar(),
                    holder.commentItemImg, App.getInstance().getOptions(),
                    animateFirstDisplayListener);
        }
        holder.commentItemImg.setOnClickListener(new OnClickListener() {
    
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                // 跳转个人主页
                 Toast.makeText(context, bean.getNick_name(),
                 Toast.LENGTH_SHORT).show();
    
            }
        });
        holder.commentNickname.setText(bean.getNick_name());
        holder.commentItemTime.setText(bean.getCreate_time() + " "
                + bean.getUnivs_name());
        // holder.commentItemContent.setText(bean.getContent());
        final TextView descriptionView = (TextView) convertView
                .findViewById(R.id.commentItemContent);
        descriptionView.setText(bean.getContent());
    
    
    
    
        holder.likeNum.setText(bean.getDigg_count());
        // notifyDataSetChanged();
        adapter.setOnCommentClickListenern(new ComReplyAdapter.commentClickListener() {
    
            @Override
            public void OnCommentClick(String realName, String tuid,
                                       String parentid, int sonPosition) {
                // TODO Auto-generated method stub
                // SKApp.getInstance().showErrToast(realName);
                rClickListener.onClick(realName, tuid, parentid, position,
                        sonPosition);
    
            }
        });
        if (bean.getL2_comment().size() > 0) {
            holder.replyList.setVisibility(View.VISIBLE);
        } else {
            holder.replyList.setVisibility(View.GONE);
        }
        return convertView;
    }
    
    private final class ViewHolder {
        public CircleImageView commentItemImg; // 评论人图片
        public TextView commentNickname; // 评论人昵称
        public TextView likeNum; // 点赞人数
        public TextView commentItemTime; // 评论时间
        public TextView commentItemContent; // 评论内容
        public NoScrollListView replyList; // 评论回复列表
        public ImageView ivLike;
        public ImageView ivSex;
        public TextView replayNum;
        public LinearLayout digLayout;
        public LinearLayout comLayout;
        public View last;
        public View nolast;
        // public Button moreButton;
        // public View line;
    
    }
    
    /**
     * 事件点击监听器
     */
    private class TextviewClickListener implements OnClickListener {
    
        int position;
    
        public TextviewClickListener(int position) {
            this.position = position;
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
            case R.id.ll_dig:
                Message digmsg = handler.obtainMessage();
                digmsg.what = 10;
                digmsg.arg1 = position;
    
                handler.sendMessage(digmsg);
                break;
            case R.id.ll_comm:
                Message msg = handler.obtainMessage();
                msg.what = 11;
                msg.arg1 = position;
                handler.sendMessage(msg);
                break;
            case R.id.commentItemContent:
                Message msg1 = handler.obtainMessage();
                msg1.what = 11;
                msg1.arg1 = position;
                handler.sendMessage(msg1);
                break;
            }
        }
    }
    
    public interface replyClickListener {
        public void onClick(String realName, String tuid, String parentid,
                            int pos, int sonPosition);
    }
    
    public void SetOnRepalClickListener(replyClickListener rListener) {
    
        rClickListener = rListener;
    
    }
    

    }
    二级adapter
    public class ComReplyAdapter extends BaseAdapter {

    private List<L2CommentBean> list;
    private LayoutInflater inflater;
    private TextView replyContent;
    private SpannableString ss;
    private commentClickListener commClickListener;
    private Context context;
    
    public ComReplyAdapter(Context context,
                           List<L2CommentBean> list, int resourceId) {
        this.context = context;
        this.list = list;
        inflater = LayoutInflater.from(context);
    }
    
    @Override
    public int getCount() {
        return list.size();
    }
    
    @Override
    public Object getItem(int position) {
        return list.get(position);
    }
    
    @Override
    public long getItemId(int position) {
        return position;
    }
    
    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        final L2CommentBean bean = list.get(position);
        convertView = inflater.inflate(R.layout.reply_item, null);
        replyContent = (TextView) convertView.findViewById(R.id.replyContent);
    
        final String replyNickName = bean.getNick_name() + " ";
        final String commentNickName = " " + bean.getTo_nick_name();
        String replyContentStr = bean.getContent();
        // 用来标识在 Span 范围内的文本前后输入新的字符时是否把它们也应用这个效果
        // Spanned.SPAN_EXCLUSIVE_EXCLUSIVE(前后都不包括)
        // Spanned.SPAN_INCLUSIVE_EXCLUSIVE(前面包括,后面不包括)
        // Spanned.SPAN_EXCLUSIVE_INCLUSIVE(前面不包括,后面包括)
        // Spanned.SPAN_INCLUSIVE_INCLUSIVE(前后都包括)
        if (!TextUtils.isEmpty(commentNickName)) {
    
            ss = new SpannableString(replyNickName + "回复" + commentNickName
                    + " " + replyContentStr);
        } else {
            ss = new SpannableString(replyNickName + " " + replyContentStr);
    
        }
    
        ss.setSpan(new ForegroundColorSpan(Color.parseColor("#22bfa7")), 0,
                replyNickName.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        ss.setSpan(new ForegroundColorSpan(Color.parseColor("#22bfa7")),
                replyNickName.length() + 2, replyNickName.length()
                        + commentNickName.length() + 2,
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        // 为回复的人昵称添加点击事件----前面的名字
        ss.setSpan(new TextSpanClick(replyNickName) {
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                super.onClick(v);
    

    // Toast.makeText(context,”个人中心”,Toast.LENGTH_SHORT).show();
    }
    }, 0, replyNickName.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    // //为评论的人的添加点击事件——-后面的名字
    ss.setSpan(new TextSpanClick(commentNickName) {
    @Override
    public void onClick(View v) {
    // TODO Auto-generated method stub
    super.onClick(v);
    // Toast.makeText(context,”个人中心”,Toast.LENGTH_SHORT).show();
    }
    }, replyNickName.length() + 2,
    replyNickName.length() + commentNickName.length() + 2,
    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    replyContent.setText(ss);

        // 添加点击事件时,必须设置
        replyContent.setMovementMethod(LinkMovementMethod.getInstance());
        replyContent.setOnClickListener(new OnClickListener() {
    
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                // Toast.makeText(context, replyNickName+"~~",
                // Toast.LENGTH_SHORT).show();
    
    
                    commClickListener.OnCommentClick(replyNickName,
                            bean.getUid(), bean.getParent_comment_id(),
                            position);
    
            }
        });
        return convertView;
    }
    
    private class TextSpanClick extends ClickableSpan {
        @SuppressWarnings("unused")
        private String nam;
    
        public TextSpanClick(String name) {
            nam = name;
        }
    
        @Override
        public void updateDrawState(TextPaint ds) {
            super.updateDrawState(ds);
            ds.setUnderlineText(false);// 取消下划线
        }
    
        @Override
        public void onClick(View v) {
            // 跳转个人主页
            // Toast.makeText(context, nam, Toast.LENGTH_SHORT).show();
        }
    }
    
    public interface commentClickListener {
        public void OnCommentClick(String realName, String tuid,
                                   String parentid, int sonPosition);
    }
    
    public void setOnCommentClickListenern(commentClickListener comListener) {
        // if (commClickListener==null) {
    
        commClickListener = comListener;
        // }
    }
    
    public void addAll(List<L2CommentBean> list) {
        this.list = list;
        notifyDataSetChanged();
    }
    

    }
    activity中使用:

     mAdapter = new CommentAdapter("0", MainActivity.this,
                    comments, R.layout.comment_item, handler);
            mAdapter.SetOnRepalClickListener(new CommentAdapter.replyClickListener() {
                @Override
                public void onClick(String realName, String tuid, String parentid,
                                    int pos, int sonPosition) {// 二级评论的回复
                    Toast.makeText(MainActivity.this, "二级的回复", Toast.LENGTH_SHORT).show();
    
                }
            });
            mListView.setAdapter(mAdapter);
    private Handler handler = new Handler() {
    
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case 10:
    
                        break;
                    case 11:// 一级评论的回复
    
                        break;
                    default:
                        break;
                }
    
            }
        };

    demo地址:http://download.csdn.net/detail/u010702071/9756595
    如果不足之处还望指正,谢谢!

    展开全文
  • Android RecyclerView 二级列表实现 简述 实现基础 实现思路 实现过程 二级列表数据格式 RecyclerView Item状态 ViewHolder 其它属性和方法 getItemStatusByPosition 方法实现 getItemCount方法实现 其它方法实现 ...

    Android RecyclerView 二级列表实现

    2017.5.16 添加demo

    Demo

    git

    简述

    在开发 Android APP 的时候,难免会需要实现二级列表的情况,而在自己的项目中使用的列表是 android.support.v7.widget 包里面的 RecyclerView,好处是可以根据情况实现不同样式的列表,可扩展程度高。而坏处是什么都要自己实现。所以在想要用 RecyclerView 实现的二级列表的时候,却发现没有类似 ListViewExpandableListView,只能自己去实现。

    实现基础

    在使用 RecyclerView 的时候,与 ListView 类似,需要创建一个 Adapter 去告诉 RecyclerView 如何工作1,而在创建 RecyclerViewAdapter 的时候,一般需要重载以下几个方法:
    onCreateViewHolder() 为每个项目创建 ViewHolder
    onBindViewHolder() 处理每个 item
    getItemViewType()onCreateViewHolder 前调用,返回 item 类型
    getItemCount() 获取 item 总数
    加载 RecyclerView 的过程如下图:

    Created with Raphaël 2.1.0StartgetItemCount()getItemViewType()onCreateViewHolder()onBindViewHolder()End

    除此之外,还需要创建一个 ViewHolder 用于寻找自定义 item的各个控件。

    实现思路

    根据上述,在实现二级列表的时候,我们在 onCreateViewHolder()onBindViewHolder() 中,判断是加载一级项目(GroupItem)还是二级子项目(SubItem)。

    实现过程

    二级列表数据格式

    一般来说,一个 GroupItem 下面有一个,或多个 SubItem,一对多。
    在这里,用一个 DataTree 来封装这种数据格式,代码如下:

    public class DataTree<K, V> {
    
        private K groupItem;
        private List<V> subItems;
    
        public DataTree(K groupItem, List<V> subItems) {
            this.groupItem = groupItem;
            this.subItems = subItems;
        }
    
        public K getGroupItem() {
            return groupItem;
        }
    
        public List<V> getSubItems() {
            return subItems;
        }
    }

    RecyclerView Item状态

    ItemStatus 用封装列表每一项的状态,包括:
    viewType item的类型,group item 还是 subitem
    groupItemIndex 一级索引位置
    subItemIndex 如果该 item 是一个二级子项目,则保存子项目索引

        private static class ItemStatus {
    
            public static final int VIEW_TYPE_GROUPITEM = 0;
            public static final int VIEW_TYPE_SUBITEM = 1;
    
            private int viewType;
            private int groupItemIndex = 0;
            private int subItemIndex = -1;
    
            public ItemStatus() {
            }
    
            public int getViewType() {
                return viewType;
            }
    
            public void setViewType(int viewType) {
                this.viewType = viewType;
            }
    
            public int getGroupItemIndex() {
                return groupItemIndex;
            }
    
            public void setGroupItemIndex(int groupItemIndex) {
                this.groupItemIndex = groupItemIndex;
            }
    
            public int getSubItemIndex() {
                return subItemIndex;
            }
    
            public void setSubItemIndex(int subItemIndex) {
                this.subItemIndex = subItemIndex;
            }
        }

    ViewHolder

    这里的 groupItemsubItem 分别用不同的布局文件,所以我把 ViewHolder 分开写,如下:

        public static class GroupItemViewHolder extends RecyclerView.ViewHolder {
            ...
            public GroupItemViewHolder(View itemView) {
                super(itemView);
                ...
            }
        }
    
        public static class SubItemViewHolder extends RecyclerView.ViewHolder {
            ...
            public SubItemViewHolder(View itemView) {
                super(itemView);
                ...
            }
        }

    其它属性和方法

    context
    list dataTrees 用于显示的数据
    list<Boolean> groupItemStatus 保存 groupItem 状态,开还是关

        //向外暴露设置显示数据的方法
        public void setDataTrees(List<DataTree<Album, Track>> dt) {
            this.dataTrees = dt;
            initGroupItemStatus(groupItemStatus);
            notifyDataSetChanged();
        }
    
        //设置初始值,所有 groupItem 默认为关闭状态
        private void initGroupItemStatus(List l) {
            for (int i = 0; i < dataTrees.size(); i++) {
                l.add(false);
            }
        }

    getItemStatusByPosition() 方法实现

    顾名思义,用于根据 position 来计算判断该 item 的状态,返回一个 ItemStatus

    position

    代码如下:

    private ItemStatus getItemStatusByPosition(int position) {
    
            ItemStatus itemStatus = new ItemStatus();
    
            int count = 0;    //计算groupItemIndex = i 时,position最大值
            int i = 0;
    
            //轮询 groupItem 的开关状态
            for (i = 0; i < groupItemStatus.size(); i++ ) {
    
                //pos刚好等于计数时,item为groupItem
                if (count == position) {
                    itemStatus.setViewType(ItemStatus.VIEW_TYPE_GROUPITEM);
                    itemStatus.setGroupItemIndex(i);
                    break;
    
                //pos大于计数时,item为groupItem(i - 1)中的某个subItem
                } else if (count > position) {
    
                    itemStatus.setViewType(ItemStatus.VIEW_TYPE_SUBITEM);
                    itemStatus.setGroupItemIndex(i - 1);
                    itemStatus.setSubItemIndex(position - ( count - dataTrees.get(i - 1).getSubItems().size() ) );
                    break;
    
                }
    
                //无论groupItem状态是开或者关,它在列表中都会存在,所有count++
                count++;
    
                //当轮询到的groupItem的状态为“开”的话,count需要加上该groupItem下面的子项目数目
                if (groupItemStatus.get(i)) {
    
                    count += dataTrees.get(i).getSubItems().size();
    
                }
    
    
            }
    
            //简单地处理当轮询到最后一项groupItem的时候
            if (i >= groupItemStatus.size()) {
                itemStatus.setGroupItemIndex(i - 1);
                itemStatus.setViewType(ItemStatus.VIEW_TYPE_SUBITEM);
                itemStatus.setSubItemIndex(position - ( count - dataTrees.get(i - 1).getSubItems().size() ) );
            }
    
            return itemStatus;
        }

    getItemCount()方法实现

    该方法在显示列表的时候会执行多次,如果返回的项目计数不正确的话程序会出现错误奔溃,代码如下:

        @Override
        public int getItemCount() {
    
            Logger.i("1");
    
            int itemCount = 0;
    
            if (groupItemStatus.size() == 0) {
                return 0;
            }
    
            for (int i = 0; i < dataTrees.size(); i++) {
    
                if (groupItemStatus.get(i)) {
                    itemCount += dataTrees.get(i).getSubItems().size() + 1;
                } else {
                    itemCount++;
                }
    
            }
    
            return itemCount;
        }

    其它方法实现

    • getItemViewType()

    该方法会在 onCreateViewHolder() 前执行,并返回 int viewType,代码如下:

        @Override
        public int getItemViewType(int position) {
            return getItemStatusByPosition(position).getViewType();
        }
    • onCreateViewHolder()
      根据不同的由 getItemViewType() 返回的 viewType 选择不同的项目布局,代码如下:
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View v;
            RecyclerView.ViewHolder viewHolder = null;
    
            if (viewType == ItemStatus.VIEW_TYPE_GROUPITEM) {
    
                v = LayoutInflater.from(parent.getContext()).inflate(R.layout
                        .item_artist_detail_album, parent, false);
                viewHolder = new GroupItemViewHolder(v);
    
            } else if (viewType == ItemStatus.VIEW_TYPE_SUBITEM) {
    
                v = LayoutInflater.from(parent.getContext()).inflate(R.layout
                        .item_artist_detail_track, parent, false);
                viewHolder = new SubItemViewHolder(v);
            }
    
            return viewHolder;
        }
    • onBindViewHolder()
      根据不同的 viewType 绑定不同的 ViewHolder ,代码如下:
       @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    
            final ItemStatus itemStatus = getItemStatusByPosition(position);
    
            final DataTree<Album, Track> dt = dataTrees.get(itemStatus.getGroupItemIndex());
    
            if ( itemStatus.getViewType() == ItemStatus.VIEW_TYPE_GROUPITEM ) {
    
                final GroupItemViewHolder groupItemVh = (GroupItemViewHolder) holder;
    
                . . .    //加载groupItem,处理groupItem控件
    
                groupItemVh.itemView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
    
                        int groupItemIndex = itemStatus.getGroupItemIndex();
    
                        if ( !groupItemStatus.get(groupItemIndex) ) {
    
                            . . .  //groupItem由“关闭”状态到“打开”状态
    
                            groupItemStatus.set(groupItemIndex, true);
                            notifyItemRangeInserted(groupItemVh.getAdapterPosition() + 1, dt.getSubItems().size());
    
                        } else {
    
                         . . .    //groupItem由“打开”状态到“关闭”状态
    
                        groupItemStatus.set(groupItemIndex, false);
                            notifyItemRangeRemoved(groupItemVh.getAdapterPosition() + 1, dt.getSubItems().size());
    
                        }
    
                    }
                });
    
            } else if (itemStatus.getViewType() == ItemStatus.VIEW_TYPE_SUBITEM) {
    
                SubItemViewHolder subItemVh = (SubItemViewHolder) holder;
    
                 . . .    //加载subItem,处理subItem控件
    
                subItemVh.itemView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
    
                     . . .    //点击subItem处理
    
                    }
                });
            }
        }

    总结

    二级列表对 RecyclerView 小小地扩展,在实现的过程中,有点麻烦的地方是对特定的 item 进行识别,即 getItemViewType() 的实现,在这里,我简单地用遍历轮询的方法去判断,应该会有更简单更节省资源的方法。实现二级列表之后,理论上可以实现多级列表,可以试试。

    在用这个方法实现之前,有尝试过有 View 提供的方法—— View.setTag() ,但是由于 RecyclerView 的加载机制,当列表被划出界面时,会被销毁,而重新划进来显示时,RecyclerView 会重新创建新的 item,而不是之前的那个,所以,之前的 Tag 会不见,最后没能实现出来,感兴趣的同学可以试试。


    1. [Android RecyclerView 使用完全解析 体验艺术般的控件 - hongyang - CSDN博客]http://blog.csdn.net/lmj623565791/article/details/45059587
    展开全文
  • 最近在做一个和抖音差不多的软件,评论的列表之前在VG实习的时候就做过,但是还是想看看抖音是怎么做的,然后利用SDK内自带的Android Device Monitor 对抖音的评论的布局进行了查看,再结合反编译抖音发现的...

    最近在做一个和抖音差不多的软件,评论的列表之前在VG实习的时候就做过,但是还是想看看抖音是怎么做的,然后利用SDK内自带的Android Device Monitor 对抖音的评论的布局进行了查看,再结合反编译抖音发现的Multitype包,大概知道了抖音和我的实现方式是一样的,把parentcomment和childcomment和加载更多的footer分别作为一种Item放在一个recyclerview中。。。然后进行局部请求,再进行局部刷新。。

    首先你得熟悉multitype的用法,我个人觉得multitype是recylclerview最好的dapter解决方案。

    然后就是上代码,由于比较简单比网上大多数的评论列表的教程的代码要少的多,就直接放到Github了,喜欢的点个赞  https://github.com/TracyEminem/TikTokComment

     

     

    最后是效果:

    展开全文
  • 老规矩 先上效果图 1542355190753.gif ...那么问题来了:当评论的dialog弹出来的时候两个dialog就会被顶起,但是我们想要的是评论列表不要被顶起 给评论列表的dialog设置此方法即可 Window win ...
        

    老规矩 先上效果图

    10151120-803743b92c62cc65.gif
    1542355190753.gif

    附下Demo TikTok

    如果想要实现抖音一样的评论功能,就要再列表dialog上面再弹出一个dialog.

    那么问题来了:当评论的dialog弹出来的时候两个dialog就会被顶起,但是我们想要的是评论列表不要被顶起
    给评论列表的dialog设置此方法即可

     Window win = dialog.getWindow();
            WindowManager.LayoutParams params = win.getAttributes();
            win.setSoftInputMode(params.SOFT_INPUT_ADJUST_NOTHING);
    

    添加依赖 版本根据你build版本来

    implementation 'com.android.support:design:27.1.1'
    

    自定义类:ListBottomSheetDialogFragment继承BottomSheetDialogFragment

    /**
     * @Author: 淘跑
     * @Date: 2018/7/2 15:18
     * @Use:
     */
    public class ListBottomSheetDialogFragment extends BottomSheetDialogFragment {
        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            //给dialog设置主题为透明背景 不然会有默认的白色背景
             setStyle(DialogFragment.STYLE_NO_FRAME, R.style.CustomBottomSheetDialogTheme);
        }
    
        /**
         * 如果想要点击外部消失的话 重写此方法
         *
         * @param savedInstanceState
         * @return
         */
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            Dialog dialog = super.onCreateDialog(savedInstanceState);
            //设置点击外部可消失
            dialog.setCanceledOnTouchOutside(true);
            //设置使软键盘弹出的时候dialog不会被顶起
            Window win = dialog.getWindow();
            WindowManager.LayoutParams params = win.getAttributes();
            win.setSoftInputMode(params.SOFT_INPUT_ADJUST_NOTHING);
            //这里设置dialog的进出动画 
            win.setWindowAnimations(R.style.DialogBottomAnim);
            return dialog;
        }
    
        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            // 在这里将view的高度设置为精确高度,即可屏蔽向上滑动不占全屏的手势。
            //如果不设置高度的话 会默认向上滑动时dialog覆盖全屏
            View view = inflater.inflate(R.layout.fragment_item_list_dialog, container, false);
            view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                    getScreenHeight(getActivity()) / 2));
            return view;
        }
    
        @Override
        public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
            final RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.rv);
            recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
            recyclerView.setAdapter(new ItemAdapter(100));
            view.findViewById(R.id.tv).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    //弹出评论输入框
                    InputDialog inputDialog = new InputDialog(getActivity());
                    Window window = inputDialog.getWindow();
                    WindowManager.LayoutParams params = window.getAttributes();
                    //设置软键盘通常是可见的
                    window.setSoftInputMode(params.SOFT_INPUT_STATE_VISIBLE);
                    inputDialog.show();
                }
            });
        }
    
        private class ItemAdapter extends RecyclerView.Adapter<ItemAdapter.MyViewHolder> {
            private final int mItemCount;
    
            ItemAdapter(int itemCount) {
                mItemCount = itemCount;
            }
    
            @Override
            public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                return new MyViewHolder(LayoutInflater.from(parent.getContext()), parent);
            }
    
            @Override
            public void onBindViewHolder(MyViewHolder holder, int position) {
                holder.text.setText(String.valueOf(position));
            }
    
            @Override
            public int getItemCount() {
                return mItemCount;
            }
    
            public class MyViewHolder extends RecyclerView.ViewHolder {
                final TextView text;
    
                MyViewHolder(LayoutInflater inflater, ViewGroup parent) {
                    super(inflater.inflate(R.layout.fragment_item_list_dialog_item, parent, false));
                    text = (TextView) itemView.findViewById(R.id.text);
                }
            }
    
        }
    
        /**
         * 得到屏幕的高
         *
         * @param context
         * @return
         */
        public static int getScreenHeight(Context context) {
            WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            int height = wm.getDefaultDisplay().getHeight();
            return height;
        }
    
    }
    

    自定义布局文件 fragment_item_list_dialog

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/shape_round_white">
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"></android.support.v7.widget.RecyclerView>
    <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:layout_gravity="center_horizontal"
            android:background="#454545"
            android:gravity="center"
            android:text="假装这是输入框"
            android:textColor="#FFFFFF"
            android:textSize="25dp" />
    </LinearLayout>
    

    item布局文件 fragment_item_list_dialog_item

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    
        <TextView
            android:id="@+id/text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="10dp"
            android:textColor="@android:color/black" />
    </LinearLayout>
    

    自定义样式style: CustomBottomSheetDialogTheme这里设置的是背景也是透明的

    <style name="CustomBottomSheetDialogTheme" parent="Widget.Design.BottomSheet.Modal">
            <item name="android:background">@android:color/transparent</item><!--边框-->
            <item name="android:windowFrame">@null</item><!--边框-->
            <item name="android:windowIsFloating">true</item><!--是否浮现在activity之上-->
            <item name="android:windowIsTranslucent">false</item><!--半透明-->
            <item name="android:windowNoTitle">true</item><!--无标题-->
            <item name="android:windowBackground">@android:color/transparent</item><!--背景透明-->
            <item name="android:backgroundDimEnabled">false</item><!--模糊-->
            <item name="android:windowSoftInputMode">adjustPan|stateHidden</item>
     </style>
    

    动画

      <!-- 底部弹出动画 -->
        <style name="DialogBottomAnim" parent="android:Animation">
            <item name="android:windowEnterAnimation">@anim/dialog_bottom_in</item>
            <item name="android:windowExitAnimation">@anim/dialog_bottom_out</item>
        </style>
    

    dialog_bottom_in.xml

    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android">
        <translate
            android:duration="400"
            android:fromYDelta="100%p" />
    </set>
    

    dialog_bottom_out

    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android">
        <translate
            android:duration="400"
            android:toYDelta="100%p" />
    </set>
    

    评论输入Dialog

    /**
     * @Author:淘跑
     * @Date: 2018/11/14 17:20
     * @Use:
     */
    public class InputDialog extends Dialog {
        public InputDialog(@NonNull Context context) {
            super(context);
    //        super(context, R.style.CustomDialog);
            init(context);
        }
    
        public Context mContext;
        public View mRootView;
    
        public void init(Context context) {
            mContext = context;
            mRootView = LayoutInflater.from(context).inflate(R.layout.dialog_input, null);
            setContentView(mRootView);
            Window window = getWindow();
            WindowManager.LayoutParams params = window.getAttributes();
            params.width = WindowManager.LayoutParams.MATCH_PARENT;
            window.setAttributes(params);
            window.setGravity(Gravity.BOTTOM);
        }
    
    }
    

    评论输入Dialog 布局

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    
        <EditText
            android:id="@+id/et"
            android:layout_width="match_parent"
            android:layout_height="80dp" />
    </LinearLayout>
    
    展开全文
  • 因为工作的关系,需要做一个省市二级联动。 1、首先,实现这个功能所用到的控件是android 的spinner 2、要做一个省市二级联动,首先我们要有所有省及其主要城市的数据,这里我给一个我的百度云的链接。大家可以...
  • Android实现二级联动

    2019-01-19 11:59:11
    Android实现二级联动,二级联动是非常常见实用的功能,用的比较多的就是地址选择了,今天就给大家讲讲如何实现:   效果图:   布局文件:     &lt;?xml version="1.0" encoding=&...
  • Android开发二级下拉菜单Android开发中,我们经常会有碰到下拉列表的需求,比如,城市下拉菜单,美团网界面上价格,食品的类型等等,都用到了这种二级下拉菜单。因此我参考了这个博文 ...先不说其他的,先看看效果图:...
  • android 二级菜单、双ListView 仿美团、购物App二级菜单
  • android二级listview列表

    2012-12-28 12:41:54
    今天来实现以下大众点评客户端的横向listview二级列表,先看一下样式。 这种横向的listview二级列表在手机软件上还不太常见,但是使用过平板的都应该知道,在平板上市比较常见的。可能是因为平板屏幕比较大,而且也...
  • (1)布局文件没有做更改 (2)在res--menu目录下的main.xml文件中代码如下: android:id="@+id/file" android:title="@string/file"> android:id=
  • Android安卓二级菜单

    2018-05-03 16:34:57
    一、首先在AndroidStudio里创建个moudle(如果不想新创建项目,且自己的电脑配置扛得住的情况下)、在activity_main.xml里布局ExpandableListView&lt;?xml version="1.0" encoding="utf-8"...
  • 对于需求改动和迭代较频繁的公司来说,如何快速开发一个二级界面来适应我们的功能需求无疑优先级更高一些。首先我们来看看其他社交类app的评论与回复列表如何展示的: Twitter不用说了,全球知名社交平台,上亿...
  • 1NextActivitypackage com.louis.classifymenuview.listview2girdview...import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.AdapterView;
  • android省市二级联动下拉框,及全国省市xml文件
  • 各位大神,小弟想通过自定义ExpandableListView实现二级多选列表。 详细功能如下: 1.点击一级列表则展开/收起二级列表(与ExpandableListView原属性不变) 2.长安一级列表则选中当前列表及当前列表下所有二级...
  • 省市资源文件:(放入res/values下) 链接:https://pan.baidu.com/s/1dMjY0m 密码:... Activity: public class MainActivity extends Activity{ protected TextView tv1; private PopupWindow popupWindow;... private
  • Android 二级菜单

    2018-11-04 21:52:46
    https://www.cnblogs.com/blog4wei/p/8875889.html
  • 通常我们在外卖或者商城app会看到竖排二级列表的界面,点击左边列表的分类菜单,右边列表则会显示对应分类下的列表内容,这篇博文将对该功能的实现归纳和整理。 首先看下效果图: 在第一个页面选择最多三个标签...
  • 项目Github托管地址:https://github.com/zhouzhuo810/ZzSecondaryLinkage效果图How to use it ?准备工作①第一步,添加布局 android:id="@+id/zz_linkage" android:
1 2 3 4 5 ... 20
收藏数 131,825
精华内容 52,730
关键字:

二级评论android